aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config2
-rw-r--r--ARCHITECTURE.md8
-rw-r--r--crates/ra_ide_api/src/call_info.rs66
-rw-r--r--crates/ra_ide_api/src/completion.rs19
-rw-r--r--crates/ra_ide_api/src/completion/completion_item.rs35
-rw-r--r--crates/ra_ide_api/src/lib.rs3
-rw-r--r--crates/ra_lsp_server/src/conv.rs19
-rw-r--r--crates/ra_lsp_server/src/lib.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs11
-rw-r--r--crates/ra_lsp_server/src/markdown.rs38
-rw-r--r--editors/code/package.json28
11 files changed, 128 insertions, 102 deletions
diff --git a/.cargo/config b/.cargo/config
index 3fba0f71d..b12f407e6 100644
--- a/.cargo/config
+++ b/.cargo/config
@@ -5,6 +5,8 @@ gen-syntax = "run --package tools --bin tools -- gen-syntax"
5gen-tests = "run --package tools --bin tools -- gen-tests" 5gen-tests = "run --package tools --bin tools -- gen-tests"
6# Installs ra_lsp_server 6# Installs ra_lsp_server
7install-lsp = "install --path crates/ra_lsp_server --force" 7install-lsp = "install --path crates/ra_lsp_server --force"
8# Installs ra_lsp_server with the jemalloc feature
9jinstall-lsp = "install --path crates/ra_lsp_server --force --features jemalloc"
8# Installs the visual studio code extension 10# Installs the visual studio code extension
9install-code = "run --package tools --bin tools -- install-code" 11install-code = "run --package tools --bin tools -- install-code"
10# Formats the full repository or installs the git hook to do it automatically. 12# Formats the full repository or installs the git hook to do it automatically.
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
index cb8f01f78..8602de56d 100644
--- a/ARCHITECTURE.md
+++ b/ARCHITECTURE.md
@@ -178,7 +178,7 @@ VS Code plugin
178 178
179To try out VS Code extensions, run `cargo install-code`. This installs both the 179To try out VS Code extensions, run `cargo install-code`. This installs both the
180`ra_lsp_server` binary and the VS Code extension. To install only the binary, use 180`ra_lsp_server` binary and the VS Code extension. To install only the binary, use
181`cargo install --path crates/ra_lsp_server --force` 181`cargo install-lsp` (shorthand for `cargo install --path crates/ra_lsp_server --force`)
182 182
183To see logs from the language server, set `RUST_LOG=info` env variable. To see 183To see logs from the language server, set `RUST_LOG=info` env variable. To see
184all communication between the server and the client, use 184all communication between the server and the client, use
@@ -186,12 +186,12 @@ all communication between the server and the client, use
186 186
187There's `rust-analyzer: status` command which prints common high-level debug 187There's `rust-analyzer: status` command which prints common high-level debug
188info. In particular, it prints info about memory usage of various data 188info. In particular, it prints info about memory usage of various data
189structures, and, if compiled with jemalloc support (`cargo install --features 189structures, and, if compiled with jemalloc support (`cargo jinstall-lsp` or
190jemalloc`), the summary statistic about the heap. 190`cargo install --path crates/ra_lsp_server --force --features jemalloc`), includes
191 statistic about the heap.
191 192
192To run tests, just `cargo test`. 193To run tests, just `cargo test`.
193 194
194To work on the VS Code extension, launch code inside `editors/code` and use `F5` to 195To work on the VS Code extension, launch code inside `editors/code` and use `F5` to
195launch/debug. To automatically apply formatter and linter suggestions, use `npm 196launch/debug. To automatically apply formatter and linter suggestions, use `npm
196run fix`. 197run fix`.
197
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index ee1e13799..2eb388e0e 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -3,9 +3,10 @@ use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SyntaxNode, TextUnit, TextRange, 4 AstNode, SyntaxNode, TextUnit, TextRange,
5 SyntaxKind::FN_DEF, 5 SyntaxKind::FN_DEF,
6 ast::{self, ArgListOwner, DocCommentsOwner}, 6 ast::{self, ArgListOwner},
7 algo::find_node_at_offset, 7 algo::find_node_at_offset,
8}; 8};
9use hir::Docs;
9 10
10use crate::{FilePosition, CallInfo, db::RootDatabase}; 11use crate::{FilePosition, CallInfo, db::RootDatabase};
11 12
@@ -26,7 +27,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
26 let fn_file = db.parse(symbol.file_id); 27 let fn_file = db.parse(symbol.file_id);
27 let fn_def = symbol.ptr.to_node(&fn_file); 28 let fn_def = symbol.ptr.to_node(&fn_file);
28 let fn_def = ast::FnDef::cast(fn_def).unwrap(); 29 let fn_def = ast::FnDef::cast(fn_def).unwrap();
29 let mut call_info = CallInfo::new(fn_def)?; 30 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?;
31
32 let mut call_info = CallInfo::new(db, function, fn_def)?;
30 // If we have a calling expression let's find which argument we are on 33 // If we have a calling expression let's find which argument we are on
31 let num_params = call_info.parameters.len(); 34 let num_params = call_info.parameters.len();
32 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 35 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
@@ -110,46 +113,13 @@ impl<'a> FnCallNode<'a> {
110} 113}
111 114
112impl CallInfo { 115impl CallInfo {
113 fn new(node: &ast::FnDef) -> Option<Self> { 116 fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> {
114 let label: String = if let Some(body) = node.body() { 117 let label = crate::completion::function_label(node)?;
115 let body_range = body.syntax().range(); 118 let doc = function.docs(db);
116 let label: String = node
117 .syntax()
118 .children()
119 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
120 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out doc comments
121 .map(|node| node.text().to_string())
122 .collect();
123 label
124 } else {
125 node.syntax().text().to_string()
126 };
127
128 let mut doc = None;
129 if let Some(docs) = node.doc_comment_text() {
130 // Massage markdown
131 let mut processed_lines = Vec::new();
132 let mut in_code_block = false;
133 for line in docs.lines() {
134 if line.starts_with("```") {
135 in_code_block = !in_code_block;
136 }
137
138 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
139 "```rust".into()
140 } else {
141 line.to_string()
142 };
143
144 processed_lines.push(line);
145 }
146
147 doc = Some(processed_lines.join("\n"));
148 }
149 119
150 Some(CallInfo { 120 Some(CallInfo {
151 parameters: param_list(node), 121 parameters: param_list(node),
152 label: label.trim().to_owned(), 122 label,
153 doc, 123 doc,
154 active_parameter: None, 124 active_parameter: None,
155 }) 125 })
@@ -284,7 +254,7 @@ fn bar() {
284 assert_eq!(info.parameters, vec!["j".to_string()]); 254 assert_eq!(info.parameters, vec!["j".to_string()]);
285 assert_eq!(info.active_parameter, Some(0)); 255 assert_eq!(info.active_parameter, Some(0));
286 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); 256 assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
287 assert_eq!(info.doc, Some("test".into())); 257 assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string()));
288 } 258 }
289 259
290 #[test] 260 #[test]
@@ -313,18 +283,18 @@ pub fn do() {
313 assert_eq!(info.active_parameter, Some(0)); 283 assert_eq!(info.active_parameter, Some(0));
314 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 284 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
315 assert_eq!( 285 assert_eq!(
316 info.doc, 286 info.doc.map(|it| it.into()),
317 Some( 287 Some(
318 r#"Adds one to the number given. 288 r#"Adds one to the number given.
319 289
320# Examples 290# Examples
321 291
322```rust 292```
323let five = 5; 293let five = 5;
324 294
325assert_eq!(6, my_crate::add_one(5)); 295assert_eq!(6, my_crate::add_one(5));
326```"# 296```"#
327 .into() 297 .to_string()
328 ) 298 )
329 ); 299 );
330 } 300 }
@@ -359,18 +329,18 @@ pub fn do_it() {
359 assert_eq!(info.active_parameter, Some(0)); 329 assert_eq!(info.active_parameter, Some(0));
360 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); 330 assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
361 assert_eq!( 331 assert_eq!(
362 info.doc, 332 info.doc.map(|it| it.into()),
363 Some( 333 Some(
364 r#"Adds one to the number given. 334 r#"Adds one to the number given.
365 335
366# Examples 336# Examples
367 337
368```rust 338```
369let five = 5; 339let five = 5;
370 340
371assert_eq!(6, my_crate::add_one(5)); 341assert_eq!(6, my_crate::add_one(5));
372```"# 342```"#
373 .into() 343 .to_string()
374 ) 344 )
375 ); 345 );
376 } 346 }
@@ -414,12 +384,12 @@ pub fn foo() {
414 ); 384 );
415 assert_eq!(info.active_parameter, Some(1)); 385 assert_eq!(info.active_parameter, Some(1));
416 assert_eq!( 386 assert_eq!(
417 info.doc, 387 info.doc.map(|it| it.into()),
418 Some( 388 Some(
419 r#"Method is called when writer finishes. 389 r#"Method is called when writer finishes.
420 390
421By default this method stops actor's `Context`."# 391By default this method stops actor's `Context`."#
422 .into() 392 .to_string()
423 ) 393 )
424 ); 394 );
425 } 395 }
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs
index b1867de42..722d94f3a 100644
--- a/crates/ra_ide_api/src/completion.rs
+++ b/crates/ra_ide_api/src/completion.rs
@@ -10,6 +10,7 @@ mod complete_scope;
10mod complete_postfix; 10mod complete_postfix;
11 11
12use ra_db::SourceDatabase; 12use ra_db::SourceDatabase;
13use ra_syntax::ast::{self, AstNode};
13 14
14use crate::{ 15use crate::{
15 db, 16 db,
@@ -61,3 +62,21 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti
61 complete_postfix::complete_postfix(&mut acc, &ctx); 62 complete_postfix::complete_postfix(&mut acc, &ctx);
62 Some(acc) 63 Some(acc)
63} 64}
65
66pub fn function_label(node: &ast::FnDef) -> Option<String> {
67 let label: String = if let Some(body) = node.body() {
68 let body_range = body.syntax().range();
69 let label: String = node
70 .syntax()
71 .children()
72 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
73 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments
74 .map(|node| node.text().to_string())
75 .collect();
76 label
77 } else {
78 node.syntax().text().to_string()
79 };
80
81 Some(label.trim().to_owned())
82}
diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs
index 49bd636a5..d3bc14894 100644
--- a/crates/ra_ide_api/src/completion/completion_item.rs
+++ b/crates/ra_ide_api/src/completion/completion_item.rs
@@ -1,12 +1,12 @@
1use hir::{Docs, Documentation}; 1use hir::{Docs, Documentation};
2use ra_syntax::{ 2use ra_syntax::TextRange;
3 ast::{self, AstNode},
4 TextRange,
5};
6use ra_text_edit::TextEdit; 3use ra_text_edit::TextEdit;
7use test_utils::tested_by; 4use test_utils::tested_by;
8 5
9use crate::completion::completion_context::CompletionContext; 6use crate::completion::{
7 completion_context::CompletionContext,
8 function_label,
9};
10 10
11/// `CompletionItem` describes a single completion variant in the editor pop-up. 11/// `CompletionItem` describes a single completion variant in the editor pop-up.
12/// It is basically a POD with various properties. To construct a 12/// It is basically a POD with various properties. To construct a
@@ -97,8 +97,8 @@ impl CompletionItem {
97 self.detail.as_ref().map(|it| it.as_str()) 97 self.detail.as_ref().map(|it| it.as_str())
98 } 98 }
99 /// A doc-comment 99 /// A doc-comment
100 pub fn documentation(&self) -> Option<&str> { 100 pub fn documentation(&self) -> Option<Documentation> {
101 self.documentation.as_ref().map(|it| it.contents()) 101 self.documentation.clone()
102 } 102 }
103 /// What string is used for filtering. 103 /// What string is used for filtering.
104 pub fn lookup(&self) -> &str { 104 pub fn lookup(&self) -> &str {
@@ -252,7 +252,7 @@ impl Builder {
252 self.documentation = Some(docs); 252 self.documentation = Some(docs);
253 } 253 }
254 254
255 if let Some(label) = function_label(ctx, function) { 255 if let Some(label) = function_item_label(ctx, function) {
256 self.detail = Some(label); 256 self.detail = Some(label);
257 } 257 }
258 258
@@ -292,24 +292,9 @@ impl Into<Vec<CompletionItem>> for Completions {
292 } 292 }
293} 293}
294 294
295fn function_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> { 295fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> {
296 let node = function.source(ctx.db).1; 296 let node = function.source(ctx.db).1;
297 297 function_label(&node)
298 let label: String = if let Some(body) = node.body() {
299 let body_range = body.syntax().range();
300 let label: String = node
301 .syntax()
302 .children()
303 .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body
304 .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments
305 .map(|node| node.text().to_string())
306 .collect();
307 label
308 } else {
309 node.syntax().text().to_string()
310 };
311
312 Some(label.trim().to_owned())
313} 298}
314 299
315#[cfg(test)] 300#[cfg(test)]
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index 51947e4cc..09cf0216d 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -58,6 +58,7 @@ pub use ra_ide_api_light::{
58pub use ra_db::{ 58pub use ra_db::{
59 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId 59 Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
60}; 60};
61pub use hir::Documentation;
61 62
62// We use jemalloc mainly to get heap usage statistics, actual performance 63// We use jemalloc mainly to get heap usage statistics, actual performance
63// differnece is not measures. 64// differnece is not measures.
@@ -266,7 +267,7 @@ impl<T> RangeInfo<T> {
266#[derive(Debug)] 267#[derive(Debug)]
267pub struct CallInfo { 268pub struct CallInfo {
268 pub label: String, 269 pub label: String,
269 pub doc: Option<String>, 270 pub doc: Option<Documentation>,
270 pub parameters: Vec<String>, 271 pub parameters: Vec<String>,
271 pub active_parameter: Option<usize>, 272 pub active_parameter: Option<usize>,
272} 273}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 8c87f5195..c033ecdea 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -87,13 +87,6 @@ impl ConvWith for CompletionItem {
87 None 87 None
88 }; 88 };
89 89
90 let documentation = self.documentation().map(|value| {
91 Documentation::MarkupContent(MarkupContent {
92 kind: MarkupKind::Markdown,
93 value: value.to_string(),
94 })
95 });
96
97 let mut res = lsp_types::CompletionItem { 90 let mut res = lsp_types::CompletionItem {
98 label: self.label().to_string(), 91 label: self.label().to_string(),
99 detail: self.detail().map(|it| it.to_string()), 92 detail: self.detail().map(|it| it.to_string()),
@@ -101,7 +94,7 @@ impl ConvWith for CompletionItem {
101 kind: self.kind().map(|it| it.conv()), 94 kind: self.kind().map(|it| it.conv()),
102 text_edit: Some(text_edit), 95 text_edit: Some(text_edit),
103 additional_text_edits, 96 additional_text_edits,
104 documentation: documentation, 97 documentation: self.documentation().map(|it| it.conv()),
105 ..Default::default() 98 ..Default::default()
106 }; 99 };
107 res.insert_text_format = Some(match self.insert_text_format() { 100 res.insert_text_format = Some(match self.insert_text_format() {
@@ -160,6 +153,16 @@ impl ConvWith for Range {
160 } 153 }
161} 154}
162 155
156impl Conv for ra_ide_api::Documentation {
157 type Output = lsp_types::Documentation;
158 fn conv(self) -> Documentation {
159 Documentation::MarkupContent(MarkupContent {
160 kind: MarkupKind::Markdown,
161 value: crate::markdown::sanitize_markdown(self).into(),
162 })
163 }
164}
165
163impl ConvWith for TextEdit { 166impl ConvWith for TextEdit {
164 type Ctx = LineIndex; 167 type Ctx = LineIndex;
165 type Output = Vec<lsp_types::TextEdit>; 168 type Output = Vec<lsp_types::TextEdit>;
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index f93d4b37d..5b5f3b948 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -2,6 +2,7 @@ mod caps;
2mod cargo_target_spec; 2mod cargo_target_spec;
3mod conv; 3mod conv;
4mod main_loop; 4mod main_loop;
5mod markdown;
5mod project_model; 6mod project_model;
6pub mod req; 7pub mod req;
7mod server_world; 8mod server_world;
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 9478ebfb8..4f75f9a22 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -1,7 +1,7 @@
1use gen_lsp_server::ErrorCode; 1use gen_lsp_server::ErrorCode;
2use lsp_types::{ 2use lsp_types::{
3 CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, 3 CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
4 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, 4 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
5 FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, 5 FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
6 MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, 6 MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
7 RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, 7 RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit,
@@ -401,12 +401,9 @@ pub fn handle_signature_help(
401 documentation: None, 401 documentation: None,
402 }) 402 })
403 .collect(); 403 .collect();
404 let documentation = call_info.doc.map(|value| { 404
405 Documentation::MarkupContent(MarkupContent { 405 let documentation = call_info.doc.map(|it| it.conv());
406 kind: MarkupKind::Markdown, 406
407 value,
408 })
409 });
410 let sig_info = SignatureInformation { 407 let sig_info = SignatureInformation {
411 label: call_info.label, 408 label: call_info.label,
412 documentation, 409 documentation,
diff --git a/crates/ra_lsp_server/src/markdown.rs b/crates/ra_lsp_server/src/markdown.rs
new file mode 100644
index 000000000..f505755e8
--- /dev/null
+++ b/crates/ra_lsp_server/src/markdown.rs
@@ -0,0 +1,38 @@
1use ra_ide_api::Documentation;
2
3pub(crate) fn sanitize_markdown(docs: Documentation) -> Documentation {
4 let docs: String = docs.into();
5
6 // Massage markdown
7 let mut processed_lines = Vec::new();
8 let mut in_code_block = false;
9 for line in docs.lines() {
10 if line.starts_with("```") {
11 in_code_block = !in_code_block;
12 }
13
14 let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
15 "```rust".into()
16 } else {
17 line.to_string()
18 };
19
20 processed_lines.push(line);
21 }
22
23 Documentation::new(&processed_lines.join("\n"))
24}
25
26#[cfg(test)]
27mod tests {
28 use super::*;
29
30 #[test]
31 fn test_codeblock_adds_rust() {
32 let comment = "```\nfn some_rust() {}\n```";
33 assert_eq!(
34 sanitize_markdown(Documentation::new(comment)).contents(),
35 "```rust\nfn some_rust() {}\n```"
36 );
37 }
38}
diff --git a/editors/code/package.json b/editors/code/package.json
index 05c67d822..1ed834d62 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -44,7 +44,9 @@
44 "vscode": "^1.1.26" 44 "vscode": "^1.1.26"
45 }, 45 },
46 "activationEvents": [ 46 "activationEvents": [
47 "onLanguage:rust" 47 "onLanguage:rust",
48 "onCommand:rust-analyzer.analyzerStatus",
49 "onCommand:rust-analyzer.collectGarbage"
48 ], 50 ],
49 "main": "./out/extension", 51 "main": "./out/extension",
50 "contributes": { 52 "contributes": {
@@ -73,35 +75,43 @@
73 "commands": [ 75 "commands": [
74 { 76 {
75 "command": "rust-analyzer.syntaxTree", 77 "command": "rust-analyzer.syntaxTree",
76 "title": "rust-analyzer: syntax tree" 78 "title": "Show syntax tree for current file",
79 "category": "Rust Analyzer"
77 }, 80 },
78 { 81 {
79 "command": "rust-analyzer.extendSelection", 82 "command": "rust-analyzer.extendSelection",
80 "title": "rust-analyzer: extend selection" 83 "title": "Extend selection",
84 "category": "Rust Analyzer"
81 }, 85 },
82 { 86 {
83 "command": "rust-analyzer.matchingBrace", 87 "command": "rust-analyzer.matchingBrace",
84 "title": "rust-analyzer: matching brace" 88 "title": "Find matching brace",
89 "category": "Rust Analyzer"
85 }, 90 },
86 { 91 {
87 "command": "rust-analyzer.parentModule", 92 "command": "rust-analyzer.parentModule",
88 "title": "rust-analyzer: parent module" 93 "title": "Locate parent module",
94 "category": "Rust Analyzer"
89 }, 95 },
90 { 96 {
91 "command": "rust-analyzer.joinLines", 97 "command": "rust-analyzer.joinLines",
92 "title": "rust-analyzer: join lines" 98 "title": "Join lines",
99 "category": "Rust Analyzer"
93 }, 100 },
94 { 101 {
95 "command": "rust-analyzer.run", 102 "command": "rust-analyzer.run",
96 "title": "rust-analyzer: run" 103 "title": "Run",
104 "category": "Rust Analyzer"
97 }, 105 },
98 { 106 {
99 "command": "rust-analyzer.analyzerStatus", 107 "command": "rust-analyzer.analyzerStatus",
100 "title": "rust-analyzer: status" 108 "title": "Status",
109 "category": "Rust Analyzer"
101 }, 110 },
102 { 111 {
103 "command": "rust-analyzer.collectGarbage", 112 "command": "rust-analyzer.collectGarbage",
104 "title": "rust-analyzer: run gc" 113 "title": "Run garbage collection",
114 "category": "Rust Analyzer"
105 } 115 }
106 ], 116 ],
107 "keybindings": [ 117 "keybindings": [