From b42b7450a207d2387114d454fb18a4479300bbf2 Mon Sep 17 00:00:00 2001 From: DJMcNab <36049421+DJMcNab@users.noreply.github.com> Date: Tue, 29 Jan 2019 17:02:06 +0000 Subject: [PATCH 1/4] Add `cargo jinstall-lsp` as a shorthand to include jemalloc support --- .cargo/config | 2 ++ ARCHITECTURE.md | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.cargo/config b/.cargo/config index 3fba0f71d9..b12f407e6f 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,6 +5,8 @@ gen-syntax = "run --package tools --bin tools -- gen-syntax" gen-tests = "run --package tools --bin tools -- gen-tests" # Installs ra_lsp_server install-lsp = "install --path crates/ra_lsp_server --force" +# Installs ra_lsp_server with the jemalloc feature +jinstall-lsp = "install --path crates/ra_lsp_server --force --features jemalloc" # Installs the visual studio code extension install-code = "run --package tools --bin tools -- install-code" # Formats the full repository or installs the git hook to do it automatically. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index cb8f01f788..8602de56d7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -178,7 +178,7 @@ VS Code plugin To try out VS Code extensions, run `cargo install-code`. This installs both the `ra_lsp_server` binary and the VS Code extension. To install only the binary, use -`cargo install --path crates/ra_lsp_server --force` +`cargo install-lsp` (shorthand for `cargo install --path crates/ra_lsp_server --force`) To see logs from the language server, set `RUST_LOG=info` env variable. To see all communication between the server and the client, use @@ -186,12 +186,12 @@ all communication between the server and the client, use There's `rust-analyzer: status` command which prints common high-level debug info. In particular, it prints info about memory usage of various data -structures, and, if compiled with jemalloc support (`cargo install --features -jemalloc`), the summary statistic about the heap. +structures, and, if compiled with jemalloc support (`cargo jinstall-lsp` or +`cargo install --path crates/ra_lsp_server --force --features jemalloc`), includes + statistic about the heap. To run tests, just `cargo test`. To work on the VS Code extension, launch code inside `editors/code` and use `F5` to launch/debug. To automatically apply formatter and linter suggestions, use `npm run fix`. - From 377ee9d3335216086a4d9cdbdeff9a803ea2815a Mon Sep 17 00:00:00 2001 From: DJMcNab <36049421+DJMcNab@users.noreply.github.com> Date: Tue, 29 Jan 2019 17:08:02 +0000 Subject: [PATCH 2/4] Start the extension when rust-analyzer status is run --- editors/code/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/editors/code/package.json b/editors/code/package.json index 05c67d822e..506137d1d5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -44,7 +44,9 @@ "vscode": "^1.1.26" }, "activationEvents": [ - "onLanguage:rust" + "onLanguage:rust", + "onCommand:rust-analyzer.analyzerStatus", + "onCommand:rust-analyzer.collectGarbage" ], "main": "./out/extension", "contributes": { From ce939795eda971f121231cca36b5bf99ef4fd6c4 Mon Sep 17 00:00:00 2001 From: DJMcNab <36049421+DJMcNab@users.noreply.github.com> Date: Tue, 29 Jan 2019 17:16:14 +0000 Subject: [PATCH 3/4] Add category to the commands --- editors/code/package.json | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 506137d1d5..1ed834d626 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -75,35 +75,43 @@ "commands": [ { "command": "rust-analyzer.syntaxTree", - "title": "rust-analyzer: syntax tree" + "title": "Show syntax tree for current file", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.extendSelection", - "title": "rust-analyzer: extend selection" + "title": "Extend selection", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.matchingBrace", - "title": "rust-analyzer: matching brace" + "title": "Find matching brace", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.parentModule", - "title": "rust-analyzer: parent module" + "title": "Locate parent module", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.joinLines", - "title": "rust-analyzer: join lines" + "title": "Join lines", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.run", - "title": "rust-analyzer: run" + "title": "Run", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.analyzerStatus", - "title": "rust-analyzer: status" + "title": "Status", + "category": "Rust Analyzer" }, { "command": "rust-analyzer.collectGarbage", - "title": "rust-analyzer: run gc" + "title": "Run garbage collection", + "category": "Rust Analyzer" } ], "keybindings": [ From b88ba007cc2631799c2334753a7de807c548685e Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Tue, 29 Jan 2019 21:39:09 -0500 Subject: [PATCH 4/4] Pass Documentation up to LSP and add "rust" to our codeblocks there --- crates/ra_ide_api/src/call_info.rs | 66 +++++-------------- crates/ra_ide_api/src/completion.rs | 19 ++++++ .../src/completion/completion_item.rs | 35 +++------- crates/ra_ide_api/src/lib.rs | 3 +- crates/ra_lsp_server/src/conv.rs | 19 +++--- crates/ra_lsp_server/src/lib.rs | 1 + .../ra_lsp_server/src/main_loop/handlers.rs | 11 ++-- crates/ra_lsp_server/src/markdown.rs | 38 +++++++++++ 8 files changed, 103 insertions(+), 89 deletions(-) create mode 100644 crates/ra_lsp_server/src/markdown.rs diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index ee1e137993..2eb388e0e7 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; use ra_syntax::{ AstNode, SyntaxNode, TextUnit, TextRange, SyntaxKind::FN_DEF, - ast::{self, ArgListOwner, DocCommentsOwner}, + ast::{self, ArgListOwner}, algo::find_node_at_offset, }; +use hir::Docs; use crate::{FilePosition, CallInfo, db::RootDatabase}; @@ -26,7 +27,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option FnCallNode<'a> { } impl CallInfo { - fn new(node: &ast::FnDef) -> Option { - let label: String = if let Some(body) = node.body() { - let body_range = body.syntax().range(); - let label: String = node - .syntax() - .children() - .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body - .filter(|child| ast::Comment::cast(child).is_none()) // Filter out doc comments - .map(|node| node.text().to_string()) - .collect(); - label - } else { - node.syntax().text().to_string() - }; - - let mut doc = None; - if let Some(docs) = node.doc_comment_text() { - // Massage markdown - let mut processed_lines = Vec::new(); - let mut in_code_block = false; - for line in docs.lines() { - if line.starts_with("```") { - in_code_block = !in_code_block; - } - - let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { - "```rust".into() - } else { - line.to_string() - }; - - processed_lines.push(line); - } - - doc = Some(processed_lines.join("\n")); - } + fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option { + let label = crate::completion::function_label(node)?; + let doc = function.docs(db); Some(CallInfo { parameters: param_list(node), - label: label.trim().to_owned(), + label, doc, active_parameter: None, }) @@ -284,7 +254,7 @@ fn bar() { assert_eq!(info.parameters, vec!["j".to_string()]); assert_eq!(info.active_parameter, Some(0)); assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); - assert_eq!(info.doc, Some("test".into())); + assert_eq!(info.doc.map(|it| it.into()), Some("test".to_string())); } #[test] @@ -313,18 +283,18 @@ pub fn do() { assert_eq!(info.active_parameter, Some(0)); assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); assert_eq!( - info.doc, + info.doc.map(|it| it.into()), Some( r#"Adds one to the number given. # Examples -```rust +``` let five = 5; assert_eq!(6, my_crate::add_one(5)); ```"# - .into() + .to_string() ) ); } @@ -359,18 +329,18 @@ pub fn do_it() { assert_eq!(info.active_parameter, Some(0)); assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); assert_eq!( - info.doc, + info.doc.map(|it| it.into()), Some( r#"Adds one to the number given. # Examples -```rust +``` let five = 5; assert_eq!(6, my_crate::add_one(5)); ```"# - .into() + .to_string() ) ); } @@ -414,12 +384,12 @@ pub fn foo() { ); assert_eq!(info.active_parameter, Some(1)); assert_eq!( - info.doc, + info.doc.map(|it| it.into()), Some( r#"Method is called when writer finishes. By default this method stops actor's `Context`."# - .into() + .to_string() ) ); } diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index b1867de427..722d94f3a2 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs @@ -10,6 +10,7 @@ mod complete_scope; mod complete_postfix; use ra_db::SourceDatabase; +use ra_syntax::ast::{self, AstNode}; use crate::{ db, @@ -61,3 +62,21 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti complete_postfix::complete_postfix(&mut acc, &ctx); Some(acc) } + +pub fn function_label(node: &ast::FnDef) -> Option { + let label: String = if let Some(body) = node.body() { + let body_range = body.syntax().range(); + let label: String = node + .syntax() + .children() + .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body + .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments + .map(|node| node.text().to_string()) + .collect(); + label + } else { + node.syntax().text().to_string() + }; + + Some(label.trim().to_owned()) +} diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index 49bd636a5d..d3bc148944 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 @@ use hir::{Docs, Documentation}; -use ra_syntax::{ - ast::{self, AstNode}, - TextRange, -}; +use ra_syntax::TextRange; use ra_text_edit::TextEdit; use test_utils::tested_by; -use crate::completion::completion_context::CompletionContext; +use crate::completion::{ + completion_context::CompletionContext, + function_label, +}; /// `CompletionItem` describes a single completion variant in the editor pop-up. /// It is basically a POD with various properties. To construct a @@ -97,8 +97,8 @@ impl CompletionItem { self.detail.as_ref().map(|it| it.as_str()) } /// A doc-comment - pub fn documentation(&self) -> Option<&str> { - self.documentation.as_ref().map(|it| it.contents()) + pub fn documentation(&self) -> Option { + self.documentation.clone() } /// What string is used for filtering. pub fn lookup(&self) -> &str { @@ -252,7 +252,7 @@ impl Builder { self.documentation = Some(docs); } - if let Some(label) = function_label(ctx, function) { + if let Some(label) = function_item_label(ctx, function) { self.detail = Some(label); } @@ -292,24 +292,9 @@ impl Into> for Completions { } } -fn function_label(ctx: &CompletionContext, function: hir::Function) -> Option { +fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Option { let node = function.source(ctx.db).1; - - let label: String = if let Some(body) = node.body() { - let body_range = body.syntax().range(); - let label: String = node - .syntax() - .children() - .filter(|child| !child.range().is_subrange(&body_range)) // Filter out body - .filter(|child| ast::Comment::cast(child).is_none()) // Filter out comments - .map(|node| node.text().to_string()) - .collect(); - label - } else { - node.syntax().text().to_string() - }; - - Some(label.trim().to_owned()) + function_label(&node) } #[cfg(test)] diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 51947e4ccd..09cf0216d0 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::{ pub use ra_db::{ Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId }; +pub use hir::Documentation; // We use jemalloc mainly to get heap usage statistics, actual performance // differnece is not measures. @@ -266,7 +267,7 @@ impl RangeInfo { #[derive(Debug)] pub struct CallInfo { pub label: String, - pub doc: Option, + pub doc: Option, pub parameters: Vec, pub active_parameter: Option, } diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 8c87f51951..c033ecdeaa 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 { None }; - let documentation = self.documentation().map(|value| { - Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: value.to_string(), - }) - }); - let mut res = lsp_types::CompletionItem { label: self.label().to_string(), detail: self.detail().map(|it| it.to_string()), @@ -101,7 +94,7 @@ impl ConvWith for CompletionItem { kind: self.kind().map(|it| it.conv()), text_edit: Some(text_edit), additional_text_edits, - documentation: documentation, + documentation: self.documentation().map(|it| it.conv()), ..Default::default() }; res.insert_text_format = Some(match self.insert_text_format() { @@ -160,6 +153,16 @@ impl ConvWith for Range { } } +impl Conv for ra_ide_api::Documentation { + type Output = lsp_types::Documentation; + fn conv(self) -> Documentation { + Documentation::MarkupContent(MarkupContent { + kind: MarkupKind::Markdown, + value: crate::markdown::sanitize_markdown(self).into(), + }) + } +} + impl ConvWith for TextEdit { type Ctx = LineIndex; type Output = Vec; diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index f93d4b37d9..5b5f3b948f 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs @@ -2,6 +2,7 @@ mod caps; mod cargo_target_spec; mod conv; mod main_loop; +mod markdown; mod project_model; pub mod req; mod 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 9478ebfb89..4f75f9a229 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 @@ use gen_lsp_server::ErrorCode; use lsp_types::{ CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, - DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, + DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, @@ -401,12 +401,9 @@ pub fn handle_signature_help( documentation: None, }) .collect(); - let documentation = call_info.doc.map(|value| { - Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value, - }) - }); + + let documentation = call_info.doc.map(|it| it.conv()); + let sig_info = SignatureInformation { label: call_info.label, documentation, diff --git a/crates/ra_lsp_server/src/markdown.rs b/crates/ra_lsp_server/src/markdown.rs new file mode 100644 index 0000000000..f505755e86 --- /dev/null +++ b/crates/ra_lsp_server/src/markdown.rs @@ -0,0 +1,38 @@ +use ra_ide_api::Documentation; + +pub(crate) fn sanitize_markdown(docs: Documentation) -> Documentation { + let docs: String = docs.into(); + + // Massage markdown + let mut processed_lines = Vec::new(); + let mut in_code_block = false; + for line in docs.lines() { + if line.starts_with("```") { + in_code_block = !in_code_block; + } + + let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { + "```rust".into() + } else { + line.to_string() + }; + + processed_lines.push(line); + } + + Documentation::new(&processed_lines.join("\n")) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_codeblock_adds_rust() { + let comment = "```\nfn some_rust() {}\n```"; + assert_eq!( + sanitize_markdown(Documentation::new(comment)).contents(), + "```rust\nfn some_rust() {}\n```" + ); + } +}