aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/hover.rs116
-rw-r--r--crates/ra_ide/src/inlay_hints.rs22
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs161
-rw-r--r--crates/ra_ide/src/syntax_tree.rs2
-rw-r--r--docs/user/features.md96
-rw-r--r--docs/user/generated_features.adoc143
-rw-r--r--docs/user/readme.adoc1
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs4
10 files changed, 358 insertions, 240 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/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/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 @@
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 2192f5090..a341684fd 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,4 +1,4 @@
1use ra_db::SourceDatabase; 1use ra_db::{FileId, SourceDatabase};
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo, AstNode, NodeOrToken, SourceFile, 4 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 @@
1This document is an index of features that the rust-analyzer language server
2provides. Shortcuts are for the default VS Code layout. If there's no shortcut,
3you can use <kbd>Ctrl+Shift+P</kbd> to search for the corresponding action.
4
5### Commands <kbd>ctrl+shift+p</kbd>
6
7
8#### Toggle inlay hints
9
10Toggle inlay hints view for the current workspace.
11It is recommended to assign a shortcut for this command to quickly turn off
12inlay hints when they prevent you from reading/writing the code.
13
14### Magic Completions
15
16In addition to usual reference completion, rust-analyzer provides some ✨magic✨
17completions as well:
18
19Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
20is placed at the appropriate position. Even though `if` is easy to type, you
21still want to complete it, to get ` { }` for free! `return` is inserted with a
22space or `;` depending on the return type of the function.
23
24When completing a function call, `()` are automatically inserted. If a function
25takes arguments, the cursor is positioned inside the parenthesis.
26
27There are postfix completions, which can be triggered by typing something like
28`foo().if`. The word after `.` determines postfix completion. Possible variants are:
29
30- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
31- `expr.match` -> `match expr {}`
32- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
33- `expr.ref` -> `&expr`
34- `expr.refm` -> `&mut expr`
35- `expr.not` -> `!expr`
36- `expr.dbg` -> `dbg!(expr)`
37
38There also snippet completions:
39
40#### Inside Expressions
41
42- `pd` -> `println!("{:?}")`
43- `ppd` -> `println!("{:#?}")`
44
45#### Inside Modules
46
47- `tfn` -> `#[test] fn f(){}`
48- `tmod` ->
49```rust
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn test_fn() {}
56}
57```
58
59### Code Highlighting
60
61Experimental feature to let rust-analyzer highlight Rust code instead of using the
62default highlighter.
63
64#### Rainbow Highlighting
65
66Experimental feature that, given code highlighting using rust-analyzer is
67active, will pick unique colors for identifiers.
68
69### Code hints
70
71Rust-analyzer has two types of hints to show the information about the code:
72
73* hover hints, appearing on hover on any element.
74
75These contain extended information on the hovered language item.
76
77* inlay hints, shown near the element hinted directly in the editor.
78
79Two types of inlay hints are displayed currently:
80
81* type hints, displaying the minimal information on the type of the expression (if the information is available)
82* method chaining hints, type information for multi-line method chains
83* parameter name hints, displaying the names of the parameters in the corresponding methods
84
85#### VS Code
86
87In VS Code, the following settings can be used to configure the inlay hints:
88
89* `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types.
90* `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains.
91* `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters.
92* `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied.
93
94**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.
95This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
96[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 @@
1=== Expand Macro Recursively
2**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs]
3
4
5Shows the full macro expansion of the macro at current cursor.
6
7|===
8| Editor | Action Name
9
10| VS Code | **Rust Analyzer: Expand macro recursively**
11|===
12
13
1=== Extend Selection 14=== Extend Selection
2**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs] 15**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
3 16
@@ -68,6 +81,38 @@ Navigates to the type of an identifier.
68|=== 81|===
69 82
70 83
84=== Hover
85**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs]
86
87
88Shows additional information, like type of an expression or documentation for definition when "focusing" code.
89Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
90
91
92=== Inlay Hints
93**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs]
94
95
96rust-analyzer shows additional information inline with the source code.
97Editors usually render this using read-only virtual text snippets interspersed with code.
98
99rust-analyzer shows hits for
100
101* types of local variables
102* names of function arguments
103* types of chained expressions
104
105**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.
106This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
107https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
108
109|===
110| Editor | Action Name
111
112| VS Code | **Rust Analyzer: Toggle inlay hints*
113|===
114
115
71=== Join Lines 116=== Join Lines
72**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs] 117**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
73 118
@@ -81,6 +126,52 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and
81|=== 126|===
82 127
83 128
129=== Magic Completions
130**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs]
131
132
133In addition to usual reference completion, rust-analyzer provides some ✨magic✨
134completions as well:
135
136Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
137is placed at the appropriate position. Even though `if` is easy to type, you
138still want to complete it, to get ` { }` for free! `return` is inserted with a
139space or `;` depending on the return type of the function.
140
141When completing a function call, `()` are automatically inserted. If a function
142takes arguments, the cursor is positioned inside the parenthesis.
143
144There are postfix completions, which can be triggered by typing something like
145`foo().if`. The word after `.` determines postfix completion. Possible variants are:
146
147- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
148- `expr.match` -> `match expr {}`
149- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
150- `expr.ref` -> `&expr`
151- `expr.refm` -> `&mut expr`
152- `expr.not` -> `!expr`
153- `expr.dbg` -> `dbg!(expr)`
154
155There also snippet completions:
156
157.Expressions
158- `pd` -> `println!("{:?}")`
159- `ppd` -> `println!("{:#?}")`
160
161.Items
162- `tfn` -> `#[test] fn f(){}`
163- `tmod` ->
164```rust
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_fn() {}
171}
172```
173
174
84=== Matching Brace 175=== Matching Brace
85**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs] 176**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
86 177
@@ -135,6 +226,19 @@ to a shortcut!
135|=== 226|===
136 227
137 228
229=== Semantic Syntax Highlighting
230**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs]
231
232
233rust-analyzer highlights the code semantically.
234For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
235rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
236It's up to the client to map those to specific colors.
237
238The general rule is that a reference to an entity gets colored the same way as the entity itself.
239We also give special modifier for `mut` and `&mut` local variables.
240
241
138=== Show Syntax Tree 242=== Show Syntax Tree
139**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs] 243**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
140 244
@@ -149,6 +253,45 @@ rust-analyzer itself.
149|=== 253|===
150 254
151 255
256=== Status
257**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs]
258
259
260Shows internal statistic about memory usage of rust-analyzer.
261
262|===
263| Editor | Action Name
264
265| VS Code | **Rust Analyzer: Status**
266|===
267
268
269=== Structural Seach and Replace
270**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs]
271
272
273Search and replace with named wildcards that will match any expression.
274The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
275A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement.
276Available via the command `rust-analyzer.ssr`.
277
278```rust
279// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
280
281// BEFORE
282String::from(foo(y + 5, z))
283
284// AFTER
285String::from((y + 5).foo(z))
286```
287
288|===
289| Editor | Action Name
290
291| VS Code | **Rust Analyzer: Structural Search Replace**
292|===
293
294
152=== Workspace Symbol 295=== Workspace Symbol
153**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs] 296**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
154 297
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 @@
8:important-caption: :heavy_exclamation_mark: 8:important-caption: :heavy_exclamation_mark:
9:caution-caption: :fire: 9:caution-caption: :fire:
10:warning-caption: :warning: 10:warning-caption: :warning:
11:source-highlighter: rouge
11:experimental: 12:experimental:
12 13
13// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository 14// 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 {
50 50
51fn is_valid_feature_name(feature: &str) -> bool { 51fn is_valid_feature_name(feature: &str) -> bool {
52 'word: for word in feature.split_whitespace() { 52 'word: for word in feature.split_whitespace() {
53 for &short in ["to"].iter() { 53 for &short in ["to", "and"].iter() {
54 if word == short { 54 if word == short {
55 continue 'word; 55 continue 'word;
56 } 56 }
57 } 57 }
58 for &short in ["To"].iter() { 58 for &short in ["To", "And"].iter() {
59 if word == short { 59 if word == short {
60 return false; 60 return false;
61 } 61 }