From c8f27a4a886413a15a2a6af4a87b39b901c873a8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 01:54:54 +0200 Subject: Generate features docs from source --- xtask/src/codegen/gen_feature_docs.rs | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 xtask/src/codegen/gen_feature_docs.rs (limited to 'xtask/src/codegen/gen_feature_docs.rs') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 00000000000..58318564872 --- /dev/null +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -0,0 +1,72 @@ +//! Generates `assists.md` documentation. + +use std::{fmt, fs, path::PathBuf}; + +use crate::{ + codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, + project_root, rust_files, Result, +}; + +pub fn generate_feature_docs(mode: Mode) -> Result<()> { + let features = Feature::collect()?; + let contents = features.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); + + let dst = project_root().join("docs/user/generated_features.adoc"); + codegen::update(&dst, &contents, mode)?; + Ok(()) +} + +#[derive(Debug)] +struct Feature { + id: String, + path: PathBuf, + doc: String, +} + +impl Feature { + fn collect() -> Result> { + let mut res = Vec::new(); + for path in rust_files(&project_root()) { + collect_file(&mut res, path)?; + } + res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id)); + return Ok(res); + + fn collect_file(acc: &mut Vec, path: PathBuf) -> Result<()> { + let text = fs::read_to_string(&path)?; + let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text); + + for block in comment_blocks { + let id = block.id; + assert!( + id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)), + "bad feature: {}", + id + ); + let doc = block.contents.join("\n"); + acc.push(Feature { id, path: path.clone(), doc }) + } + + Ok(()) + } + } +} + +impl fmt::Display for Feature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "=== {}", self.id)?; + let path = self.path.strip_prefix(&project_root()).unwrap(); + let name = self.path.file_name().unwrap(); + + //FIXME: generate line number as well + writeln!( + f, + "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]", + path.display(), + name.to_str().unwrap(), + )?; + + writeln!(f, "\n{}", self.doc)?; + Ok(()) + } +} -- cgit 1.4.1-3-g733a5 From f593393ebb9bfa515caf168a9f037324eeb6edfe Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:45:41 +0200 Subject: Specify actions --- crates/ra_ide/src/goto_definition.rs | 2 +- crates/ra_ide/src/goto_implementation.rs | 2 +- crates/ra_ide/src/goto_type_definition.rs | 8 +++++++- crates/ra_ide/src/runnables.rs | 11 +++++++++++ docs/user/features.md | 6 ------ docs/user/generated_features.adoc | 27 ++++++++++++++++++++++++--- xtask/src/codegen/gen_feature_docs.rs | 25 ++++++++++++++++++++----- 7 files changed, 64 insertions(+), 17 deletions(-) (limited to 'xtask/src/codegen/gen_feature_docs.rs') diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 83ea5092c81..daeeac76f63 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -17,7 +17,7 @@ use crate::{ FilePosition, NavigationTarget, RangeInfo, }; -// Feature: Go To Definition +// Feature: Go to Definition // // Navigates to the definition of an identifier. // diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs index a5a296d228b..622a094e600 100644 --- a/crates/ra_ide/src/goto_implementation.rs +++ b/crates/ra_ide/src/goto_implementation.rs @@ -6,7 +6,7 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; -// Feature: Go To Implementation +// Feature: Go to Implementation // // Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. // diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index eeadfa9ee70..e74a502ecb9 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs @@ -5,9 +5,15 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; -// Feature: Go To Type Definition +// Feature: Go to Type Definition // // Navigates to the type of an identifier. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Go to Type Definition* +// |=== pub(crate) fn goto_type_definition( db: &RootDatabase, position: FilePosition, diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 6e7e47199c8..4bf2678e104 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -44,6 +44,17 @@ pub enum RunnableKind { Bin, } +// Feature: Run +// +// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor +// location**. Super useful for repeatedly running just a single test. Do bind this +// to a shortcut! +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Run** +// |=== pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { let sema = Semantics::new(db); let source_file = sema.parse(file_id); diff --git a/docs/user/features.md b/docs/user/features.md index 4d940225249..df8e73a20d5 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -4,12 +4,6 @@ you can use Ctrl+Shift+P to search for the corresponding action. ### Commands ctrl+shift+p -#### Run - -Shows a popup suggesting to run a test/benchmark/binary **at the current cursor -location**. Super useful for repeatedly running just a single test. Do bind this -to a shortcut! - #### Parent Module Navigates to the parent module of the current module. diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index e1eb5d88a8f..1f6fcc97410 100644 --- a/docs/user/generated_features.adoc +++ b/docs/user/generated_features.adoc @@ -29,7 +29,7 @@ Provides a tree of the symbols defined in the file. Can be used to |=== -=== Go To Definition +=== Go to Definition **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs] @@ -42,7 +42,7 @@ Navigates to the definition of an identifier. |=== -=== Go To Implementation +=== Go to Implementation **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs] @@ -55,12 +55,18 @@ Navigates to the impl block of structs, enums or traits. Also implemented as a c |=== -=== Go To Type Definition +=== Go to Type Definition **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs] Navigates to the type of an identifier. +|=== +| Editor | Action Name + +| VS Code | **Go to Type Definition* +|=== + === On Typing Assists **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs] @@ -73,6 +79,21 @@ Some features trigger on typing certain characters: - typing `.` in a chain method call auto-indents +=== Run +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs] + + +Shows a popup suggesting to run a test/benchmark/binary **at the current cursor +location**. Super useful for repeatedly running just a single test. Do bind this +to a shortcut! + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Run** +|=== + + === Workspace Symbol **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs] diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index 58318564872..170a3e88942 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -38,11 +38,7 @@ impl Feature { for block in comment_blocks { let id = block.id; - assert!( - id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)), - "bad feature: {}", - id - ); + assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); let doc = block.contents.join("\n"); acc.push(Feature { id, path: path.clone(), doc }) } @@ -52,6 +48,25 @@ impl Feature { } } +fn is_valid_feature_name(feature: &str) -> bool { + 'word: for word in feature.split_whitespace() { + for &short in ["to"].iter() { + if word == short { + continue 'word; + } + } + for &short in ["To"].iter() { + if word == short { + return false; + } + } + if !word.starts_with(char::is_uppercase) { + return false; + } + } + true +} + impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}", self.id)?; -- cgit 1.4.1-3-g733a5 From 1c6a2eb14a84c3a66972d1a6da429cca1aa8b40a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 11:29:19 +0200 Subject: Move the rest of the features to generated docs --- crates/ra_ide/src/completion.rs | 47 ++++++- crates/ra_ide/src/completion/complete_postfix.rs | 6 +- crates/ra_ide/src/hover.rs | 116 ++++++++-------- crates/ra_ide/src/inlay_hints.rs | 22 +++- crates/ra_ide/src/syntax_highlighting.rs | 161 ++++++++++++----------- crates/ra_ide/src/syntax_tree.rs | 2 +- docs/user/features.md | 96 -------------- docs/user/generated_features.adoc | 143 ++++++++++++++++++++ docs/user/readme.adoc | 1 + xtask/src/codegen/gen_feature_docs.rs | 4 +- 10 files changed, 358 insertions(+), 240 deletions(-) delete mode 100644 docs/user/features.md (limited to 'xtask/src/codegen/gen_feature_docs.rs') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 191300704b5..d890b69d26f 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - mod completion_config; mod completion_item; mod completion_context; @@ -35,6 +33,51 @@ pub use crate::completion::{ completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, }; +//FIXME: split the following feature into fine-grained features. + +// Feature: Magic Completions +// +// In addition to usual reference completion, rust-analyzer provides some ✨magic✨ +// completions as well: +// +// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor +// is placed at the appropriate position. Even though `if` is easy to type, you +// still want to complete it, to get ` { }` for free! `return` is inserted with a +// space or `;` depending on the return type of the function. +// +// When completing a function call, `()` are automatically inserted. If a function +// takes arguments, the cursor is positioned inside the parenthesis. +// +// There are postfix completions, which can be triggered by typing something like +// `foo().if`. The word after `.` determines postfix completion. Possible variants are: +// +// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` +// - `expr.match` -> `match expr {}` +// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` +// - `expr.ref` -> `&expr` +// - `expr.refm` -> `&mut expr` +// - `expr.not` -> `!expr` +// - `expr.dbg` -> `dbg!(expr)` +// +// There also snippet completions: +// +// .Expressions +// - `pd` -> `println!("{:?}")` +// - `ppd` -> `println!("{:#?}")` +// +// .Items +// - `tfn` -> `#[test] fn f(){}` +// - `tmod` -> +// ```rust +// #[cfg(test)] +// mod tests { +// use super::*; +// +// #[test] +// fn test_fn() {} +// } +// ``` + /// Main entry point for completion. We run completion as a two-phase process. /// /// First, we look at the position and collect a so-called `CompletionContext. diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 02e660ca8e4..59b58bf98b6 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs @@ -1,12 +1,11 @@ //! FIXME: write short doc here - +use ra_assists::utils::TryEnum; use ra_syntax::{ ast::{self, AstNode}, TextRange, TextSize, }; use ra_text_edit::TextEdit; -use super::completion_config::SnippetCap; use crate::{ completion::{ completion_context::CompletionContext, @@ -14,7 +13,8 @@ use crate::{ }, CompletionItem, }; -use ra_assists::utils::TryEnum; + +use super::completion_config::SnippetCap; pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.config.enable_postfix_completions { diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 3e721dcca39..d96cb559691 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,10 +1,10 @@ -//! Logic for computing info that is displayed when the user hovers over any -//! source code items (e.g. function call, struct field, variable symbol...) +use std::iter::once; use hir::{ Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, ModuleSource, Semantics, }; +use itertools::Itertools; use ra_db::SourceDatabase; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, @@ -21,8 +21,6 @@ use crate::{ display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, FilePosition, RangeInfo, }; -use itertools::Itertools; -use std::iter::once; /// Contains the results when hovering over an item #[derive(Debug, Default)] @@ -62,6 +60,63 @@ impl HoverResult { } } +// Feature: Hover +// +// Shows additional information, like type of an expression or documentation for definition when "focusing" code. +// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. +pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id).syntax().clone(); + let token = pick_best(file.token_at_offset(position.offset))?; + let token = sema.descend_into_macros(token); + + let mut res = HoverResult::new(); + + if let Some((node, name_kind)) = match_ast! { + match (token.parent()) { + ast::NameRef(name_ref) => { + classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) + }, + ast::Name(name) => { + classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) + }, + _ => None, + } + } { + let range = sema.original_range(&node).range; + res.extend(hover_text_from_name_kind(db, name_kind)); + + if !res.is_empty() { + return Some(RangeInfo::new(range, res)); + } + } + + let node = token + .ancestors() + .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; + + let ty = match_ast! { + match node { + ast::MacroCall(_it) => { + // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. + // (e.g expanding a builtin macro). So we give up here. + return None; + }, + ast::Expr(it) => { + sema.type_of_expr(&it) + }, + ast::Pat(it) => { + sema.type_of_pat(&it) + }, + _ => None, + } + }?; + + res.extend(Some(rust_code_markup(&ty.display(db)))); + let range = sema.original_range(&node).range; + Some(RangeInfo::new(range, res)) +} + fn hover_text( docs: Option, desc: Option, @@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option Option> { - let sema = Semantics::new(db); - let file = sema.parse(position.file_id).syntax().clone(); - let token = pick_best(file.token_at_offset(position.offset))?; - let token = sema.descend_into_macros(token); - - let mut res = HoverResult::new(); - - if let Some((node, name_kind)) = match_ast! { - match (token.parent()) { - ast::NameRef(name_ref) => { - classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) - }, - ast::Name(name) => { - classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition())) - }, - _ => None, - } - } { - let range = sema.original_range(&node).range; - res.extend(hover_text_from_name_kind(db, name_kind)); - - if !res.is_empty() { - return Some(RangeInfo::new(range, res)); - } - } - - let node = token - .ancestors() - .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?; - - let ty = match_ast! { - match node { - ast::MacroCall(_it) => { - // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. - // (e.g expanding a builtin macro). So we give up here. - return None; - }, - ast::Expr(it) => { - sema.type_of_expr(&it) - }, - ast::Pat(it) => { - sema.type_of_pat(&it) - }, - _ => None, - } - }?; - - res.extend(Some(rust_code_markup(&ty.display(db)))); - let range = sema.original_range(&node).range; - Some(RangeInfo::new(range, res)) -} - fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index b391f903a80..75bd3c96bb1 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -1,5 +1,3 @@ -//! This module defines multiple types of inlay hints and their visibility - use hir::{Adt, HirDisplay, Semantics, Type}; use ra_ide_db::RootDatabase; use ra_prof::profile; @@ -39,6 +37,26 @@ pub struct InlayHint { pub label: SmolStr, } +// Feature: Inlay Hints +// +// rust-analyzer shows additional information inline with the source code. +// Editors usually render this using read-only virtual text snippets interspersed with code. +// +// rust-analyzer shows hits for +// +// * types of local variables +// * names of function arguments +// * types of chained expressions +// +// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations. +// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: +// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2]. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Toggle inlay hints* +// |=== pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 8a995d779ba..3ab1f0a2157 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,5 +1,3 @@ -//! Implements syntax highlighting. - mod tags; mod html; #[cfg(test)] @@ -32,81 +30,15 @@ pub struct HighlightedRange { pub binding_hash: Option, } -#[derive(Debug)] -struct HighlightedRangeStack { - stack: Vec>, -} - -/// We use a stack to implement the flattening logic for the highlighted -/// syntax ranges. -impl HighlightedRangeStack { - fn new() -> Self { - Self { stack: vec![Vec::new()] } - } - - fn push(&mut self) { - self.stack.push(Vec::new()); - } - - /// Flattens the highlighted ranges. - /// - /// For example `#[cfg(feature = "foo")]` contains the nested ranges: - /// 1) parent-range: Attribute [0, 23) - /// 2) child-range: String [16, 21) - /// - /// The following code implements the flattening, for our example this results to: - /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]` - fn pop(&mut self) { - let children = self.stack.pop().unwrap(); - let prev = self.stack.last_mut().unwrap(); - let needs_flattening = !children.is_empty() - && !prev.is_empty() - && prev.last().unwrap().range.contains_range(children.first().unwrap().range); - if !needs_flattening { - prev.extend(children); - } else { - let mut parent = prev.pop().unwrap(); - for ele in children { - assert!(parent.range.contains_range(ele.range)); - let mut cloned = parent.clone(); - parent.range = TextRange::new(parent.range.start(), ele.range.start()); - cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); - if !parent.range.is_empty() { - prev.push(parent); - } - prev.push(ele); - parent = cloned; - } - if !parent.range.is_empty() { - prev.push(parent); - } - } - } - - fn add(&mut self, range: HighlightedRange) { - self.stack - .last_mut() - .expect("during DFS traversal, the stack must not be empty") - .push(range) - } - - fn flattened(mut self) -> Vec { - assert_eq!( - self.stack.len(), - 1, - "after DFS traversal, the stack should only contain a single element" - ); - let mut res = self.stack.pop().unwrap(); - res.sort_by_key(|range| range.range.start()); - // Check that ranges are sorted and disjoint - assert!(res - .iter() - .zip(res.iter().skip(1)) - .all(|(left, right)| left.range.end() <= right.range.start())); - res - } -} - +// Feature: Semantic Syntax Highlighting +// +// rust-analyzer highlights the code semantically. +// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. +// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. +// It's up to the client to map those to specific colors. +// +// The general rule is that a reference to an entity gets colored the same way as the entity itself. +// We also give special modifier for `mut` and `&mut` local variables. pub(crate) fn highlight( db: &RootDatabase, file_id: FileId, @@ -291,6 +223,81 @@ pub(crate) fn highlight( stack.flattened() } +#[derive(Debug)] +struct HighlightedRangeStack { + stack: Vec>, +} + +/// We use a stack to implement the flattening logic for the highlighted +/// syntax ranges. +impl HighlightedRangeStack { + fn new() -> Self { + Self { stack: vec![Vec::new()] } + } + + fn push(&mut self) { + self.stack.push(Vec::new()); + } + + /// Flattens the highlighted ranges. + /// + /// For example `#[cfg(feature = "foo")]` contains the nested ranges: + /// 1) parent-range: Attribute [0, 23) + /// 2) child-range: String [16, 21) + /// + /// The following code implements the flattening, for our example this results to: + /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]` + fn pop(&mut self) { + let children = self.stack.pop().unwrap(); + let prev = self.stack.last_mut().unwrap(); + let needs_flattening = !children.is_empty() + && !prev.is_empty() + && prev.last().unwrap().range.contains_range(children.first().unwrap().range); + if !needs_flattening { + prev.extend(children); + } else { + let mut parent = prev.pop().unwrap(); + for ele in children { + assert!(parent.range.contains_range(ele.range)); + let mut cloned = parent.clone(); + parent.range = TextRange::new(parent.range.start(), ele.range.start()); + cloned.range = TextRange::new(ele.range.end(), cloned.range.end()); + if !parent.range.is_empty() { + prev.push(parent); + } + prev.push(ele); + parent = cloned; + } + if !parent.range.is_empty() { + prev.push(parent); + } + } + } + + fn add(&mut self, range: HighlightedRange) { + self.stack + .last_mut() + .expect("during DFS traversal, the stack must not be empty") + .push(range) + } + + fn flattened(mut self) -> Vec { + assert_eq!( + self.stack.len(), + 1, + "after DFS traversal, the stack should only contain a single element" + ); + let mut res = self.stack.pop().unwrap(); + res.sort_by_key(|range| range.range.start()); + // Check that ranges are sorted and disjoint + assert!(res + .iter() + .zip(res.iter().skip(1)) + .all(|(left, right)| left.range.end() <= right.range.start())); + res + } +} + fn highlight_format_specifier(kind: FormatSpecifier) -> Option { Some(match kind { FormatSpecifier::Open diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index 2192f509041..a341684fda2 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs @@ -1,4 +1,4 @@ -use ra_db::SourceDatabase; +use ra_db::{FileId, SourceDatabase}; use ra_ide_db::RootDatabase; use ra_syntax::{ algo, AstNode, NodeOrToken, SourceFile, diff --git a/docs/user/features.md b/docs/user/features.md deleted file mode 100644 index ff8cb2d6ef6..00000000000 --- a/docs/user/features.md +++ /dev/null @@ -1,96 +0,0 @@ -This document is an index of features that the rust-analyzer language server -provides. Shortcuts are for the default VS Code layout. If there's no shortcut, -you can use Ctrl+Shift+P to search for the corresponding action. - -### Commands ctrl+shift+p - - -#### Toggle inlay hints - -Toggle inlay hints view for the current workspace. -It is recommended to assign a shortcut for this command to quickly turn off -inlay hints when they prevent you from reading/writing the code. - -### Magic Completions - -In addition to usual reference completion, rust-analyzer provides some ✨magic✨ -completions as well: - -Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor -is placed at the appropriate position. Even though `if` is easy to type, you -still want to complete it, to get ` { }` for free! `return` is inserted with a -space or `;` depending on the return type of the function. - -When completing a function call, `()` are automatically inserted. If a function -takes arguments, the cursor is positioned inside the parenthesis. - -There are postfix completions, which can be triggered by typing something like -`foo().if`. The word after `.` determines postfix completion. Possible variants are: - -- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` -- `expr.match` -> `match expr {}` -- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` -- `expr.ref` -> `&expr` -- `expr.refm` -> `&mut expr` -- `expr.not` -> `!expr` -- `expr.dbg` -> `dbg!(expr)` - -There also snippet completions: - -#### Inside Expressions - -- `pd` -> `println!("{:?}")` -- `ppd` -> `println!("{:#?}")` - -#### Inside Modules - -- `tfn` -> `#[test] fn f(){}` -- `tmod` -> -```rust -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_fn() {} -} -``` - -### Code Highlighting - -Experimental feature to let rust-analyzer highlight Rust code instead of using the -default highlighter. - -#### Rainbow Highlighting - -Experimental feature that, given code highlighting using rust-analyzer is -active, will pick unique colors for identifiers. - -### Code hints - -Rust-analyzer has two types of hints to show the information about the code: - -* hover hints, appearing on hover on any element. - -These contain extended information on the hovered language item. - -* inlay hints, shown near the element hinted directly in the editor. - -Two types of inlay hints are displayed currently: - -* type hints, displaying the minimal information on the type of the expression (if the information is available) -* method chaining hints, type information for multi-line method chains -* parameter name hints, displaying the names of the parameters in the corresponding methods - -#### VS Code - -In VS Code, the following settings can be used to configure the inlay hints: - -* `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types. -* `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains. -* `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters. -* `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied. - -**Note:** VS Code does not have native support for inlay hints [yet](https://github.com/microsoft/vscode/issues/16221) and the hints are implemented using decorations. -This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: -[1](https://github.com/rust-analyzer/rust-analyzer/issues/1623), [2](https://github.com/rust-analyzer/rust-analyzer/issues/3453). diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index bf0a36d0183..a806e3ff119 100644 --- a/docs/user/generated_features.adoc +++ b/docs/user/generated_features.adoc @@ -1,3 +1,16 @@ +=== Expand Macro Recursively +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs] + + +Shows the full macro expansion of the macro at current cursor. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Expand macro recursively** +|=== + + === Extend Selection **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs] @@ -68,6 +81,38 @@ Navigates to the type of an identifier. |=== +=== Hover +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs] + + +Shows additional information, like type of an expression or documentation for definition when "focusing" code. +Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. + + +=== Inlay Hints +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs] + + +rust-analyzer shows additional information inline with the source code. +Editors usually render this using read-only virtual text snippets interspersed with code. + +rust-analyzer shows hits for + +* types of local variables +* names of function arguments +* types of chained expressions + +**Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations. +This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: +https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2]. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Toggle inlay hints* +|=== + + === Join Lines **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs] @@ -81,6 +126,52 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and |=== +=== Magic Completions +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs] + + +In addition to usual reference completion, rust-analyzer provides some ✨magic✨ +completions as well: + +Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor +is placed at the appropriate position. Even though `if` is easy to type, you +still want to complete it, to get ` { }` for free! `return` is inserted with a +space or `;` depending on the return type of the function. + +When completing a function call, `()` are automatically inserted. If a function +takes arguments, the cursor is positioned inside the parenthesis. + +There are postfix completions, which can be triggered by typing something like +`foo().if`. The word after `.` determines postfix completion. Possible variants are: + +- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` +- `expr.match` -> `match expr {}` +- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` +- `expr.ref` -> `&expr` +- `expr.refm` -> `&mut expr` +- `expr.not` -> `!expr` +- `expr.dbg` -> `dbg!(expr)` + +There also snippet completions: + +.Expressions +- `pd` -> `println!("{:?}")` +- `ppd` -> `println!("{:#?}")` + +.Items +- `tfn` -> `#[test] fn f(){}` +- `tmod` -> +```rust +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fn() {} +} +``` + + === Matching Brace **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs] @@ -135,6 +226,19 @@ to a shortcut! |=== +=== Semantic Syntax Highlighting +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs] + + +rust-analyzer highlights the code semantically. +For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. +rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. +It's up to the client to map those to specific colors. + +The general rule is that a reference to an entity gets colored the same way as the entity itself. +We also give special modifier for `mut` and `&mut` local variables. + + === Show Syntax Tree **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs] @@ -149,6 +253,45 @@ rust-analyzer itself. |=== +=== Status +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs] + + +Shows internal statistic about memory usage of rust-analyzer. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Status** +|=== + + +=== Structural Seach and Replace +**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs] + + +Search and replace with named wildcards that will match any expression. +The syntax for a structural search replace command is ` ==>> `. +A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement. +Available via the command `rust-analyzer.ssr`. + +```rust +// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] + +// BEFORE +String::from(foo(y + 5, z)) + +// AFTER +String::from((y + 5).foo(z)) +``` + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Structural Search Replace** +|=== + + === Workspace Symbol **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs] diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 8cfa41144e7..12def732710 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -8,6 +8,7 @@ :important-caption: :heavy_exclamation_mark: :caution-caption: :fire: :warning-caption: :warning: +:source-highlighter: rouge :experimental: // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index 170a3e88942..a0c2ffef91c 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -50,12 +50,12 @@ impl Feature { fn is_valid_feature_name(feature: &str) -> bool { 'word: for word in feature.split_whitespace() { - for &short in ["to"].iter() { + for &short in ["to", "and"].iter() { if word == short { continue 'word; } } - for &short in ["To"].iter() { + for &short in ["To", "And"].iter() { if word == short { return false; } -- cgit 1.4.1-3-g733a5 From 7e3ee77c83dc4e9af470491046206bf46d2a9a7e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 12:12:41 +0200 Subject: Tweak whitespace --- docs/user/generated_features.adoc | 19 ------------------- xtask/src/codegen/gen_feature_docs.rs | 4 ++-- 2 files changed, 2 insertions(+), 21 deletions(-) (limited to 'xtask/src/codegen/gen_feature_docs.rs') diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index a806e3ff119..803073d550f 100644 --- a/docs/user/generated_features.adoc +++ b/docs/user/generated_features.adoc @@ -1,7 +1,6 @@ === Expand Macro Recursively **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs] - Shows the full macro expansion of the macro at current cursor. |=== @@ -14,7 +13,6 @@ Shows the full macro expansion of the macro at current cursor. === Extend Selection **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs] - Extends the current selection to the encompassing syntactic construct (expression, statement, item, module, etc). It works with multiple cursors. @@ -28,7 +26,6 @@ Extends the current selection to the encompassing syntactic construct === File Structure **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs[structure.rs] - Provides a tree of the symbols defined in the file. Can be used to * fuzzy search symbol in a file (super useful) @@ -45,7 +42,6 @@ Provides a tree of the symbols defined in the file. Can be used to === Go to Definition **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs] - Navigates to the definition of an identifier. |=== @@ -58,7 +54,6 @@ Navigates to the definition of an identifier. === Go to Implementation **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs] - Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. |=== @@ -71,7 +66,6 @@ Navigates to the impl block of structs, enums or traits. Also implemented as a c === Go to Type Definition **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs] - Navigates to the type of an identifier. |=== @@ -84,7 +78,6 @@ Navigates to the type of an identifier. === Hover **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs] - Shows additional information, like type of an expression or documentation for definition when "focusing" code. Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. @@ -92,7 +85,6 @@ Focusing is usually hovering with a mouse, but can also be triggered with a shor === Inlay Hints **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs] - rust-analyzer shows additional information inline with the source code. Editors usually render this using read-only virtual text snippets interspersed with code. @@ -116,7 +108,6 @@ https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.co === Join Lines **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs] - Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. |=== @@ -129,7 +120,6 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and === Magic Completions **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs] - In addition to usual reference completion, rust-analyzer provides some ✨magic✨ completions as well: @@ -175,7 +165,6 @@ mod tests { === Matching Brace **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs] - If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, moves cursor to the matching brace. It uses the actual parser to determine braces, so it won't confuse generics with comparisons. @@ -190,7 +179,6 @@ braces, so it won't confuse generics with comparisons. === On Typing Assists **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs] - Some features trigger on typing certain characters: - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression @@ -201,7 +189,6 @@ Some features trigger on typing certain characters: === Parent Module **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs[parent_module.rs] - Navigates to the parent module of the current module. |=== @@ -214,7 +201,6 @@ Navigates to the parent module of the current module. === Run **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs] - Shows a popup suggesting to run a test/benchmark/binary **at the current cursor location**. Super useful for repeatedly running just a single test. Do bind this to a shortcut! @@ -229,7 +215,6 @@ to a shortcut! === Semantic Syntax Highlighting **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs] - rust-analyzer highlights the code semantically. For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. @@ -242,7 +227,6 @@ We also give special modifier for `mut` and `&mut` local variables. === Show Syntax Tree **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs] - Shows the parse tree of the current file. It exists mostly for debugging rust-analyzer itself. @@ -256,7 +240,6 @@ rust-analyzer itself. === Status **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs] - Shows internal statistic about memory usage of rust-analyzer. |=== @@ -269,7 +252,6 @@ Shows internal statistic about memory usage of rust-analyzer. === Structural Seach and Replace **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs] - Search and replace with named wildcards that will match any expression. The syntax for a structural search replace command is ` ==>> `. A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement. @@ -295,7 +277,6 @@ String::from((y + 5).foo(z)) === Workspace Symbol **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs] - Uses fuzzy-search to find types, modules and functions by name across your project and dependencies. This is **the** most useful feature, which improves code navigation tremendously. It mostly works on top of the built-in LSP diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index a0c2ffef91c..a6f339e7ba5 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -10,7 +10,7 @@ use crate::{ pub fn generate_feature_docs(mode: Mode) -> Result<()> { let features = Feature::collect()?; let contents = features.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); - + let contents = contents.trim().to_string() + "\n"; let dst = project_root().join("docs/user/generated_features.adoc"); codegen::update(&dst, &contents, mode)?; Ok(()) @@ -81,7 +81,7 @@ impl fmt::Display for Feature { name.to_str().unwrap(), )?; - writeln!(f, "\n{}", self.doc)?; + writeln!(f, "{}", self.doc)?; Ok(()) } } -- cgit 1.4.1-3-g733a5 From 13a996f3b68c175f6e6ad8d89081e45850dc5583 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 12:49:06 +0200 Subject: Force / slashes on windows --- xtask/src/codegen/gen_feature_docs.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'xtask/src/codegen/gen_feature_docs.rs') diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index a6f339e7ba5..dbe583e8e4f 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -70,14 +70,15 @@ fn is_valid_feature_name(feature: &str) -> bool { impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "=== {}", self.id)?; - let path = self.path.strip_prefix(&project_root()).unwrap(); + let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string(); + let path = path.replace('\\', "/"); let name = self.path.file_name().unwrap(); //FIXME: generate line number as well writeln!( f, "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]", - path.display(), + path, name.to_str().unwrap(), )?; -- cgit 1.4.1-3-g733a5