704: Feat/jinstall lsp r=matklad a=DJMcNab

Add `cargo jinstall-lsp` as a shorthand to include jemalloc support

Also activate the extension when the commands are run which is makes sense to activate.

I still need to work out what `Run` actually does to give it a better name, and other extensions through some voodoo magic are able to hide their commands from the command palette before the extension is activated, which would be a better fix.

705: Pass Documentation up to LSP and add "rust" to our codeblocks there r=matklad a=kjeremy

Fixes #699

Co-authored-by: DJMcNab <36049421+djmcnab@users.noreply.github.com>
Co-authored-by: Jeremy Kolb <kjeremy@gmail.com>
This commit is contained in:
bors[bot] 2019-01-30 19:08:06 +00:00
commit 897e74f089
11 changed files with 128 additions and 102 deletions

View file

@ -5,6 +5,8 @@ gen-syntax = "run --package tools --bin tools -- gen-syntax"
gen-tests = "run --package tools --bin tools -- gen-tests" gen-tests = "run --package tools --bin tools -- gen-tests"
# Installs ra_lsp_server # Installs ra_lsp_server
install-lsp = "install --path crates/ra_lsp_server --force" 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 # Installs the visual studio code extension
install-code = "run --package tools --bin tools -- install-code" install-code = "run --package tools --bin tools -- install-code"
# Formats the full repository or installs the git hook to do it automatically. # Formats the full repository or installs the git hook to do it automatically.

View file

