aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/completion.rs47
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs6
-rw-r--r--crates/ra_ide/src/display/structure.rs19
-rw-r--r--crates/ra_ide/src/expand_macro.rs11
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/goto_definition.rs11
-rw-r--r--crates/ra_ide/src/goto_implementation.rs (renamed from crates/ra_ide/src/impls.rs)11
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs11
-rw-r--r--crates/ra_ide/src/hover.rs116
-rw-r--r--crates/ra_ide/src/inlay_hints.rs22
-rw-r--r--crates/ra_ide/src/join_lines.rs11
-rw-r--r--crates/ra_ide/src/lib.rs4
-rw-r--r--crates/ra_ide/src/matching_brace.rs13
-rw-r--r--crates/ra_ide/src/parent_module.rs12
-rw-r--r--crates/ra_ide/src/references.rs27
-rw-r--r--crates/ra_ide/src/runnables.rs13
-rw-r--r--crates/ra_ide/src/ssr.rs24
-rw-r--r--crates/ra_ide/src/status.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs161
-rw-r--r--crates/ra_ide/src/syntax_tree.rs16
-rw-r--r--crates/ra_ide/src/typing.rs7
21 files changed, 390 insertions, 175 deletions
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 @@
1//! FIXME: write short doc here
2
3mod completion_config; 1mod completion_config;
4mod completion_item; 2mod completion_item;
5mod completion_context; 3mod completion_context;
@@ -35,6 +33,51 @@ pub use crate::completion::{
35 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, 33 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
36}; 34};
37 35
36//FIXME: split the following feature into fine-grained features.
37
38// Feature: Magic Completions
39//
40// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
41// completions as well:
42//
43// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
44// is placed at the appropriate position. Even though `if` is easy to type, you
45// still want to complete it, to get ` { }` for free! `return` is inserted with a
46// space or `;` depending on the return type of the function.
47//
48// When completing a function call, `()` are automatically inserted. If a function
49// takes arguments, the cursor is positioned inside the parenthesis.
50//
51// There are postfix completions, which can be triggered by typing something like
52// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
53//
54// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
55// - `expr.match` -> `match expr {}`
56// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
57// - `expr.ref` -> `&expr`
58// - `expr.refm` -> `&mut expr`
59// - `expr.not` -> `!expr`
60// - `expr.dbg` -> `dbg!(expr)`
61//
62// There also snippet completions:
63//
64// .Expressions
65// - `pd` -> `println!("{:?}")`
66// - `ppd` -> `println!("{:#?}")`
67//
68// .Items
69// - `tfn` -> `#[test] fn f(){}`
70// - `tmod` ->
71// ```rust
72// #[cfg(test)]
73// mod tests {
74// use super::*;
75//
76// #[test]
77// fn test_fn() {}
78// }
79// ```
80
38/// Main entry point for completion. We run completion as a two-phase process. 81/// Main entry point for completion. We run completion as a two-phase process.
39/// 82///
40/// First, we look at the position and collect a so-called `CompletionContext. 83/// 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 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2use ra_assists::utils::TryEnum;
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, AstNode}, 4 ast::{self, AstNode},
5 TextRange, TextSize, 5 TextRange, TextSize,
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
10use crate::{ 9use crate::{
11 completion::{ 10 completion::{
12 completion_context::CompletionContext, 11 completion_context::CompletionContext,
@@ -14,7 +13,8 @@ use crate::{
14 }, 13 },
15 CompletionItem, 14 CompletionItem,
16}; 15};
17use ra_assists::utils::TryEnum; 16
17use super::completion_config::SnippetCap;
18 18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
20 if !ctx.config.enable_postfix_completions { 20 if !ctx.config.enable_postfix_completions {
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index 967eee5d2..aad5a8e4d 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -1,10 +1,6 @@
1//! FIXME: write short doc here
2
3use crate::TextRange;
4
5use ra_syntax::{ 1use ra_syntax::{
6 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, 2 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
7 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, 3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
8}; 4};
9 5
10#[derive(Debug, Clone)] 6#[derive(Debug, Clone)]
@@ -18,6 +14,19 @@ pub struct StructureNode {
18 pub deprecated: bool, 14 pub deprecated: bool,
19} 15}
20 16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
21pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> { 30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
22 let mut res = Vec::new(); 31 let mut res = Vec::new();
23 let mut stack = Vec::new(); 32 let mut stack = Vec::new();
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 @@
1//! This modules implements "expand macro" functionality in the IDE
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use ra_syntax::{
@@ -14,6 +12,15 @@ pub struct ExpandedMacro {
14 pub expansion: String, 12 pub expansion: String,
15} 13}
16 14
15// Feature: Expand Macro Recursively
16//
17// Shows the full macro expansion of the macro at current cursor.
18//
19// |===
20// | Editor | Action Name
21//
22// | VS Code | **Rust Analyzer: Expand macro recursively**
23// |===
17pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 24pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
18 let sema = Semantics::new(db); 25 let sema = Semantics::new(db);
19 let file = sema.parse(position.file_id); 26 let file = sema.parse(position.file_id);
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 554594a43..a4bc93cdb 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use std::iter::successors; 1use std::iter::successors;
4 2
5use hir::Semantics; 3use hir::Semantics;
@@ -14,6 +12,16 @@ use ra_syntax::{
14 12
15use crate::FileRange; 13use crate::FileRange;
16 14
15// Feature: Extend Selection
16//
17// Extends the current selection to the encompassing syntactic construct
18// (expression, statement, item, module, etc). It works with multiple cursors.
19//
20// |===
21// | Editor | Shortcut
22//
23// | VS Code | kbd:[Ctrl+Shift+→]
24// |===
17pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 25pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
18 let sema = Semantics::new(db); 26 let sema = Semantics::new(db);
19 let src = sema.parse(frange.file_id); 27 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..a6c86e99c 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::{ 2use ra_ide_db::{
5 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref},
@@ -17,6 +15,15 @@ use crate::{
17 FilePosition, NavigationTarget, RangeInfo, 15 FilePosition, NavigationTarget, RangeInfo,
18}; 16};
19 17
18// Feature: Go to Definition
19//
20// Navigates to the definition of an identifier.
21//
22// |===
23// | Editor | Shortcut
24//
25// | VS Code | kbd:[F12]
26// |===
20pub(crate) fn goto_definition( 27pub(crate) fn goto_definition(
21 db: &RootDatabase, 28 db: &RootDatabase,
22 position: FilePosition, 29 position: FilePosition,
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/goto_implementation.rs
index ea2225f70..0cec0657e 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -1,11 +1,18 @@
1//! FIXME: write short doc here
2
3use hir::{Crate, ImplDef, Semantics}; 1use hir::{Crate, ImplDef, Semantics};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 3use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6 4
7use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
8 6
7// Feature: Go to Implementation
8//
9// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
10//
11// |===
12// | Editor | Shortcut
13//
14// | VS Code | kbd:[Ctrl+F12]
15// |===
9pub(crate) fn goto_implementation( 16pub(crate) fn goto_implementation(
10 db: &RootDatabase, 17 db: &RootDatabase,
11 position: FilePosition, 18 position: FilePosition,
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index a84637489..91a3097fb 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,10 +1,17 @@
1//! FIXME: write short doc here
2
3use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
4use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
5 3
6use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
7 5
6// Feature: Go to Type Definition
7//
8// Navigates to the type of an identifier.
9//
10// |===
11// | Editor | Action Name
12//
13// | VS Code | **Go to Type Definition*
14// |===
8pub(crate) fn goto_type_definition( 15pub(crate) fn goto_type_definition(
9 db: &RootDatabase, 16 db: &RootDatabase,
10 position: FilePosition, 17 position: FilePosition,
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 @@
1//! Logic for computing info that is displayed when the user hovers over any 1use std::iter::once;
2//! source code items (e.g. function call, struct field, variable symbol...)
3 2
4use hir::{ 3use hir::{
5 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
6 ModuleSource, Semantics, 5 ModuleSource, Semantics,
7}; 6};
7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
9use ra_ide_db::{ 9use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
@@ -21,8 +21,6 @@ use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
22 FilePosition, RangeInfo, 22 FilePosition, RangeInfo,
23}; 23};
24use itertools::Itertools;
25use std::iter::once;
26 24
27/// Contains the results when hovering over an item 25/// Contains the results when hovering over an item
28#[derive(Debug, Default)] 26#[derive(Debug, Default)]
@@ -62,6 +60,63 @@ impl HoverResult {
62 } 60 }
63} 61}
64 62
63// Feature: Hover
64//
65// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
66// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
67pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
68 let sema = Semantics::new(db);
69 let file = sema.parse(position.file_id).syntax().clone();
70 let token = pick_best(file.token_at_offset(position.offset))?;
71 let token = sema.descend_into_macros(token);
72
73 let mut res = HoverResult::new();
74
75 if let Some((node, name_kind)) = match_ast! {
76 match (token.parent()) {
77 ast::NameRef(name_ref) => {
78 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
79 },
80 ast::Name(name) => {
81 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
82 },
83 _ => None,
84 }
85 } {
86 let range = sema.original_range(&node).range;
87 res.extend(hover_text_from_name_kind(db, name_kind));
88
89 if !res.is_empty() {
90 return Some(RangeInfo::new(range, res));
91 }
92 }
93
94 let node = token
95 .ancestors()
96 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
97
98 let ty = match_ast! {
99 match node {
100 ast::MacroCall(_it) => {
101 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
102 // (e.g expanding a builtin macro). So we give up here.
103 return None;
104 },
105 ast::Expr(it) => {
106 sema.type_of_expr(&it)
107 },
108 ast::Pat(it) => {
109 sema.type_of_pat(&it)
110 },
111 _ => None,
112 }
113 }?;
114
115 res.extend(Some(rust_code_markup(&ty.display(db))));
116 let range = sema.original_range(&node).range;
117 Some(RangeInfo::new(range, res))
118}
119
65fn hover_text( 120fn hover_text(
66 docs: Option<String>, 121 docs: Option<String>,
67 desc: Option<String>, 122 desc: Option<String>,
@@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
160 } 215 }
161} 216}
162 217
163pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
164 let sema = Semantics::new(db);
165 let file = sema.parse(position.file_id).syntax().clone();
166 let token = pick_best(file.token_at_offset(position.offset))?;
167 let token = sema.descend_into_macros(token);
168
169 let mut res = HoverResult::new();
170
171 if let Some((node, name_kind)) = match_ast! {
172 match (token.parent()) {
173 ast::NameRef(name_ref) => {
174 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
175 },
176 ast::Name(name) => {
177 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
178 },
179 _ => None,
180 }
181 } {
182 let range = sema.original_range(&node).range;
183 res.extend(hover_text_from_name_kind(db, name_kind));
184
185 if !res.is_empty() {
186 return Some(RangeInfo::new(range, res));
187 }
188 }
189
190 let node = token
191 .ancestors()
192 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
193
194 let ty = match_ast! {
195 match node {
196 ast::MacroCall(_it) => {
197 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
198 // (e.g expanding a builtin macro). So we give up here.
199 return None;
200 },
201 ast::Expr(it) => {
202 sema.type_of_expr(&it)
203 },
204 ast::Pat(it) => {
205 sema.type_of_pat(&it)
206 },
207 _ => None,
208 }
209 }?;
210
211 res.extend(Some(rust_code_markup(&ty.display(db))));
212 let range = sema.original_range(&node).range;
213 Some(RangeInfo::new(range, res))
214}
215
216fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 218fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
217 return tokens.max_by_key(priority); 219 return tokens.max_by_key(priority);
218 fn priority(n: &SyntaxToken) -> usize { 220 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 @@
1//! This module defines multiple types of inlay hints and their visibility
2
3use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_prof::profile; 3use ra_prof::profile;
@@ -39,6 +37,26 @@ pub struct InlayHint {
39 pub label: SmolStr, 37 pub label: SmolStr,
40} 38}
41 39
40// Feature: Inlay Hints
41//
42// rust-analyzer shows additional information inline with the source code.
43// Editors usually render this using read-only virtual text snippets interspersed with code.
44//
45// rust-analyzer shows hits for
46//
47// * types of local variables
48// * names of function arguments
49// * types of chained expressions
50//
51// **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.
52// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
53// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
54//
55// |===
56// | Editor | Action Name
57//
58// | VS Code | **Rust Analyzer: Toggle inlay hints*
59// |===
42pub(crate) fn inlay_hints( 60pub(crate) fn inlay_hints(
43 db: &RootDatabase, 61 db: &RootDatabase,
44 file_id: FileId, 62 file_id: FileId,
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 @@
1//! FIXME: write short doc here
2
3use itertools::Itertools; 1use itertools::Itertools;
4use ra_fmt::{compute_ws, extract_trivial_expression}; 2use ra_fmt::{compute_ws, extract_trivial_expression};
5use ra_syntax::{ 3use ra_syntax::{
@@ -11,6 +9,15 @@ use ra_syntax::{
11}; 9};
12use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
13 11
12// Feature: Join Lines
13//
14// Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
15//
16// |===
17// | Editor | Action Name
18//
19// | VS Code | **Rust Analyzer: Join lines**
20// |===
14pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { 21pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
15 let range = if range.is_empty() { 22 let range = if range.is_empty() {
16 let syntax = file.syntax(); 23 let syntax = file.syntax();
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;
23mod runnables; 23mod runnables;
24mod goto_definition; 24mod goto_definition;
25mod goto_type_definition; 25mod goto_type_definition;
26mod goto_implementation;
26mod extend_selection; 27mod extend_selection;
27mod hover; 28mod hover;
28mod call_hierarchy; 29mod call_hierarchy;
@@ -30,7 +31,6 @@ mod call_info;
30mod syntax_highlighting; 31mod syntax_highlighting;
31mod parent_module; 32mod parent_module;
32mod references; 33mod references;
33mod impls;
34mod diagnostics; 34mod diagnostics;
35mod syntax_tree; 35mod syntax_tree;
36mod folding_ranges; 36mod folding_ranges;
@@ -373,7 +373,7 @@ impl Analysis {
373 &self, 373 &self,
374 position: FilePosition, 374 position: FilePosition,
375 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 375 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
376 self.with_db(|db| impls::goto_implementation(db, position)) 376 self.with_db(|db| goto_implementation::goto_implementation(db, position))
377 } 377 }
378 378
379 /// Returns the type definitions for the symbol at `position`. 379 /// Returns the type definitions for the symbol at `position`.
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 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; 1use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
4 2
3// Feature: Matching Brace
4//
5// If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
6// moves cursor to the matching brace. It uses the actual parser to determine
7// braces, so it won't confuse generics with comparisons.
8//
9// |===
10// | Editor | Action Name
11//
12// | VS Code | **Rust Analyzer: Find matching brace**
13// |===
5pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 14pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
6 const BRACES: &[SyntaxKind] = 15 const BRACES: &[SyntaxKind] =
7 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; 16 &[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 @@
1//! FIXME: write short doc here
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_db::{CrateId, FileId, FilePosition}; 2use ra_db::{CrateId, FileId, FilePosition};
5use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
@@ -11,6 +9,16 @@ use test_utils::mark;
11 9
12use crate::NavigationTarget; 10use crate::NavigationTarget;
13 11
12// Feature: Parent Module
13//
14// Navigates to the parent module of the current module.
15//
16// |===
17// | Editor | Action Name
18//
19// | VS Code | **Rust Analyzer: Locate parent module**
20// |===
21
14/// This returns `Vec` because a module may be included from several places. We 22/// This returns `Vec` because a module may be included from several places. We
15/// don't handle this case yet though, so the Vec has length at most one. 23/// don't handle this case yet though, so the Vec has length at most one.
16pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 24pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 96444bf6a..bb40d2043 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -615,6 +615,33 @@ mod tests {
615 ); 615 );
616 } 616 }
617 617
618 #[test]
619 fn test_find_all_refs_nested_module() {
620 let code = r#"
621 //- /lib.rs
622 mod foo {
623 mod bar;
624 }
625
626 fn f<|>() {}
627
628 //- /foo/bar.rs
629 use crate::f;
630
631 fn g() {
632 f();
633 }
634 "#;
635
636 let (analysis, pos) = analysis_and_position(code);
637 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
638 check_result(
639 refs,
640 "f FN_DEF FileId(1) 25..34 28..29 Other",
641 &["FileId(2) 11..12 Other", "FileId(2) 27..28 StructLiteral"],
642 );
643 }
644
618 fn get_all_refs(text: &str) -> ReferenceSearchResult { 645 fn get_all_refs(text: &str) -> ReferenceSearchResult {
619 let (analysis, position) = single_file_with_position(text); 646 let (analysis, position) = single_file_with_position(text);
620 analysis.find_all_refs(position, None).unwrap().unwrap() 647 analysis.find_all_refs(position, None).unwrap().unwrap()
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 6e7e47199..286d45eee 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 2use itertools::Itertools;
5use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
@@ -44,6 +42,17 @@ pub enum RunnableKind {
44 Bin, 42 Bin,
45} 43}
46 44
45// Feature: Run
46//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
48// location**. Super useful for repeatedly running just a single test. Do bind this
49// to a shortcut!
50//
51// |===
52// | Editor | Action Name
53//
54// | VS Code | **Rust Analyzer: Run**
55// |===
47pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 56pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
48 let sema = Semantics::new(db); 57 let sema = Semantics::new(db);
49 let source_file = sema.parse(file_id); 58 let source_file = sema.parse(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 @@
1//! structural search replace
2
3use std::{collections::HashMap, iter::once, str::FromStr}; 1use std::{collections::HashMap, iter::once, str::FromStr};
4 2
5use ra_db::{SourceDatabase, SourceDatabaseExt}; 3use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError {
25 23
26impl std::error::Error for SsrError {} 24impl std::error::Error for SsrError {}
27 25
26// Feature: Structural Seach and Replace
27//
28// Search and replace with named wildcards that will match any expression.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement.
31// Available via the command `rust-analyzer.ssr`.
32//
33// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
35//
36// // BEFORE
37// String::from(foo(y + 5, z))
38//
39// // AFTER
40// String::from((y + 5).foo(z))
41// ```
42//
43// |===
44// | Editor | Action Name
45//
46// | VS Code | **Rust Analyzer: Structural Search Replace**
47// |===
28pub fn parse_search_replace( 48pub fn parse_search_replace(
29 query: &str, 49 query: &str,
30 parse_only: bool, 50 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 @@
1//! FIXME: write short doc here
2
3use std::{fmt, iter::FromIterator, sync::Arc}; 1use std::{fmt, iter::FromIterator, sync::Arc};
4 2
5use hir::MacroFile; 3use hir::MacroFile;
@@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
26 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() 24 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>()
27} 25}
28 26
27// Feature: Status
28//
29// Shows internal statistic about memory usage of rust-analyzer.
30//
31// |===
32// | Editor | Action Name
33//
34// | VS Code | **Rust Analyzer: Status**
35// |===
29pub(crate) fn status(db: &RootDatabase) -> String { 36pub(crate) fn status(db: &RootDatabase) -> String {
30 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 37 let files_stats = db.query(FileTextQuery).entries::<FilesStats>();
31 let syntax_tree_stats = syntax_tree_stats(db); 38 let syntax_tree_stats = syntax_tree_stats(db);
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index cd6464b40..0b53ebe69 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,3 @@
1//! Implements syntax highlighting.
2
3mod tags; 1mod tags;
4mod html; 2mod html;
5#[cfg(test)] 3#[cfg(test)]
@@ -32,81 +30,15 @@ pub struct HighlightedRange {
32 pub binding_hash: Option<u64>, 30 pub binding_hash: Option<u64>,
33} 31}
34 32
35#[derive(Debug)] 33// Feature: Semantic Syntax Highlighting
36struct HighlightedRangeStack { 34//
37 stack: Vec<Vec<HighlightedRange>>, 35// rust-analyzer highlights the code semantically.
38} 36// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
39 37// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
40/// We use a stack to implement the flattening logic for the highlighted 38// It's up to the client to map those to specific colors.
41/// syntax ranges. 39//
42impl HighlightedRangeStack { 40// The general rule is that a reference to an entity gets colored the same way as the entity itself.
43 fn new() -> Self { 41// We also give special modifier for `mut` and `&mut` local variables.
44 Self { stack: vec![Vec::new()] }
45 }
46
47 fn push(&mut self) {
48 self.stack.push(Vec::new());
49 }
50
51 /// Flattens the highlighted ranges.
52 ///
53 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
54 /// 1) parent-range: Attribute [0, 23)
55 /// 2) child-range: String [16, 21)
56 ///
57 /// The following code implements the flattening, for our example this results to:
58 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
59 fn pop(&mut self) {
60 let children = self.stack.pop().unwrap();
61 let prev = self.stack.last_mut().unwrap();
62 let needs_flattening = !children.is_empty()
63 && !prev.is_empty()
64 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
65 if !needs_flattening {
66 prev.extend(children);
67 } else {
68 let mut parent = prev.pop().unwrap();
69 for ele in children {
70 assert!(parent.range.contains_range(ele.range));
71 let mut cloned = parent.clone();
72 parent.range = TextRange::new(parent.range.start(), ele.range.start());
73 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
74 if !parent.range.is_empty() {
75 prev.push(parent);
76 }
77 prev.push(ele);
78 parent = cloned;
79 }
80 if !parent.range.is_empty() {
81 prev.push(parent);
82 }
83 }
84 }
85
86 fn add(&mut self, range: HighlightedRange) {
87 self.stack
88 .last_mut()
89 .expect("during DFS traversal, the stack must not be empty")
90 .push(range)
91 }
92
93 fn flattened(mut self) -> Vec<HighlightedRange> {
94 assert_eq!(
95 self.stack.len(),
96 1,
97 "after DFS traversal, the stack should only contain a single element"
98 );
99 let mut res = self.stack.pop().unwrap();
100 res.sort_by_key(|range| range.range.start());
101 // Check that ranges are sorted and disjoint
102 assert!(res
103 .iter()
104 .zip(res.iter().skip(1))
105 .all(|(left, right)| left.range.end() <= right.range.start()));
106 res
107 }
108}
109
110pub(crate) fn highlight( 42pub(crate) fn highlight(
111 db: &RootDatabase, 43 db: &RootDatabase,
112 file_id: FileId, 44 file_id: FileId,
@@ -291,6 +223,81 @@ pub(crate) fn highlight(
291 stack.flattened() 223 stack.flattened()
292} 224}
293 225
226#[derive(Debug)]
227struct HighlightedRangeStack {
228 stack: Vec<Vec<HighlightedRange>>,
229}
230
231/// We use a stack to implement the flattening logic for the highlighted
232/// syntax ranges.
233impl HighlightedRangeStack {
234 fn new() -> Self {
235 Self { stack: vec![Vec::new()] }
236 }
237
238 fn push(&mut self) {
239 self.stack.push(Vec::new());
240 }
241
242 /// Flattens the highlighted ranges.
243 ///
244 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
245 /// 1) parent-range: Attribute [0, 23)
246 /// 2) child-range: String [16, 21)
247 ///
248 /// The following code implements the flattening, for our example this results to:
249 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
250 fn pop(&mut self) {
251 let children = self.stack.pop().unwrap();
252 let prev = self.stack.last_mut().unwrap();
253 let needs_flattening = !children.is_empty()
254 && !prev.is_empty()
255 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
256 if !needs_flattening {
257 prev.extend(children);
258 } else {
259 let mut parent = prev.pop().unwrap();
260 for ele in children {
261 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone();
263 parent.range = TextRange::new(parent.range.start(), ele.range.start());
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() {
266 prev.push(parent);
267 }
268 prev.push(ele);
269 parent = cloned;
270 }
271 if !parent.range.is_empty() {
272 prev.push(parent);
273 }
274 }
275 }
276
277 fn add(&mut self, range: HighlightedRange) {
278 self.stack
279 .last_mut()
280 .expect("during DFS traversal, the stack must not be empty")
281 .push(range)
282 }
283
284 fn flattened(mut self) -> Vec<HighlightedRange> {
285 assert_eq!(
286 self.stack.len(),
287 1,
288 "after DFS traversal, the stack should only contain a single element"
289 );
290 let mut res = self.stack.pop().unwrap();
291 res.sort_by_key(|range| range.range.start());
292 // Check that ranges are sorted and disjoint
293 assert!(res
294 .iter()
295 .zip(res.iter().skip(1))
296 .all(|(left, right)| left.range.end() <= right.range.start()));
297 res
298 }
299}
300
294fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { 301fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
295 Some(match kind { 302 Some(match kind {
296 FormatSpecifier::Open 303 FormatSpecifier::Open
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 86c70ff83..a341684fd 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,6 +1,4 @@
1//! FIXME: write short doc here 1use ra_db::{FileId, SourceDatabase};
2
3use ra_db::SourceDatabase;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use ra_syntax::{
6 algo, AstNode, NodeOrToken, SourceFile, 4 algo, AstNode, NodeOrToken, SourceFile,
@@ -8,8 +6,16 @@ use ra_syntax::{
8 SyntaxToken, TextRange, TextSize, 6 SyntaxToken, TextRange, TextSize,
9}; 7};
10 8
11pub use ra_db::FileId; 9// Feature: Show Syntax Tree
12 10//
11// Shows the parse tree of the current file. It exists mostly for debugging
12// rust-analyzer itself.
13//
14// |===
15// | Editor | Action Name
16//
17// | VS Code | **Rust Analyzer: Show Syntax Tree**
18// |===
13pub(crate) fn syntax_tree( 19pub(crate) fn syntax_tree(
14 db: &RootDatabase, 20 db: &RootDatabase,
15 file_id: FileId, 21 file_id: FileId,
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;
32 32
33pub(crate) const TRIGGER_CHARS: &str = ".=>"; 33pub(crate) const TRIGGER_CHARS: &str = ".=>";
34 34
35// Feature: On Typing Assists
36//
37// Some features trigger on typing certain characters:
38//
39// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
40// - Enter inside comments automatically inserts `///`
41// - typing `.` in a chain method call auto-indents
35pub(crate) fn on_char_typed( 42pub(crate) fn on_char_typed(
36 db: &RootDatabase, 43 db: &RootDatabase,
37 position: FilePosition, 44 position: FilePosition,