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 +++ 9 files changed, 275 insertions(+), 203 deletions(-) create mode 100644 crates/ra_ide/src/goto_implementation.rs delete mode 100644 crates/ra_ide/src/impls.rs (limited to 'crates') 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); -- cgit v1.2.3