@ -178,7 +178,7 @@ VS Code plugin
To try out VS Code extensions, run `cargo install-code`. This installs both the 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 `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 To see logs from the language server, set `RUST_LOG=info` env variable. To see
all communication between the server and the client, use 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 There's `rust-analyzer: status` command which prints common high-level debug
info. In particular, it prints info about memory usage of various data info. In particular, it prints info about memory usage of various data
structures, and, if compiled with jemalloc support (`cargo install --features structures, and, if compiled with jemalloc support (`cargo jinstall-lsp` or
jemalloc`), the summary statistic about the heap. `cargo install --path crates/ra_lsp_server --force --features jemalloc`), includes
statistic about the heap.
To run tests, just `cargo test`. To run tests, just `cargo test`.
To work on the VS Code extension, launch code inside `editors/code` and use `F5` to 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 launch/debug. To automatically apply formatter and linter suggestions, use `npm
run fix`. run fix`.

View file

@ -3,9 +3,10 @@ use ra_db::SourceDatabase;
use ra_syntax::{ use ra_syntax::{
AstNode, SyntaxNode, TextUnit, TextRange, AstNode, SyntaxNode, TextUnit, TextRange,
SyntaxKind::FN_DEF, SyntaxKind::FN_DEF,
ast::{self, ArgListOwner, DocCommentsOwner}, ast::{self, ArgListOwner},
algo::find_node_at_offset, algo::find_node_at_offset,
}; };
use hir::Docs;
use crate::{FilePosition, CallInfo, db::RootDatabase}; use crate::{FilePosition, CallInfo, db::RootDatabase};
@ -26,7 +27,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
let fn_file = db.parse(symbol.file_id); let fn_file = db.parse(symbol.file_id);
let fn_def = symbol.ptr.to_node(&fn_file); let fn_def = symbol.ptr.to_node(&fn_file);
let fn_def = ast::FnDef::cast(fn_def).unwrap(); let fn_def = ast::FnDef::cast(fn_def).unwrap();
let mut call_info = CallInfo::new(fn_def)?; let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?;
let mut call_info = CallInfo::new(db, function, fn_def)?;
// If we have a calling expression let's find which argument we are on // If we have a calling expression let's find which argument we are on
let num_params = call_info.parameters.len(); let num_params = call_info.parameters.len();
let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();
@ -110,46 +113,13 @@ impl<'a> FnCallNode<'a> {
} }
impl CallInfo { impl CallInfo {
fn new(node: &ast::FnDef) -> Option<Self> { fn new(db: &RootDatabase, function: hir::Function, node: &ast::FnDef) -> Option<Self> {
let label: String = if let Some(body) = node.body() { let label = crate::completion::function_label(node)?;
let body_range = body.syntax().range(); let doc = function.docs(db);
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"));
}
Some(CallInfo { Some(CallInfo {
parameters: param_list(node), parameters: param_list(node),
label: label.trim().to_owned(), label,
doc, doc,
active_parameter: None, active_parameter: None,
}) })
@ -284,7 +254,7 @@ fn bar() {
assert_eq!(info.parameters, vec!["j".to_string()]); assert_eq!(info.parameters, vec!["j".to_string()]);
assert_eq!(info.active_parameter, Some(0)); assert_eq!(info.active_parameter, Some(0));
assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string()); 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] #[test]
@ -313,18 +283,18 @@ pub fn do() {
assert_eq!(info.active_parameter, Some(0)); assert_eq!(info.active_parameter, Some(0));
assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
assert_eq!( assert_eq!(
info.doc, info.doc.map(|it| it.into()),
Some( Some(
r#"Adds one to the number given. r#"Adds one to the number given.
# Examples # Examples
```rust ```
let five = 5; let five = 5;
assert_eq!(6, my_crate::add_one(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.active_parameter, Some(0));
assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string()); assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
assert_eq!( assert_eq!(
info.doc, info.doc.map(|it| it.into()),
Some( Some(
r#"Adds one to the number given. r#"Adds one to the number given.
# Examples # Examples
```rust ```
let five = 5; let five = 5;
assert_eq!(6, my_crate::add_one(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.active_parameter, Some(1));
assert_eq!( assert_eq!(
info.doc, info.doc.map(|it| it.into()),
Some( Some(
r#"Method is called when writer finishes. r#"Method is called when writer finishes.
By default this method stops actor's `Context`."# By default this method stops actor's `Context`."#
.into() .to_string()
) )
); );
} }

View file

@ -10,6 +10,7 @@ mod complete_scope;
mod complete_postfix; mod complete_postfix;
use ra_db::SourceDatabase; use ra_db::SourceDatabase;
use ra_syntax::ast::{self, AstNode};
use crate::{ use crate::{
db, db,
@ -61,3 +62,21 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti
complete_postfix::complete_postfix(&mut acc, &ctx); complete_postfix::complete_postfix(&mut acc, &ctx);
Some(acc) Some(acc)
} }
pub fn function_label(node: &ast::FnDef) -> Option<String> {
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())
}

View file

@ -1,12 +1,12 @@
use hir::{Docs, Documentation}; use hir::{Docs, Documentation};
use ra_syntax::{ use ra_syntax::TextRange;
ast::{self, AstNode},
TextRange,
};
use ra_text_edit::TextEdit; use ra_text_edit::TextEdit;
use test_utils::tested_by; 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. /// `CompletionItem` describes a single completion variant in the editor pop-up.
/// It is basically a POD with various properties. To construct a /// 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()) self.detail.as_ref().map(|it| it.as_str())
} }
/// A doc-comment /// A doc-comment
pub fn documentation(&self) -> Option<&str> { pub fn documentation(&self) -> Option<Documentation> {
self.documentation.as_ref().map(|it| it.contents()) self.documentation.clone()
} }
/// What string is used for filtering. /// What string is used for filtering.
pub fn lookup(&self) -> &str { pub fn lookup(&self) -> &str {
@ -252,7 +252,7 @@ impl Builder {
self.documentation = Some(docs); 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); self.detail = Some(label);
} }
@ -292,24 +292,9 @@ impl Into<Vec<CompletionItem>> for Completions {
} }
} }
fn function_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> { fn function_item_label(ctx: &CompletionContext, function: hir::Function) -> Option<String> {
let node = function.source(ctx.db).1; let node = function.source(ctx.db).1;
function_label(&node)
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())
} }
#[cfg(test)] #[cfg(test)]

View file

@ -58,6 +58,7 @@ pub use ra_ide_api_light::{
pub use ra_db::{ pub use ra_db::{
Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, SourceRootId
}; };
pub use hir::Documentation;
// We use jemalloc mainly to get heap usage statistics, actual performance // We use jemalloc mainly to get heap usage statistics, actual performance
// differnece is not measures. // differnece is not measures.
@ -266,7 +267,7 @@ impl<T> RangeInfo<T> {
#[derive(Debug)] #[derive(Debug)]
pub struct CallInfo { pub struct CallInfo {
pub label: String, pub label: String,
pub doc: Option<String>, pub doc: Option<Documentation>,
pub parameters: Vec<String>, pub parameters: Vec<String>,
pub active_parameter: Option<usize>, pub active_parameter: Option<usize>,
} }

View file

@ -87,13 +87,6 @@ impl ConvWith for CompletionItem {
None None
}; };
let documentation = self.documentation().map(|value| {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: value.to_string(),
})
});
let mut res = lsp_types::CompletionItem { let mut res = lsp_types::CompletionItem {
label: self.label().to_string(), label: self.label().to_string(),
detail: self.detail().map(|it| it.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()), kind: self.kind().map(|it| it.conv()),
text_edit: Some(text_edit), text_edit: Some(text_edit),
additional_text_edits, additional_text_edits,
documentation: documentation, documentation: self.documentation().map(|it| it.conv()),
..Default::default() ..Default::default()
}; };
res.insert_text_format = Some(match self.insert_text_format() { 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 { impl ConvWith for TextEdit {
type Ctx = LineIndex; type Ctx = LineIndex;
type Output = Vec<lsp_types::TextEdit>; type Output = Vec<lsp_types::TextEdit>;

View file

@ -2,6 +2,7 @@ mod caps;
mod cargo_target_spec; mod cargo_target_spec;
mod conv; mod conv;
mod main_loop; mod main_loop;
mod markdown;
mod project_model; mod project_model;
pub mod req; pub mod req;
mod server_world; mod server_world;

View file

@ -1,7 +1,7 @@
use gen_lsp_server::ErrorCode; use gen_lsp_server::ErrorCode;
use lsp_types::{ use lsp_types::{
CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity, CodeActionResponse, CodeLens, Command, Diagnostic, DiagnosticSeverity,
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, FoldingRangeKind, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent,
MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, MarkupKind, ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range,
RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, RenameParams, SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit,
@ -401,12 +401,9 @@ pub fn handle_signature_help(
documentation: None, documentation: None,
}) })
.collect(); .collect();
let documentation = call_info.doc.map(|value| {
Documentation::MarkupContent(MarkupContent { let documentation = call_info.doc.map(|it| it.conv());
kind: MarkupKind::Markdown,
value,
})
});
let sig_info = SignatureInformation { let sig_info = SignatureInformation {
label: call_info.label, label: call_info.label,
documentation, documentation,

View file

@ -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```"
);
}
}

View file

@ -44,7 +44,9 @@
"vscode": "^1.1.26" "vscode": "^1.1.26"
}, },
"activationEvents": [ "activationEvents": [
"onLanguage:rust" "onLanguage:rust",
"onCommand:rust-analyzer.analyzerStatus",
"onCommand:rust-analyzer.collectGarbage"
], ],
"main": "./out/extension", "main": "./out/extension",
"contributes": { "contributes": {
@ -73,35 +75,43 @@
"commands": [ "commands": [
{ {
"command": "rust-analyzer.syntaxTree", "command": "rust-analyzer.syntaxTree",
"title": "rust-analyzer: syntax tree" "title": "Show syntax tree for current file",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.extendSelection", "command": "rust-analyzer.extendSelection",
"title": "rust-analyzer: extend selection" "title": "Extend selection",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.matchingBrace", "command": "rust-analyzer.matchingBrace",
"title": "rust-analyzer: matching brace" "title": "Find matching brace",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.parentModule", "command": "rust-analyzer.parentModule",
"title": "rust-analyzer: parent module" "title": "Locate parent module",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.joinLines", "command": "rust-analyzer.joinLines",
"title": "rust-analyzer: join lines" "title": "Join lines",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.run", "command": "rust-analyzer.run",
"title": "rust-analyzer: run" "title": "Run",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.analyzerStatus", "command": "rust-analyzer.analyzerStatus",
"title": "rust-analyzer: status" "title": "Status",
"category": "Rust Analyzer"
}, },
{ {
"command": "rust-analyzer.collectGarbage", "command": "rust-analyzer.collectGarbage",
"title": "rust-analyzer: run gc" "title": "Run garbage collection",
"category": "Rust Analyzer"
} }
], ],
"keybindings": [ "keybindings": [