aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-03-28 11:28:31 +0000
committerGitHub <[email protected]>2020-03-28 11:28:31 +0000
commitc30425dc96895117b644f29b758cee9dac36839b (patch)
treed3ccef4aa8f681cc9de29f0435ad20e87911a6ba /crates
parenta1fea0d34ee8f3436aefd87d4c133a7ff50ffbb0 (diff)
parent311cbbdad599d51c6f08f7dd72c299f7c0128bb2 (diff)
Merge #3753
3753: Introduce stdx crate r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/Cargo.toml4
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs14
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs9
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs39
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs4
-rw-r--r--crates/ra_hir_def/Cargo.toml2
-rw-r--r--crates/ra_hir_def/src/nameres.rs5
-rw-r--r--crates/ra_hir_ty/Cargo.toml2
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs8
-rw-r--r--crates/ra_hir_ty/src/test_db.rs3
-rw-r--r--crates/ra_hir_ty/src/tests.rs26
-rw-r--r--crates/ra_ide/Cargo.toml4
-rw-r--r--crates/ra_ide/src/completion/presentation.rs45
-rw-r--r--crates/ra_ide/src/diagnostics.rs18
-rw-r--r--crates/ra_ide/src/display.rs13
-rw-r--r--crates/ra_ide/src/display/function_signature.rs18
-rw-r--r--crates/ra_ide/src/display/short_label.rs4
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/ast/make.rs15
-rw-r--r--crates/ra_syntax/src/lib.rs7
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs5
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs6
-rw-r--r--crates/rust-analyzer/src/world.rs15
-rw-r--r--crates/stdx/Cargo.toml11
-rw-r--r--crates/stdx/src/lib.rs75
26 files changed, 219 insertions, 137 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index a87f4052a..3bcf58ba4 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -8,12 +8,12 @@ authors = ["rust-analyzer developers"]
8doctest = false 8doctest = false
9 9
10[dependencies] 10[dependencies]
11format-buf = "1.0.0"
12join_to_string = "0.1.3"
13rustc-hash = "1.1.0" 11rustc-hash = "1.1.0"
14itertools = "0.9.0" 12itertools = "0.9.0"
15either = "1.5.3" 13either = "1.5.3"
16 14
15stdx = { path = "../stdx" }
16
17ra_syntax = { path = "../ra_syntax" } 17ra_syntax = { path = "../ra_syntax" }
18ra_text_edit = { path = "../ra_text_edit" } 18ra_text_edit = { path = "../ra_text_edit" }
19ra_fmt = { path = "../ra_fmt" } 19ra_fmt = { path = "../ra_fmt" }
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index dd2bed25a..15f9b216b 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -1,17 +1,13 @@
1//! FIXME: write short doc here
2
3use join_to_string::join;
4use ra_syntax::{ 1use ra_syntax::{
5 ast::{self, AstNode}, 2 ast::{self, AstNode},
6 Direction, SmolStr, 3 Direction, SmolStr,
7 SyntaxKind::{IDENT, WHITESPACE}, 4 SyntaxKind::{IDENT, WHITESPACE},
8 TextRange, TextUnit, 5 TextRange, TextUnit,
9}; 6};
7use stdx::SepBy;
10 8
11use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
12 10
13const DERIVE_TRAIT: &str = "derive";
14
15// Assist: add_custom_impl 11// Assist: add_custom_impl
16// 12//
17// Adds impl block for derived trait. 13// Adds impl block for derived trait.
@@ -38,7 +34,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
38 .descendants_with_tokens() 34 .descendants_with_tokens()
39 .filter(|t| t.kind() == IDENT) 35 .filter(|t| t.kind() == IDENT)
40 .find_map(|i| i.into_token()) 36 .find_map(|i| i.into_token())
41 .filter(|t| *t.text() == DERIVE_TRAIT)? 37 .filter(|t| *t.text() == "derive")?
42 .text() 38 .text()
43 .clone(); 39 .clone();
44 40
@@ -63,8 +59,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
63 .filter(|t| t != trait_token.text()) 59 .filter(|t| t != trait_token.text())
64 .collect::<Vec<SmolStr>>(); 60 .collect::<Vec<SmolStr>>();
65 let has_more_derives = !new_attr_input.is_empty(); 61 let has_more_derives = !new_attr_input.is_empty();
66 let new_attr_input = 62 let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
67 join(new_attr_input.iter()).separator(", ").surround_with("(", ")").to_string();
68 let new_attr_input_len = new_attr_input.len(); 63 let new_attr_input_len = new_attr_input.len();
69 64
70 let mut buf = String::new(); 65 let mut buf = String::new();
@@ -100,9 +95,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
100 95
101#[cfg(test)] 96#[cfg(test)]
102mod tests { 97mod tests {
103 use super::*;
104 use crate::helpers::{check_assist, check_assist_not_applicable}; 98 use crate::helpers::{check_assist, check_assist_not_applicable};
105 99
100 use super::*;
101
106 #[test] 102 #[test]
107 fn add_custom_impl_for_unique_input() { 103 fn add_custom_impl_for_unique_input() {
108 check_assist( 104 check_assist(
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index afae7d385..6622eadb2 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -1,9 +1,8 @@
1use format_buf::format;
2use join_to_string::join;
3use ra_syntax::{ 1use ra_syntax::{
4 ast::{self, AstNode, NameOwner, TypeParamsOwner}, 2 ast::{self, AstNode, NameOwner, TypeParamsOwner},
5 TextUnit, 3 TextUnit,
6}; 4};
5use stdx::{format_to, SepBy};
7 6
8use crate::{Assist, AssistCtx, AssistId}; 7use crate::{Assist, AssistCtx, AssistId};
9 8
@@ -36,7 +35,7 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
36 let mut buf = String::new(); 35 let mut buf = String::new();
37 buf.push_str("\n\nimpl"); 36 buf.push_str("\n\nimpl");
38 if let Some(type_params) = &type_params { 37 if let Some(type_params) = &type_params {
39 format!(buf, "{}", type_params.syntax()); 38 format_to!(buf, "{}", type_params.syntax());
40 } 39 }
41 buf.push_str(" "); 40 buf.push_str(" ");
42 buf.push_str(name.text().as_str()); 41 buf.push_str(name.text().as_str());
@@ -47,7 +46,9 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
47 .map(|it| it.text().clone()); 46 .map(|it| it.text().clone());
48 let type_params = 47 let type_params =
49 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 48 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
50 join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf); 49
50 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
51 format_to!(buf, "<{}>", generic_params)
51 } 52 }
52 buf.push_str(" {\n"); 53 buf.push_str(" {\n");
53 edit.set_cursor(start_offset + TextUnit::of_str(&buf)); 54 edit.set_cursor(start_offset + TextUnit::of_str(&buf));
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index 729a223e0..240b19fa3 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -1,14 +1,11 @@
1use std::fmt::Write;
2
3use format_buf::format;
4use hir::Adt; 1use hir::Adt;
5use join_to_string::join;
6use ra_syntax::{ 2use ra_syntax::{
7 ast::{ 3 ast::{
8 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, 4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
9 }, 5 },
10 TextUnit, T, 6 TextUnit, T,
11}; 7};
8use stdx::{format_to, SepBy};
12 9
13use crate::{Assist, AssistCtx, AssistId}; 10use crate::{Assist, AssistCtx, AssistId};
14 11
@@ -53,24 +50,22 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
53 buf.push('\n'); 50 buf.push('\n');
54 } 51 }
55 52
56 let vis = strukt.visibility().map(|v| format!("{} ", v.syntax())); 53 let vis = strukt.visibility().map(|v| format!("{} ", v));
57 let vis = vis.as_deref().unwrap_or(""); 54 let vis = vis.as_deref().unwrap_or("");
58 write!(&mut buf, " {}fn new(", vis).unwrap();
59
60 join(field_list.fields().filter_map(|f| {
61 Some(format!("{}: {}", f.name()?.syntax().text(), f.ascribed_type()?.syntax().text()))
62 }))
63 .separator(", ")
64 .to_buf(&mut buf);
65 55
66 buf.push_str(") -> Self { Self {"); 56 let params = field_list
67 57 .fields()
68 join(field_list.fields().filter_map(|f| Some(f.name()?.syntax().text()))) 58 .filter_map(|f| {
69 .separator(", ") 59 Some(format!(
70 .surround_with(" ", " ") 60 "{}: {}",
71 .to_buf(&mut buf); 61 f.name()?.syntax().text(),
62 f.ascribed_type()?.syntax().text()
63 ))
64 })
65 .sep_by(", ");
66 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
72 67
73 buf.push_str("} }"); 68 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
74 69
75 let (start_offset, end_offset) = impl_def 70 let (start_offset, end_offset) = impl_def
76 .and_then(|impl_def| { 71 .and_then(|impl_def| {
@@ -103,7 +98,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
103 let mut buf = String::with_capacity(code.len()); 98 let mut buf = String::with_capacity(code.len());
104 buf.push_str("\n\nimpl"); 99 buf.push_str("\n\nimpl");
105 if let Some(type_params) = &type_params { 100 if let Some(type_params) = &type_params {
106 format!(buf, "{}", type_params.syntax()); 101 format_to!(buf, "{}", type_params.syntax());
107 } 102 }
108 buf.push_str(" "); 103 buf.push_str(" ");
109 buf.push_str(strukt.name().unwrap().text().as_str()); 104 buf.push_str(strukt.name().unwrap().text().as_str());
@@ -114,10 +109,10 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
114 .map(|it| it.text().clone()); 109 .map(|it| it.text().clone());
115 let type_params = 110 let type_params =
116 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 111 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
117 join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf); 112 format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", "))
118 } 113 }
119 114
120 format!(&mut buf, " {{\n{}\n}}\n", code); 115 format_to!(buf, " {{\n{}\n}}\n", code);
121 116
122 buf 117 buf
123} 118}
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index b453c51fb..1edbdc14c 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -1,4 +1,3 @@
1use format_buf::format;
2use ra_syntax::{ 1use ra_syntax::{
3 ast::{self, AstNode}, 2 ast::{self, AstNode},
4 SyntaxKind::{ 3 SyntaxKind::{
@@ -7,6 +6,7 @@ use ra_syntax::{
7 }, 6 },
8 SyntaxNode, TextUnit, 7 SyntaxNode, TextUnit,
9}; 8};
9use stdx::format_to;
10use test_utils::tested_by; 10use test_utils::tested_by;
11 11
12use crate::{Assist, AssistCtx, AssistId}; 12use crate::{Assist, AssistCtx, AssistId};
@@ -52,7 +52,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
52 buf.push_str("let var_name = "); 52 buf.push_str("let var_name = ");
53 TextUnit::of_str("let ") 53 TextUnit::of_str("let ")
54 }; 54 };
55 format!(buf, "{}", expr.syntax()); 55 format_to!(buf, "{}", expr.syntax());
56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 30a12337e..56e791e3e 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -15,6 +15,8 @@ either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17 17
18stdx = { path = "../stdx" }
19
18ra_arena = { path = "../ra_arena" } 20ra_arena = { path = "../ra_arena" }
19ra_db = { path = "../ra_db" } 21ra_db = { path = "../ra_db" }
20ra_syntax = { path = "../ra_syntax" } 22ra_syntax = { path = "../ra_syntax" }
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 40bdc34f5..f279c2ad4 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -63,6 +63,7 @@ use ra_db::{CrateId, Edition, FileId};
63use ra_prof::profile; 63use ra_prof::profile;
64use ra_syntax::ast; 64use ra_syntax::ast;
65use rustc_hash::FxHashMap; 65use rustc_hash::FxHashMap;
66use stdx::format_to;
66 67
67use crate::{ 68use crate::{
68 db::DefDatabase, 69 db::DefDatabase,
@@ -246,7 +247,7 @@ impl CrateDefMap {
246 entries.sort_by_key(|(name, _)| name.clone()); 247 entries.sort_by_key(|(name, _)| name.clone());
247 248
248 for (name, def) in entries { 249 for (name, def) in entries {
249 *buf += &format!("{}:", name); 250 format_to!(buf, "{}:", name);
250 251
251 if def.types.is_some() { 252 if def.types.is_some() {
252 *buf += " t"; 253 *buf += " t";
@@ -265,7 +266,7 @@ impl CrateDefMap {
265 } 266 }
266 267
267 for (name, child) in map.modules[module].children.iter() { 268 for (name, child) in map.modules[module].children.iter() {
268 let path = path.to_string() + &format!("::{}", name); 269 let path = &format!("{}::{}", path, name);
269 go(buf, map, &path, *child); 270 go(buf, map, &path, *child);
270 } 271 }
271 } 272 }
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 9962112db..5a58d70cf 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -13,6 +13,8 @@ ena = "0.13.1"
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15 15
16stdx = { path = "../stdx" }
17
16hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } 18hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
17hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" } 19hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
18ra_arena = { path = "../ra_arena" } 20ra_arena = { path = "../ra_arena" }
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 6eafdc8f6..0f8522021 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -4,6 +4,7 @@ use std::any::Any;
4 4
5use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 5use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
6use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
7use stdx::format_to;
7 8
8pub use hir_def::diagnostics::UnresolvedModule; 9pub use hir_def::diagnostics::UnresolvedModule;
9pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 10pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
@@ -37,12 +38,11 @@ pub struct MissingFields {
37 38
38impl Diagnostic for MissingFields { 39impl Diagnostic for MissingFields {
39 fn message(&self) -> String { 40 fn message(&self) -> String {
40 use std::fmt::Write; 41 let mut buf = String::from("Missing structure fields:\n");
41 let mut message = String::from("Missing structure fields:\n");
42 for field in &self.missed_fields { 42 for field in &self.missed_fields {
43 writeln!(message, "- {}", field).unwrap(); 43 format_to!(buf, "- {}", field);
44 } 44 }
45 message 45 buf
46 } 46 }
47 fn source(&self) -> InFile<SyntaxNodePtr> { 47 fn source(&self) -> InFile<SyntaxNodePtr> {
48 InFile { file_id: self.file, value: self.field_list.into() } 48 InFile { file_id: self.file, value: self.field_list.into() }
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 5bbeabf51..208096aab 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -10,6 +10,7 @@ use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{ 10use ra_db::{
11 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, 11 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast,
12}; 12};
13use stdx::format_to;
13 14
14use crate::{db::HirDatabase, expr::ExprValidator}; 15use crate::{db::HirDatabase, expr::ExprValidator};
15 16
@@ -131,7 +132,7 @@ impl TestDB {
131 for f in fns { 132 for f in fns {
132 let infer = self.infer(f.into()); 133 let infer = self.infer(f.into());
133 let mut sink = DiagnosticSink::new(|d| { 134 let mut sink = DiagnosticSink::new(|d| {
134 buf += &format!("{:?}: {}\n", d.syntax_node(self).text(), d.message()); 135 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
135 }); 136 });
136 infer.add_diagnostics(self, f, &mut sink); 137 infer.add_diagnostics(self, f, &mut sink);
137 let mut validator = ExprValidator::new(f, infer, &mut sink); 138 let mut validator = ExprValidator::new(f, infer, &mut sink);
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 7e9547340..027e5a8f8 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -7,7 +7,6 @@ mod traits;
7mod method_resolution; 7mod method_resolution;
8mod macros; 8mod macros;
9 9
10use std::fmt::Write;
11use std::sync::Arc; 10use std::sync::Arc;
12 11
13use hir_def::{ 12use hir_def::{
@@ -26,6 +25,7 @@ use ra_syntax::{
26 algo, 25 algo,
27 ast::{self, AstNode}, 26 ast::{self, AstNode},
28}; 27};
28use stdx::format_to;
29 29
30use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult}; 30use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResult};
31 31
@@ -63,7 +63,7 @@ fn infer(ra_fixture: &str) -> String {
63fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { 63fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
64 let (db, file_id) = TestDB::with_single_file(content); 64 let (db, file_id) = TestDB::with_single_file(content);
65 65
66 let mut acc = String::new(); 66 let mut buf = String::new();
67 67
68 let mut infer_def = |inference_result: Arc<InferenceResult>, 68 let mut infer_def = |inference_result: Arc<InferenceResult>,
69 body_source_map: Arc<BodySourceMap>| { 69 body_source_map: Arc<BodySourceMap>| {
@@ -106,15 +106,14 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
106 (src_ptr.value.range(), node.text().to_string().replace("\n", " ")) 106 (src_ptr.value.range(), node.text().to_string().replace("\n", " "))
107 }; 107 };
108 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 108 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
109 writeln!( 109 format_to!(
110 acc, 110 buf,
111 "{}{} '{}': {}", 111 "{}{} '{}': {}\n",
112 macro_prefix, 112 macro_prefix,
113 range, 113 range,
114 ellipsize(text, 15), 114 ellipsize(text, 15),
115 ty.display(&db) 115 ty.display(&db)
116 ) 116 );
117 .unwrap();
118 } 117 }
119 if include_mismatches { 118 if include_mismatches {
120 mismatches.sort_by_key(|(src_ptr, _)| { 119 mismatches.sort_by_key(|(src_ptr, _)| {
@@ -123,15 +122,14 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
123 for (src_ptr, mismatch) in &mismatches { 122 for (src_ptr, mismatch) in &mismatches {
124 let range = src_ptr.value.range(); 123 let range = src_ptr.value.range();
125 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" }; 124 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
126 writeln!( 125 format_to!(
127 acc, 126 buf,
128 "{}{}: expected {}, got {}", 127 "{}{}: expected {}, got {}\n",
129 macro_prefix, 128 macro_prefix,
130 range, 129 range,
131 mismatch.expected.display(&db), 130 mismatch.expected.display(&db),
132 mismatch.actual.display(&db), 131 mismatch.actual.display(&db),
133 ) 132 );
134 .unwrap();
135 } 133 }
136 } 134 }
137 }; 135 };
@@ -158,8 +156,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
158 infer_def(infer, source_map); 156 infer_def(infer, source_map);
159 } 157 }
160 158
161 acc.truncate(acc.trim_end().len()); 159 buf.truncate(buf.trim_end().len());
162 acc 160 buf
163} 161}
164 162
165fn visit_module( 163fn visit_module(
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 36eec0e60..b4a29b81b 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -12,14 +12,14 @@ wasm = []
12 12
13[dependencies] 13[dependencies]
14either = "1.5.3" 14either = "1.5.3"
15format-buf = "1.0.0"
16indexmap = "1.3.2" 15indexmap = "1.3.2"
17itertools = "0.9.0" 16itertools = "0.9.0"
18join_to_string = "0.1.3"
19log = "0.4.8" 17log = "0.4.8"
20rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
21rand = { version = "0.7.3", features = ["small_rng"] } 19rand = { version = "0.7.3", features = ["small_rng"] }
22 20
21stdx = { path = "../stdx" }
22
23ra_syntax = { path = "../ra_syntax" } 23ra_syntax = { path = "../ra_syntax" }
24ra_text_edit = { path = "../ra_text_edit" } 24ra_text_edit = { path = "../ra_text_edit" }
25ra_db = { path = "../ra_db" } 25ra_db = { path = "../ra_db" }
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 253848602..60f1b83f3 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,15 +1,14 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2 2
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; 3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
4use join_to_string::join;
5use ra_syntax::ast::NameOwner; 4use ra_syntax::ast::NameOwner;
5use stdx::SepBy;
6use test_utils::tested_by; 6use test_utils::tested_by;
7 7
8use crate::completion::{
9 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
10};
11
12use crate::{ 8use crate::{
9 completion::{
10 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
11 },
13 display::{const_label, macro_label, type_label, FunctionSignature}, 12 display::{const_label, macro_label, type_label, FunctionSignature},
14 RootDatabase, 13 RootDatabase,
15}; 14};
@@ -221,13 +220,13 @@ impl Completions {
221 builder = builder.trigger_call_info(); 220 builder = builder.trigger_call_info();
222 let snippet = if ctx.options.add_call_argument_snippets { 221 let snippet = if ctx.options.add_call_argument_snippets {
223 let to_skip = if has_self_param { 1 } else { 0 }; 222 let to_skip = if has_self_param { 1 } else { 0 };
224 let function_params_snippet = join( 223 let function_params_snippet = function_signature
225 function_signature.parameter_names.iter().skip(to_skip).enumerate().map( 224 .parameter_names
226 |(index, param_name)| format!("${{{}:{}}}", index + 1, param_name), 225 .iter()
227 ), 226 .skip(to_skip)
228 ) 227 .enumerate()
229 .separator(", ") 228 .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
230 .to_string(); 229 .sep_by(", ");
231 format!("{}({})$0", name, function_params_snippet) 230 format!("{}({})$0", name, function_params_snippet)
232 } else { 231 } else {
233 format!("{}($0)", name) 232 format!("{}($0)", name)
@@ -281,18 +280,16 @@ impl Completions {
281 .into_iter() 280 .into_iter()
282 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); 281 .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
283 let detail = match variant.kind(ctx.db) { 282 let detail = match variant.kind(ctx.db) {
284 StructKind::Tuple | StructKind::Unit => { 283 StructKind::Tuple | StructKind::Unit => detail_types
285 join(detail_types.map(|(_, t)| t.display(ctx.db).to_string())) 284 .map(|(_, t)| t.display(ctx.db).to_string())
286 .separator(", ") 285 .sep_by(", ")
287 .surround_with("(", ")") 286 .surround_with("(", ")")
288 .to_string() 287 .to_string(),
289 } 288 StructKind::Record => detail_types
290 StructKind::Record => { 289 .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
291 join(detail_types.map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))) 290 .sep_by(", ")
292 .separator(", ") 291 .surround_with("{ ", " }")
293 .surround_with("{ ", " }") 292 .to_string(),
294 .to_string()
295 }
296 }; 293 };
297 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 294 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
298 .kind(CompletionItemKind::EnumVariant) 295 .kind(CompletionItemKind::EnumVariant)
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index a10e642db..c1d7ddaf2 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -200,8 +200,8 @@ fn check_struct_shorthand_initialization(
200#[cfg(test)] 200#[cfg(test)]
201mod tests { 201mod tests {
202 use insta::assert_debug_snapshot; 202 use insta::assert_debug_snapshot;
203 use join_to_string::join;
204 use ra_syntax::SourceFile; 203 use ra_syntax::SourceFile;
204 use stdx::SepBy;
205 use test_utils::assert_eq_text; 205 use test_utils::assert_eq_text;
206 206
207 use crate::mock_analysis::{analysis_and_position, single_file}; 207 use crate::mock_analysis::{analysis_and_position, single_file};
@@ -254,16 +254,12 @@ mod tests {
254 .map(|it| it.len() - it.trim_start().len()) 254 .map(|it| it.len() - it.trim_start().len())
255 .next() 255 .next()
256 .expect("empty fixture"); 256 .expect("empty fixture");
257 let after = join(after.lines().filter_map(|line| { 257 let after = after
258 if line.len() > margin { 258 .lines()
259 Some(&line[margin..]) 259 .filter_map(|line| if line.len() > margin { Some(&line[margin..]) } else { None })
260 } else { 260 .sep_by("\n")
261 None 261 .suffix("\n")
262 } 262 .to_string();
263 }))
264 .separator("\n")
265 .suffix("\n")
266 .to_string();
267 263
268 assert_eq_text!(&after, &actual); 264 assert_eq_text!(&after, &actual);
269 assert!( 265 assert!(
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index c395057a7..722092de9 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,12 +6,13 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::{Display, Write}; 9use std::fmt::Display;
10 10
11use ra_syntax::{ 11use ra_syntax::{
12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
13 SyntaxKind::{ATTR, COMMENT}, 13 SyntaxKind::{ATTR, COMMENT},
14}; 14};
15use stdx::format_to;
15 16
16pub use function_signature::FunctionSignature; 17pub use function_signature::FunctionSignature;
17pub use navigation_target::NavigationTarget; 18pub use navigation_target::NavigationTarget;
@@ -78,18 +79,18 @@ pub(crate) fn rust_code_markup_with_doc(
78 doc: Option<&str>, 79 doc: Option<&str>,
79 mod_path: Option<&str>, 80 mod_path: Option<&str>,
80) -> String { 81) -> String {
81 let mut markup = "```rust\n".to_owned(); 82 let mut buf = "```rust\n".to_owned();
82 83
83 if let Some(mod_path) = mod_path { 84 if let Some(mod_path) = mod_path {
84 if !mod_path.is_empty() { 85 if !mod_path.is_empty() {
85 write!(markup, "{}\n", mod_path).unwrap(); 86 format_to!(buf, "{}\n", mod_path);
86 } 87 }
87 } 88 }
88 write!(markup, "{}\n```", code).unwrap(); 89 format_to!(buf, "{}\n```", code);
89 90
90 if let Some(doc) = doc { 91 if let Some(doc) = doc {
91 write!(markup, "\n\n{}", doc).unwrap(); 92 format_to!(buf, "\n\n{}", doc);
92 } 93 }
93 94
94 markup 95 buf
95} 96}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index ec1bbd5a0..b967a6816 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -1,12 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::fmt::{self, Display}; 3use std::{
4 convert::From,
5 fmt::{self, Display},
6};
4 7
5use hir::{Docs, Documentation, HasSource, HirDisplay}; 8use hir::{Docs, Documentation, HasSource, HirDisplay};
6use join_to_string::join;
7use ra_ide_db::RootDatabase; 9use ra_ide_db::RootDatabase;
8use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 10use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
9use std::convert::From; 11use stdx::SepBy;
10 12
11use crate::display::{generic_parameters, where_predicates}; 13use crate::display::{generic_parameters, where_predicates};
12 14
@@ -227,21 +229,17 @@ impl Display for FunctionSignature {
227 } 229 }
228 230
229 if !self.generic_parameters.is_empty() { 231 if !self.generic_parameters.is_empty() {
230 join(self.generic_parameters.iter()) 232 write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
231 .separator(", ")
232 .surround_with("<", ">")
233 .to_fmt(f)?;
234 } 233 }
235 234
236 join(self.parameters.iter()).separator(", ").surround_with("(", ")").to_fmt(f)?; 235 write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;
237 236
238 if let Some(t) = &self.ret_type { 237 if let Some(t) = &self.ret_type {
239 write!(f, " -> {}", t)?; 238 write!(f, " -> {}", t)?;
240 } 239 }
241 240
242 if !self.where_predicates.is_empty() { 241 if !self.where_predicates.is_empty() {
243 write!(f, "\nwhere ")?; 242 write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?;
244 join(self.where_predicates.iter()).separator(",\n ").to_fmt(f)?;
245 } 243 }
246 244
247 Ok(()) 245 Ok(())
diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs
index 9ffc9b980..4b081bf6c 100644
--- a/crates/ra_ide/src/display/short_label.rs
+++ b/crates/ra_ide/src/display/short_label.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use format_buf::format;
4use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner}; 3use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner};
4use stdx::format_to;
5 5
6pub(crate) trait ShortLabel { 6pub(crate) trait ShortLabel {
7 fn short_label(&self) -> Option<String>; 7 fn short_label(&self) -> Option<String>;
@@ -80,7 +80,7 @@ where
80 let mut buf = short_label_from_node(node, prefix)?; 80 let mut buf = short_label_from_node(node, prefix)?;
81 81
82 if let Some(type_ref) = node.ascribed_type() { 82 if let Some(type_ref) = node.ascribed_type() {
83 format!(buf, ": {}", type_ref.syntax()); 83 format_to!(buf, ": {}", type_ref.syntax());
84 } 84 }
85 85
86 Some(buf) 86 Some(buf)
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 6fccc2303..3c6ae77e4 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -18,6 +18,8 @@ rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
20 20
21stdx = { path = "../stdx" }
22
21ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
22ra_parser = { path = "../ra_parser" } 24ra_parser = { path = "../ra_parser" }
23 25
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index dbf8e6370..0c908573d 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -1,6 +1,7 @@
1//! This module contains free-standing functions for creating AST fragments out 1//! This module contains free-standing functions for creating AST fragments out
2//! of smaller pieces. 2//! of smaller pieces.
3use itertools::Itertools; 3use itertools::Itertools;
4use stdx::format_to;
4 5
5use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; 6use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken};
6 7
@@ -34,14 +35,14 @@ pub fn use_tree(
34 let mut buf = "use ".to_string(); 35 let mut buf = "use ".to_string();
35 buf += &path.syntax().to_string(); 36 buf += &path.syntax().to_string();
36 if let Some(use_tree_list) = use_tree_list { 37 if let Some(use_tree_list) = use_tree_list {
37 buf += &format!("::{}", use_tree_list); 38 format_to!(buf, "::{}", use_tree_list);
38 } 39 }
39 if add_star { 40 if add_star {
40 buf += "::*"; 41 buf += "::*";
41 } 42 }
42 43
43 if let Some(alias) = alias { 44 if let Some(alias) = alias {
44 buf += &format!(" {}", alias); 45 format_to!(buf, " {}", alias);
45 } 46 }
46 ast_from_text(&buf) 47 ast_from_text(&buf)
47} 48}
@@ -70,15 +71,15 @@ pub fn block_expr(
70 stmts: impl IntoIterator<Item = ast::Stmt>, 71 stmts: impl IntoIterator<Item = ast::Stmt>,
71 tail_expr: Option<ast::Expr>, 72 tail_expr: Option<ast::Expr>,
72) -> ast::BlockExpr { 73) -> ast::BlockExpr {
73 let mut text = "{\n".to_string(); 74 let mut buf = "{\n".to_string();
74 for stmt in stmts.into_iter() { 75 for stmt in stmts.into_iter() {
75 text += &format!(" {}\n", stmt); 76 format_to!(buf, " {}\n", stmt);
76 } 77 }
77 if let Some(tail_expr) = tail_expr { 78 if let Some(tail_expr) = tail_expr {
78 text += &format!(" {}\n", tail_expr) 79 format_to!(buf, " {}\n", tail_expr)
79 } 80 }
80 text += "}"; 81 buf += "}";
81 ast_from_text(&format!("fn f() {}", text)) 82 ast_from_text(&format!("fn f() {}", buf))
82} 83}
83 84
84pub fn block_from_expr(e: ast::Expr) -> ast::Block { 85pub fn block_from_expr(e: ast::Expr) -> ast::Block {
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index cef926ed3..f0e16dc2b 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -32,9 +32,10 @@ pub mod ast;
32#[doc(hidden)] 32#[doc(hidden)]
33pub mod fuzz; 33pub mod fuzz;
34 34
35use std::{fmt::Write, marker::PhantomData, sync::Arc}; 35use std::{marker::PhantomData, sync::Arc};
36 36
37use ra_text_edit::AtomTextEdit; 37use ra_text_edit::AtomTextEdit;
38use stdx::format_to;
38 39
39use crate::syntax_node::GreenNode; 40use crate::syntax_node::GreenNode;
40 41
@@ -115,7 +116,7 @@ impl Parse<SourceFile> {
115 pub fn debug_dump(&self) -> String { 116 pub fn debug_dump(&self) -> String {
116 let mut buf = format!("{:#?}", self.tree().syntax()); 117 let mut buf = format!("{:#?}", self.tree().syntax());
117 for err in self.errors.iter() { 118 for err in self.errors.iter() {
118 writeln!(buf, "error {:?}: {}", err.range(), err).unwrap(); 119 format_to!(buf, "error {:?}: {}\n", err.range(), err);
119 } 120 }
120 buf 121 buf
121 } 122 }
@@ -296,7 +297,7 @@ fn api_walkthrough() {
296 NodeOrToken::Node(it) => it.text().to_string(), 297 NodeOrToken::Node(it) => it.text().to_string(),
297 NodeOrToken::Token(it) => it.text().to_string(), 298 NodeOrToken::Token(it) => it.text().to_string(),
298 }; 299 };
299 buf += &format!("{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent); 300 format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
300 indent += 2; 301 indent += 2;
301 } 302 }
302 WalkEvent::Leave(_) => indent -= 2, 303 WalkEvent::Leave(_) => indent -= 2,
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index e071e9b8d..8fe6799d2 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -30,6 +30,8 @@ serde = { version = "1.0.104", features = ["derive"] }
30serde_json = "1.0.48" 30serde_json = "1.0.48"
31threadpool = "1.7.1" 31threadpool = "1.7.1"
32 32
33stdx = { path = "../stdx" }
34
33lsp-server = "0.3.1" 35lsp-server = "0.3.1"
34ra_cargo_watch = { path = "../ra_cargo_watch" } 36ra_cargo_watch = { path = "../ra_cargo_watch" }
35ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 27459be8c..75cf2dae5 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1,7 +1,7 @@
1//! Fully type-check project and print various stats, like the number of type 1//! Fully type-check project and print various stats, like the number of type
2//! errors. 2//! errors.
3 3
4use std::{collections::HashSet, fmt::Write, path::Path, time::Instant}; 4use std::{collections::HashSet, path::Path, time::Instant};
5 5
6use hir::{ 6use hir::{
7 db::{AstDatabase, DefDatabase, HirDatabase}, 7 db::{AstDatabase, DefDatabase, HirDatabase},
@@ -13,6 +13,7 @@ use itertools::Itertools;
13use ra_db::SourceDatabaseExt; 13use ra_db::SourceDatabaseExt;
14use ra_syntax::AstNode; 14use ra_syntax::AstNode;
15use rand::{seq::SliceRandom, thread_rng}; 15use rand::{seq::SliceRandom, thread_rng};
16use stdx::format_to;
16 17
17use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}; 18use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity};
18 19
@@ -128,7 +129,7 @@ pub fn analysis_stats(
128 let original_file = src.file_id.original_file(db); 129 let original_file = src.file_id.original_file(db);
129 let path = db.file_relative_path(original_file); 130 let path = db.file_relative_path(original_file);
130 let syntax_range = src.value.syntax().text_range(); 131 let syntax_range = src.value.syntax().text_range();
131 write!(msg, " ({:?} {})", path, syntax_range).unwrap(); 132 format_to!(msg, " ({:?} {})", path, syntax_range);
132 } 133 }
133 if verbosity.is_spammy() { 134 if verbosity.is_spammy() {
134 bar.println(msg.to_string()); 135 bar.println(msg.to_string());
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1033d6de9..12f8ca297 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -3,7 +3,6 @@
3//! `ra_ide` crate. 3//! `ra_ide` crate.
4 4
5use std::{ 5use std::{
6 fmt::Write as _,
7 io::Write as _, 6 io::Write as _,
8 process::{self, Stdio}, 7 process::{self, Stdio},
9}; 8};
@@ -28,6 +27,7 @@ use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
28use rustc_hash::FxHashMap; 27use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 28use serde::{Deserialize, Serialize};
30use serde_json::to_value; 29use serde_json::to_value;
30use stdx::format_to;
31 31
32use crate::{ 32use crate::{
33 cargo_target_spec::CargoTargetSpec, 33 cargo_target_spec::CargoTargetSpec,
@@ -46,11 +46,11 @@ use crate::{
46pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 46pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
47 let _p = profile("handle_analyzer_status"); 47 let _p = profile("handle_analyzer_status");
48 let mut buf = world.status(); 48 let mut buf = world.status();
49 writeln!(buf, "\n\nrequests:").unwrap(); 49 format_to!(buf, "\n\nrequests:");
50 let requests = world.latest_requests.read(); 50 let requests = world.latest_requests.read();
51 for (is_last, r) in requests.iter() { 51 for (is_last, r) in requests.iter() {
52 let mark = if is_last { "*" } else { " " }; 52 let mark = if is_last { "*" } else { " " };
53 writeln!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis()).unwrap(); 53 format_to!(buf, "{}{:4} {:<36}{}ms", mark, r.id, r.method, r.duration.as_millis());
54 } 54 }
55 Ok(buf) 55 Ok(buf)
56} 56}
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index de85bb017..64a7b907e 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -19,6 +19,7 @@ use ra_ide::{
19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
21use relative_path::RelativePathBuf; 21use relative_path::RelativePathBuf;
22use stdx::format_to;
22 23
23use crate::{ 24use crate::{
24 diagnostics::{CheckFixes, DiagnosticCollection}, 25 diagnostics::{CheckFixes, DiagnosticCollection},
@@ -319,23 +320,23 @@ impl WorldSnapshot {
319 } 320 }
320 321
321 pub fn status(&self) -> String { 322 pub fn status(&self) -> String {
322 let mut res = String::new(); 323 let mut buf = String::new();
323 if self.workspaces.is_empty() { 324 if self.workspaces.is_empty() {
324 res.push_str("no workspaces\n") 325 buf.push_str("no workspaces\n")
325 } else { 326 } else {
326 res.push_str("workspaces:\n"); 327 buf.push_str("workspaces:\n");
327 for w in self.workspaces.iter() { 328 for w in self.workspaces.iter() {
328 res += &format!("{} packages loaded\n", w.n_packages()); 329 format_to!(buf, "{} packages loaded\n", w.n_packages());
329 } 330 }
330 } 331 }
331 res.push_str("\nanalysis:\n"); 332 buf.push_str("\nanalysis:\n");
332 res.push_str( 333 buf.push_str(
333 &self 334 &self
334 .analysis 335 .analysis
335 .status() 336 .status()
336 .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()), 337 .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
337 ); 338 );
338 res 339 buf
339 } 340 }
340 341
341 pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> { 342 pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
new file mode 100644
index 000000000..f9e380c10
--- /dev/null
+++ b/crates/stdx/Cargo.toml
@@ -0,0 +1,11 @@
1[package]
2name = "stdx"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6
7[lib]
8doctest = false
9
10[dependencies]
11# Think twice before adding anything here
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
new file mode 100644
index 000000000..8492c17af
--- /dev/null
+++ b/crates/stdx/src/lib.rs
@@ -0,0 +1,75 @@
1//! Missing batteries for standard libraries.
2
3use std::{cell::Cell, fmt};
4
5/// Appends formatted string to a `String`.
6#[macro_export]
7macro_rules! format_to {
8 (&buf:expr) => ();
9 ($buf:expr, $lit:literal $($arg:tt)*) => {
10 { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
11 };
12}
13
14pub trait SepBy: Sized {
15 /// Returns an `impl fmt::Display`, which joins elements via a separator.
16 fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
17}
18
19impl<I> SepBy for I
20where
21 I: Iterator,
22 I::Item: fmt::Display,
23{
24 fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self> {
25 SepByBuilder::new(sep, self)
26 }
27}
28
29pub struct SepByBuilder<'a, I> {
30 sep: &'a str,
31 prefix: &'a str,
32 suffix: &'a str,
33 iter: Cell<Option<I>>,
34}
35
36impl<'a, I> SepByBuilder<'a, I> {
37 fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> {
38 SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) }
39 }
40
41 pub fn prefix(mut self, prefix: &'a str) -> Self {
42 self.prefix = prefix;
43 self
44 }
45
46 pub fn suffix(mut self, suffix: &'a str) -> Self {
47 self.suffix = suffix;
48 self
49 }
50
51 /// Set both suffix and prefix.
52 pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self {
53 self.prefix(prefix).suffix(suffix)
54 }
55}
56
57impl<I> fmt::Display for SepByBuilder<'_, I>
58where
59 I: Iterator,
60 I::Item: fmt::Display,
61{
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 f.write_str(self.prefix)?;
64 let mut first = true;
65 for item in self.iter.take().unwrap() {
66 if !first {
67 f.write_str(self.sep)?;
68 }
69 first = false;
70 fmt::Display::fmt(&item, f)?;
71 }
72 f.write_str(self.suffix)?;
73 Ok(())
74 }
75}