From 383247a9ae8202f20ce6f01d1429c1cd2a11d516 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 00:33:37 +0200 Subject: Generalize --- xtask/src/codegen.rs | 20 ++++++++++++++++++-- xtask/src/codegen/gen_assists_docs.rs | 10 +++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index b4907f4b2..2e8fd3494 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -61,8 +61,24 @@ fn extract_comment_blocks(text: &str) -> Vec> { do_extract_comment_blocks(text, false) } -fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec> { - do_extract_comment_blocks(text, true) +fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec { + assert!(tag.starts_with(char::is_uppercase)); + let tag = format!("{}:", tag); + let mut res = Vec::new(); + for mut block in do_extract_comment_blocks(text, true) { + let first = block.remove(0); + if first.starts_with(&tag) { + let id = first[tag.len()..].trim().to_string(); + let block = CommentBlock { id, contents: block }; + res.push(block); + } + } + res +} + +struct CommentBlock { + id: String, + contents: Vec, } fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec> { diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 20dcde820..6ebeb8aea 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs @@ -33,22 +33,18 @@ impl Assist { fn collect_file(acc: &mut Vec, path: &Path) -> Result<()> { let text = fs::read_to_string(path)?; - let comment_blocks = extract_comment_blocks_with_empty_lines(&text); + let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text); for block in comment_blocks { // FIXME: doesn't support blank lines yet, need to tweak // `extract_comment_blocks` for that. - let mut lines = block.iter(); - let first_line = lines.next().unwrap(); - if !first_line.starts_with("Assist: ") { - continue; - } - let id = first_line["Assist: ".len()..].to_string(); + let id = block.id; assert!( id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), "invalid assist id: {:?}", id ); + let mut lines = block.contents.iter(); let doc = take_until(lines.by_ref(), "```").trim().to_string(); assert!( -- cgit v1.2.3 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 --- crates/ra_ide/src/display/structure.rs | 13 ++ crates/ra_ide/src/extend_selection.rs | 10 ++ crates/ra_ide/src/goto_definition.rs | 9 ++ crates/ra_ide/src/goto_implementation.rs | 210 ++++++++++++++++++++++++++++++ crates/ra_ide/src/goto_type_definition.rs | 3 + crates/ra_ide/src/impls.rs | 201 ---------------------------- crates/ra_ide/src/lib.rs | 4 +- crates/ra_ide/src/typing.rs | 7 + crates/ra_ide_db/src/symbol_index.rs | 21 +++ docs/user/features.md | 52 -------- docs/user/generated_features.adoc | 98 ++++++++++++++ docs/user/readme.adoc | 5 +- xtask/src/codegen.rs | 5 +- xtask/src/codegen/gen_feature_docs.rs | 72 ++++++++++ xtask/src/main.rs | 1 + xtask/tests/tidy.rs | 7 + 16 files changed, 459 insertions(+), 259 deletions(-) create mode 100644 crates/ra_ide/src/goto_implementation.rs delete mode 100644 crates/ra_ide/src/impls.rs create mode 100644 docs/user/generated_features.adoc create mode 100644 xtask/src/codegen/gen_feature_docs.rs diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index 967eee5d2..3f59b89bb 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs @@ -18,6 +18,19 @@ pub struct StructureNode { pub deprecated: bool, } +// Feature: File Structure +// +// Provides a tree of the symbols defined in the file. Can be used to +// +// * fuzzy search symbol in a file (super useful) +// * draw breadcrumbs to describe the context around the cursor +// * draw outline of the file +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[Ctrl+Shift+O] +// |=== pub fn file_structure(file: &SourceFile) -> Vec { let mut res = Vec::new(); let mut stack = Vec::new(); diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index 554594a43..cee0a19e1 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs @@ -14,6 +14,16 @@ use ra_syntax::{ use crate::FileRange; +// Feature: Extend Selection +// +// Extends the current selection to the encompassing syntactic construct +// (expression, statement, item, module, etc). It works with multiple cursors. +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[Ctrl+Shift+→] +// |=== pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let sema = Semantics::new(db); let src = sema.parse(frange.file_id); diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 90e85d419..83ea5092c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -17,6 +17,15 @@ use crate::{ FilePosition, NavigationTarget, RangeInfo, }; +// Feature: Go To Definition +// +// Navigates to the definition of an identifier. +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[F12] +// |=== pub(crate) fn goto_definition( db: &RootDatabase, position: FilePosition, diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs new file mode 100644 index 000000000..a5a296d22 --- /dev/null +++ b/crates/ra_ide/src/goto_implementation.rs @@ -0,0 +1,210 @@ +//! FIXME: write short doc here + +use hir::{Crate, ImplDef, Semantics}; +use ra_ide_db::RootDatabase; +use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; + +use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; + +// Feature: Go To Implementation +// +// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[Ctrl+F12] +// |=== +pub(crate) fn goto_implementation( + db: &RootDatabase, + position: FilePosition, +) -> Option>> { + let sema = Semantics::new(db); + let source_file = sema.parse(position.file_id); + let syntax = source_file.syntax().clone(); + + let krate = sema.to_module_def(position.file_id)?.krate(); + + if let Some(nominal_def) = find_node_at_offset::(&syntax, position.offset) { + return Some(RangeInfo::new( + nominal_def.syntax().text_range(), + impls_for_def(&sema, &nominal_def, krate)?, + )); + } else if let Some(trait_def) = find_node_at_offset::(&syntax, position.offset) { + return Some(RangeInfo::new( + trait_def.syntax().text_range(), + impls_for_trait(&sema, &trait_def, krate)?, + )); + } + + None +} + +fn impls_for_def( + sema: &Semantics, + node: &ast::NominalDef, + krate: Crate, +) -> Option> { + let ty = match node { + ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), + ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), + ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), + }; + + let impls = ImplDef::all_in_crate(sema.db, krate); + + Some( + impls + .into_iter() + .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) + .map(|imp| imp.to_nav(sema.db)) + .collect(), + ) +} + +fn impls_for_trait( + sema: &Semantics, + node: &ast::TraitDef, + krate: Crate, +) -> Option> { + let tr = sema.to_def(node)?; + + let impls = ImplDef::for_trait(sema.db, krate, tr); + + Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) +} + +#[cfg(test)] +mod tests { + use crate::mock_analysis::analysis_and_position; + + fn check_goto(fixture: &str, expected: &[&str]) { + let (analysis, pos) = analysis_and_position(fixture); + + let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; + assert_eq!(navs.len(), expected.len()); + navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); + navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); + } + + #[test] + fn goto_implementation_works() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + impl Foo {} + ", + &["impl IMPL_DEF FileId(1) 12..23"], + ); + } + + #[test] + fn goto_implementation_works_multiple_blocks() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + impl Foo {} + impl Foo {} + ", + &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], + ); + } + + #[test] + fn goto_implementation_works_multiple_mods() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + mod a { + impl super::Foo {} + } + mod b { + impl super::Foo {} + } + ", + &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], + ); + } + + #[test] + fn goto_implementation_works_multiple_files() { + check_goto( + " + //- /lib.rs + struct Foo<|>; + mod a; + mod b; + //- /a.rs + impl crate::Foo {} + //- /b.rs + impl crate::Foo {} + ", + &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], + ); + } + + #[test] + fn goto_implementation_for_trait() { + check_goto( + " + //- /lib.rs + trait T<|> {} + struct Foo; + impl T for Foo {} + ", + &["impl IMPL_DEF FileId(1) 23..40"], + ); + } + + #[test] + fn goto_implementation_for_trait_multiple_files() { + check_goto( + " + //- /lib.rs + trait T<|> {}; + struct Foo; + mod a; + mod b; + //- /a.rs + impl crate::T for crate::Foo {} + //- /b.rs + impl crate::T for crate::Foo {} + ", + &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], + ); + } + + #[test] + fn goto_implementation_all_impls() { + check_goto( + " + //- /lib.rs + trait T {} + struct Foo<|>; + impl Foo {} + impl T for Foo {} + impl T for &Foo {} + ", + &[ + "impl IMPL_DEF FileId(1) 23..34", + "impl IMPL_DEF FileId(1) 35..52", + "impl IMPL_DEF FileId(1) 53..71", + ], + ); + } + + #[test] + fn goto_implementation_to_builtin_derive() { + check_goto( + " + //- /lib.rs + #[derive(Copy)] + struct Foo<|>; + ", + &["impl IMPL_DEF FileId(1) 0..15"], + ); + } +} diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index a84637489..eeadfa9ee 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs @@ -5,6 +5,9 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; +// Feature: Go To Type Definition +// +// Navigates to the type of an identifier. pub(crate) fn goto_type_definition( db: &RootDatabase, position: FilePosition, diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/impls.rs deleted file mode 100644 index ea2225f70..000000000 --- a/crates/ra_ide/src/impls.rs +++ /dev/null @@ -1,201 +0,0 @@ -//! FIXME: write short doc here - -use hir::{Crate, ImplDef, Semantics}; -use ra_ide_db::RootDatabase; -use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; - -use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; - -pub(crate) fn goto_implementation( - db: &RootDatabase, - position: FilePosition, -) -> Option>> { - let sema = Semantics::new(db); - let source_file = sema.parse(position.file_id); - let syntax = source_file.syntax().clone(); - - let krate = sema.to_module_def(position.file_id)?.krate(); - - if let Some(nominal_def) = find_node_at_offset::(&syntax, position.offset) { - return Some(RangeInfo::new( - nominal_def.syntax().text_range(), - impls_for_def(&sema, &nominal_def, krate)?, - )); - } else if let Some(trait_def) = find_node_at_offset::(&syntax, position.offset) { - return Some(RangeInfo::new( - trait_def.syntax().text_range(), - impls_for_trait(&sema, &trait_def, krate)?, - )); - } - - None -} - -fn impls_for_def( - sema: &Semantics, - node: &ast::NominalDef, - krate: Crate, -) -> Option> { - let ty = match node { - ast::NominalDef::StructDef(def) => sema.to_def(def)?.ty(sema.db), - ast::NominalDef::EnumDef(def) => sema.to_def(def)?.ty(sema.db), - ast::NominalDef::UnionDef(def) => sema.to_def(def)?.ty(sema.db), - }; - - let impls = ImplDef::all_in_crate(sema.db, krate); - - Some( - impls - .into_iter() - .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) - .map(|imp| imp.to_nav(sema.db)) - .collect(), - ) -} - -fn impls_for_trait( - sema: &Semantics, - node: &ast::TraitDef, - krate: Crate, -) -> Option> { - let tr = sema.to_def(node)?; - - let impls = ImplDef::for_trait(sema.db, krate, tr); - - Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) -} - -#[cfg(test)] -mod tests { - use crate::mock_analysis::analysis_and_position; - - fn check_goto(fixture: &str, expected: &[&str]) { - let (analysis, pos) = analysis_and_position(fixture); - - let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; - assert_eq!(navs.len(), expected.len()); - navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); - navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); - } - - #[test] - fn goto_implementation_works() { - check_goto( - " - //- /lib.rs - struct Foo<|>; - impl Foo {} - ", - &["impl IMPL_DEF FileId(1) 12..23"], - ); - } - - #[test] - fn goto_implementation_works_multiple_blocks() { - check_goto( - " - //- /lib.rs - struct Foo<|>; - impl Foo {} - impl Foo {} - ", - &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], - ); - } - - #[test] - fn goto_implementation_works_multiple_mods() { - check_goto( - " - //- /lib.rs - struct Foo<|>; - mod a { - impl super::Foo {} - } - mod b { - impl super::Foo {} - } - ", - &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], - ); - } - - #[test] - fn goto_implementation_works_multiple_files() { - check_goto( - " - //- /lib.rs - struct Foo<|>; - mod a; - mod b; - //- /a.rs - impl crate::Foo {} - //- /b.rs - impl crate::Foo {} - ", - &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], - ); - } - - #[test] - fn goto_implementation_for_trait() { - check_goto( - " - //- /lib.rs - trait T<|> {} - struct Foo; - impl T for Foo {} - ", - &["impl IMPL_DEF FileId(1) 23..40"], - ); - } - - #[test] - fn goto_implementation_for_trait_multiple_files() { - check_goto( - " - //- /lib.rs - trait T<|> {}; - struct Foo; - mod a; - mod b; - //- /a.rs - impl crate::T for crate::Foo {} - //- /b.rs - impl crate::T for crate::Foo {} - ", - &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], - ); - } - - #[test] - fn goto_implementation_all_impls() { - check_goto( - " - //- /lib.rs - trait T {} - struct Foo<|>; - impl Foo {} - impl T for Foo {} - impl T for &Foo {} - ", - &[ - "impl IMPL_DEF FileId(1) 23..34", - "impl IMPL_DEF FileId(1) 35..52", - "impl IMPL_DEF FileId(1) 53..71", - ], - ); - } - - #[test] - fn goto_implementation_to_builtin_derive() { - check_goto( - " - //- /lib.rs - #[derive(Copy)] - struct Foo<|>; - ", - &["impl IMPL_DEF FileId(1) 0..15"], - ); - } -} diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d983cd910..12d5716e8 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -23,6 +23,7 @@ mod completion; mod runnables; mod goto_definition; mod goto_type_definition; +mod goto_implementation; mod extend_selection; mod hover; mod call_hierarchy; @@ -30,7 +31,6 @@ mod call_info; mod syntax_highlighting; mod parent_module; mod references; -mod impls; mod diagnostics; mod syntax_tree; mod folding_ranges; @@ -373,7 +373,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancelable>>> { - self.with_db(|db| impls::goto_implementation(db, position)) + self.with_db(|db| goto_implementation::goto_implementation(db, position)) } /// Returns the type definitions for the symbol at `position`. diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 39bb3b357..67e2c33a0 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs @@ -32,6 +32,13 @@ pub(crate) use on_enter::on_enter; pub(crate) const TRIGGER_CHARS: &str = ".=>"; +// Feature: On Typing Assists +// +// Some features trigger on typing certain characters: +// +// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression +// - Enter inside comments automatically inserts `///` +// - typing `.` in a chain method call auto-indents pub(crate) fn on_char_typed( db: &RootDatabase, position: FilePosition, diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs index 95be11134..acc31fe3b 100644 --- a/crates/ra_ide_db/src/symbol_index.rs +++ b/crates/ra_ide_db/src/symbol_index.rs @@ -110,6 +110,27 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc Arc::new(SymbolIndex::new(symbols)) } +// Feature: Workspace Symbol +// +// 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 +// functionality, however `#` and `*` symbols can be used to narrow down the +// search. Specifically, +// +// - `Foo` searches for `Foo` type in the current workspace +// - `foo#` searches for `foo` function in the current workspace +// - `Foo*` searches for `Foo` type among dependencies, including `stdlib` +// - `foo#*` searches for `foo` function among dependencies +// +// That is, `#` switches from "types" to all symbols, `*` switches from the current +// workspace to dependencies. +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[Ctrl+T] +// |=== pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap(salsa::Snapshot); diff --git a/docs/user/features.md b/docs/user/features.md index 12ecdec13..4d9402252 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -2,58 +2,6 @@ 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. -### Workspace Symbol ctrl+t - -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 -functionality, however `#` and `*` symbols can be used to narrow down the -search. Specifically, - -- `Foo` searches for `Foo` type in the current workspace -- `foo#` searches for `foo` function in the current workspace -- `Foo*` searches for `Foo` type among dependencies, including `stdlib` -- `foo#*` searches for `foo` function among dependencies - -That is, `#` switches from "types" to all symbols, `*` switches from the current -workspace to dependencies. - -### Document Symbol ctrl+shift+o - -Provides a tree of the symbols defined in the file. Can be used to - -* fuzzy search symbol in a file (super useful) -* draw breadcrumbs to describe the context around the cursor -* draw outline of the file - -### On Typing Assists - -Some features trigger on typing certain characters: - -- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression -- Enter inside comments automatically inserts `///` -- typing `.` in a chain method call auto-indents - -### Extend Selection - -Extends the current selection to the encompassing syntactic construct -(expression, statement, item, module, etc). It works with multiple cursors. This -is a relatively new feature of LSP: -https://github.com/Microsoft/language-server-protocol/issues/613, check your -editor's LSP library to see if this feature is supported. - -### Go to Definition - -Navigates to the definition of an identifier. - -### Go to Implementation - -Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. - -### Go to Type Defintion - -Navigates to the type of an identifier. - ### Commands ctrl+shift+p #### Run diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc new file mode 100644 index 000000000..e1eb5d88a --- /dev/null +++ b/docs/user/generated_features.adoc @@ -0,0 +1,98 @@ +=== 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. + +|=== +| Editor | Shortcut + +| VS Code | kbd:[Ctrl+Shift+→] +|=== + + +=== 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) +* draw breadcrumbs to describe the context around the cursor +* draw outline of the file + +|=== +| Editor | Shortcut + +| VS Code | kbd:[Ctrl+Shift+O] +|=== + + +=== 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. + +|=== +| Editor | Shortcut + +| VS Code | kbd:[F12] +|=== + + +=== 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. + +|=== +| Editor | Shortcut + +| VS Code | kbd:[Ctrl+F12] +|=== + + +=== 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. + + +=== 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 +- Enter inside comments automatically inserts `///` +- typing `.` in a chain method call auto-indents + + +=== 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 +functionality, however `#` and `*` symbols can be used to narrow down the +search. Specifically, + +- `Foo` searches for `Foo` type in the current workspace +- `foo#` searches for `foo` function in the current workspace +- `Foo*` searches for `Foo` type among dependencies, including `stdlib` +- `foo#*` searches for `foo` function among dependencies + +That is, `#` switches from "types" to all symbols, `*` switches from the current +workspace to dependencies. + +|=== +| Editor | Shortcut + +| VS Code | kbd:[Ctrl+T] +|=== diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 64bd0feb1..7b159bfc6 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: +:experimental: // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository @@ -268,6 +269,6 @@ Gnome Builder currently has support for RLS, and there's no way to configure the 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). 2. Enable the Rust Builder plugin. -== Usage +== Features -See https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md[features.md]. +include::./generated_features.adoc[] diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 2e8fd3494..3e2a66979 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -8,14 +8,15 @@ mod gen_syntax; mod gen_parser_tests; mod gen_assists_docs; +mod gen_feature_docs; use std::{mem, path::Path}; use crate::{not_bash::fs2, Result}; pub use self::{ - gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, - gen_syntax::generate_syntax, + gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, + gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, }; const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs new file mode 100644 index 000000000..583185648 --- /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(()) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index dff3ce4a1..9d7cdd114 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -75,6 +75,7 @@ FLAGS: codegen::generate_syntax(Mode::Overwrite)?; codegen::generate_parser_tests(Mode::Overwrite)?; codegen::generate_assists_docs(Mode::Overwrite)?; + codegen::generate_feature_docs(Mode::Overwrite)?; Ok(()) } "format" => { diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 2e9fcf07c..06ff45d99 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -30,6 +30,13 @@ fn generated_assists_are_fresh() { } } +#[test] +fn generated_features_are_fresh() { + if let Err(error) = codegen::generate_feature_docs(Mode::Verify) { + panic!("{}. Please update features by running `cargo xtask codegen`", error); + } +} + #[test] fn check_code_formatting() { if let Err(error) = run_rustfmt(Mode::Verify) { -- cgit v1.2.3 From 5dd65495699fb3d9c6e4b3c4f27b78a64d23e567 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:28:53 +0200 Subject: Simplify --- xtask/src/codegen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 3e2a66979..f47d54125 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -41,7 +41,7 @@ pub enum Mode { /// With verify = false, fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { match fs2::read_to_string(path) { - Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { + Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { return Ok(()); } _ => (), -- cgit v1.2.3 From c1161718792a1841841a51bc8450d37c4f1ff535 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:29:15 +0200 Subject: Ignore HTML --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f835edef0..dab51647d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ crates/*/target *.log *.iml .vscode/settings.json -cargo-timing*.html +*.html -- cgit v1.2.3 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(-) diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 83ea5092c..daeeac76f 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 a5a296d22..622a094e6 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 eeadfa9ee..e74a502ec 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 6e7e47199..4bf2678e1 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 4d9402252..df8e73a20 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 e1eb5d88a..1f6fcc974 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 583185648..170a3e889 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 v1.2.3 From 8915183d7da07a4b295e5e93a889dea4c15024a0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 09:59:38 +0200 Subject: Don't require module docs for Features and Assists --- .../src/handlers/add_from_impl_for_enum.rs | 4 +- crates/ra_assists/src/tests/generated.rs | 19 ++++++++ crates/ra_ide/src/display/structure.rs | 6 +-- crates/ra_ide/src/extend_selection.rs | 2 - crates/ra_ide/src/goto_definition.rs | 2 - crates/ra_ide/src/goto_implementation.rs | 2 - crates/ra_ide/src/goto_type_definition.rs | 2 - crates/ra_ide/src/join_lines.rs | 11 ++++- crates/ra_ide/src/matching_brace.rs | 13 ++++- crates/ra_ide/src/parent_module.rs | 12 ++++- crates/ra_ide/src/runnables.rs | 2 - crates/ra_ide/src/syntax_tree.rs | 14 ++++-- docs/user/assists.md | 18 +++++++ docs/user/features.md | 16 ------- docs/user/generated_features.adoc | 55 ++++++++++++++++++++++ xtask/tests/tidy.rs | 7 ++- 16 files changed, 140 insertions(+), 45 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 6a675e812..776bddf91 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -4,9 +4,9 @@ use test_utils::mark; use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; -// Assist add_from_impl_for_enum +// Assist: add_from_impl_for_enum // -// Adds a From impl for an enum variant with one tuple field +// Adds a From impl for an enum variant with one tuple field. // // ``` // enum A { <|>One(u32) } diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 250e56a69..4e0536828 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -58,6 +58,25 @@ fn main() { ) } +#[test] +fn doctest_add_from_impl_for_enum() { + check_doc_test( + "add_from_impl_for_enum", + r#####" +enum A { <|>One(u32) } +"#####, + r#####" +enum A { One(u32) } + +impl From for A { + fn from(v: u32) -> Self { + A::One(v) + } +} +"#####, + ) +} + #[test] fn doctest_add_function() { check_doc_test( diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs index 3f59b89bb..aad5a8e4d 100644 --- a/crates/ra_ide/src/display/structure.rs +++ b/crates/ra_ide/src/display/structure.rs @@ -1,10 +1,6 @@ -//! FIXME: write short doc here - -use crate::TextRange; - use ra_syntax::{ ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, - match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, + match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent, }; #[derive(Debug, Clone)] diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs index cee0a19e1..a4bc93cdb 100644 --- a/crates/ra_ide/src/extend_selection.rs +++ b/crates/ra_ide/src/extend_selection.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use std::iter::successors; use hir::Semantics; diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index daeeac76f..a6c86e99c 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::Semantics; use ra_ide_db::{ defs::{classify_name, classify_name_ref}, diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs index 622a094e6..0cec0657e 100644 --- a/crates/ra_ide/src/goto_implementation.rs +++ b/crates/ra_ide/src/goto_implementation.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::{Crate, ImplDef, Semantics}; use ra_ide_db::RootDatabase; use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs index e74a502ec..91a3097fb 100644 --- a/crates/ra_ide/src/goto_type_definition.rs +++ b/crates/ra_ide/src/goto_type_definition.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use ra_ide_db::RootDatabase; use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index af1ade8a1..5036c1fb0 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use itertools::Itertools; use ra_fmt::{compute_ws, extract_trivial_expression}; use ra_syntax::{ @@ -11,6 +9,15 @@ use ra_syntax::{ }; use ra_text_edit::{TextEdit, TextEditBuilder}; +// Feature: Join Lines +// +// Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Join lines** +// |=== pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { let range = if range.is_empty() { let syntax = file.syntax(); diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs index b85348706..407a9636d 100644 --- a/crates/ra_ide/src/matching_brace.rs +++ b/crates/ra_ide/src/matching_brace.rs @@ -1,7 +1,16 @@ -//! FIXME: write short doc here - use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; +// Feature: Matching Brace +// +// 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. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Find matching brace** +// |=== pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option { const BRACES: &[SyntaxKind] = &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index a083fb1eb..fa1535da5 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::Semantics; use ra_db::{CrateId, FileId, FilePosition}; use ra_ide_db::RootDatabase; @@ -11,6 +9,16 @@ use test_utils::mark; use crate::NavigationTarget; +// Feature: Parent Module +// +// Navigates to the parent module of the current module. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Locate parent module** +// |=== + /// This returns `Vec` because a module may be included from several places. We /// don't handle this case yet though, so the Vec has length at most one. pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec { diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 4bf2678e1..286d45eee 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; use itertools::Itertools; use ra_ide_db::RootDatabase; diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs index 86c70ff83..2192f5090 100644 --- a/crates/ra_ide/src/syntax_tree.rs +++ b/crates/ra_ide/src/syntax_tree.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; use ra_syntax::{ @@ -8,8 +6,16 @@ use ra_syntax::{ SyntaxToken, TextRange, TextSize, }; -pub use ra_db::FileId; - +// Feature: Show Syntax Tree +// +// Shows the parse tree of the current file. It exists mostly for debugging +// rust-analyzer itself. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Show Syntax Tree** +// |=== pub(crate) fn syntax_tree( db: &RootDatabase, file_id: FileId, diff --git a/docs/user/assists.md b/docs/user/assists.md index 4ad7ea59d..b5d813b54 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -56,6 +56,24 @@ fn main() { } ``` +## `add_from_impl_for_enum` + +Adds a From impl for an enum variant with one tuple field. + +```rust +// BEFORE +enum A { ┃One(u32) } + +// AFTER +enum A { One(u32) } + +impl From for A { + fn from(v: u32) -> Self { + A::One(v) + } +} +``` + ## `add_function` Adds a stub function with a signature matching the function under the cursor. diff --git a/docs/user/features.md b/docs/user/features.md index df8e73a20..ba7ca15a6 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -4,24 +4,8 @@ you can use Ctrl+Shift+P to search for the corresponding action. ### Commands ctrl+shift+p -#### Parent Module -Navigates to the parent module of the current module. -#### Matching Brace - -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. - -#### Join Lines - -Join selected lines into one, smartly fixing up whitespace and trailing commas. - -#### Show Syntax Tree - -Shows the parse tree of the current file. It exists mostly for debugging -rust-analyzer itself. #### Expand Macro Recursively diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index 1f6fcc974..bf0a36d01 100644 --- a/docs/user/generated_features.adoc +++ b/docs/user/generated_features.adoc @@ -68,6 +68,34 @@ Navigates to the type of an identifier. |=== +=== 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. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Join lines** +|=== + + +=== 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. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Find matching brace** +|=== + + === On Typing Assists **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs] @@ -79,6 +107,19 @@ Some features trigger on typing certain characters: - typing `.` in a chain method call auto-indents +=== 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. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Locate parent module** +|=== + + === Run **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs] @@ -94,6 +135,20 @@ to a shortcut! |=== +=== 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. + +|=== +| Editor | Action Name + +| VS Code | **Rust Analyzer: Show Syntax Tree** +|=== + + === 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/tests/tidy.rs b/xtask/tests/tidy.rs index 06ff45d99..4ac5d929f 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -102,7 +102,7 @@ impl TidyDocs { fn visit(&mut self, path: &Path, text: &str) { // Test hopefully don't really need comments, and for assists we already // have special comments which are source of doc tests and user docs. - if is_exclude_dir(path, &["tests", "test_data", "handlers"]) { + if is_exclude_dir(path, &["tests", "test_data"]) { return; } @@ -117,9 +117,12 @@ impl TidyDocs { if first_line.starts_with("//!") { if first_line.contains("FIXME") { - self.contains_fixme.push(path.to_path_buf()) + self.contains_fixme.push(path.to_path_buf()); } } else { + if text.contains("// Feature:") || text.contains("// Assist:") { + return; + } self.missing_docs.push(path.display().to_string()); } -- cgit v1.2.3 From b795a07320e13bcbedb6435bcfddb3ecd0ed2bde Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 31 May 2020 10:14:36 +0200 Subject: Doc more features --- crates/ra_ide/src/expand_macro.rs | 11 +++++++-- crates/ra_ide/src/ssr.rs | 24 ++++++++++++++++++-- crates/ra_ide/src/status.rs | 11 +++++++-- docs/user/features.md | 48 --------------------------------------- docs/user/readme.adoc | 7 ++++++ 5 files changed, 47 insertions(+), 54 deletions(-) diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs index f536ba3e7..54a47aac0 100644 --- a/crates/ra_ide/src/expand_macro.rs +++ b/crates/ra_ide/src/expand_macro.rs @@ -1,5 +1,3 @@ -//! This modules implements "expand macro" functionality in the IDE - use hir::Semantics; use ra_ide_db::RootDatabase; use ra_syntax::{ @@ -14,6 +12,15 @@ pub struct ExpandedMacro { pub expansion: String, } +// Feature: Expand Macro Recursively +// +// Shows the full macro expansion of the macro at current cursor. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Expand macro recursively** +// |=== pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let file = sema.parse(position.file_id); diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 130d3b4c3..93e9aee1d 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs @@ -1,5 +1,3 @@ -//! structural search replace - use std::{collections::HashMap, iter::once, str::FromStr}; use ra_db::{SourceDatabase, SourceDatabaseExt}; @@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError { impl std::error::Error for SsrError {} +// Feature: Structural Seach and Replace +// +// 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** +// |=== pub fn parse_search_replace( query: &str, parse_only: bool, diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs index 30eb5c995..5b7992920 100644 --- a/crates/ra_ide/src/status.rs +++ b/crates/ra_ide/src/status.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use std::{fmt, iter::FromIterator, sync::Arc}; use hir::MacroFile; @@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { db.query(hir::db::ParseMacroQuery).entries::() } +// Feature: Status +// +// Shows internal statistic about memory usage of rust-analyzer. +// +// |=== +// | Editor | Action Name +// +// | VS Code | **Rust Analyzer: Status** +// |=== pub(crate) fn status(db: &RootDatabase) -> String { let files_stats = db.query(FileTextQuery).entries::(); let syntax_tree_stats = syntax_tree_stats(db); diff --git a/docs/user/features.md b/docs/user/features.md index ba7ca15a6..ff8cb2d6e 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -5,60 +5,12 @@ you can use Ctrl+Shift+P to search for the corresponding action. ### Commands ctrl+shift+p - - -#### Expand Macro Recursively - -Shows the full macro expansion of the macro at current cursor. - -#### Status - -Shows internal statistic about memory usage of rust-analyzer. - -#### Show RA Version - -Show current rust-analyzer version. - #### 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. -#### Run Garbage Collection - -Manually triggers GC. - -#### Start Cargo Watch - -Start `cargo watch` for live error highlighting. Will prompt to install if it's not already installed. - -#### Stop Cargo Watch - -Stop `cargo watch`. - -#### Structural Seach and Replace - -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)) -``` - -### Assists (Code Actions) - -Assists, or code actions, are small local refactorings, available in a particular context. -They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. - -See [assists.md](./assists.md) for the list of available assists. - ### Magic Completions In addition to usual reference completion, rust-analyzer provides some ✨magic✨ diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index 7b159bfc6..8cfa41144 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc @@ -272,3 +272,10 @@ Gnome Builder currently has support for RLS, and there's no way to configure the == Features include::./generated_features.adoc[] + +== Assists (Code Actions) + +Assists, or code actions, are small local refactorings, available in a particular context. +They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. + +See [assists.md](./assists.md) for the list of available assists. -- cgit v1.2.3 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 diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 191300704..d890b69d2 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 02e660ca8..59b58bf98 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 3e721dcca..d96cb5596 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 b391f903a..75bd3c96b 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 8a995d779..3ab1f0a21 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 2192f5090..a341684fd 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 ff8cb2d6e..000000000 --- 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 bf0a36d01..a806e3ff1 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 8cfa41144..12def7327 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 170a3e889..a0c2ffef9 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 v1.2.3 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(-) diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc index a806e3ff1..803073d55 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 a0c2ffef9..a6f339e7b 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 v1.2.3 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(-) diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index a6f339e7b..dbe583e8e 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 v1.2.3