diff options
-rw-r--r-- | crates/ra_ide/src/completion.rs | 47 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_postfix.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 116 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 22 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 161 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_tree.rs | 2 | ||||
-rw-r--r-- | docs/user/features.md | 96 | ||||
-rw-r--r-- | docs/user/generated_features.adoc | 143 | ||||
-rw-r--r-- | docs/user/readme.adoc | 1 | ||||
-rw-r--r-- | xtask/src/codegen/gen_feature_docs.rs | 4 |
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 | |||
3 | mod completion_config; | 1 | mod completion_config; |
4 | mod completion_item; | 2 | mod completion_item; |
5 | mod completion_context; | 3 | mod 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 | 2 | use ra_assists::utils::TryEnum; | |
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, AstNode}, | 4 | ast::{self, AstNode}, |
5 | TextRange, TextSize, | 5 | TextRange, TextSize, |
6 | }; | 6 | }; |
7 | use ra_text_edit::TextEdit; | 7 | use ra_text_edit::TextEdit; |
8 | 8 | ||
9 | use super::completion_config::SnippetCap; | ||
10 | use crate::{ | 9 | use 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 | }; |
17 | use ra_assists::utils::TryEnum; | 16 | |
17 | use super::completion_config::SnippetCap; | ||
18 | 18 | ||
19 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 19 | pub(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 | 1 | use std::iter::once; |
2 | //! source code items (e.g. function call, struct field, variable symbol...) | ||
3 | 2 | ||
4 | use hir::{ | 3 | use 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 | }; |
7 | use itertools::Itertools; | ||
8 | use ra_db::SourceDatabase; | 8 | use ra_db::SourceDatabase; |
9 | use ra_ide_db::{ | 9 | use 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 | }; |
24 | use itertools::Itertools; | ||
25 | use 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. | ||
67 | pub(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 | |||
65 | fn hover_text( | 120 | fn 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 | ||
163 | pub(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 | |||
216 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 218 | fn 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 | |||
3 | use hir::{Adt, HirDisplay, Semantics, Type}; | 1 | use hir::{Adt, HirDisplay, Semantics, Type}; |
4 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
5 | use ra_prof::profile; | 3 | use 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 | // |=== | ||
42 | pub(crate) fn inlay_hints( | 60 | pub(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 | |||
3 | mod tags; | 1 | mod tags; |
4 | mod html; | 2 | mod 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 |
36 | struct 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 | // |
42 | impl 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 | |||
110 | pub(crate) fn highlight( | 42 | pub(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)] | ||
227 | struct HighlightedRangeStack { | ||
228 | stack: Vec<Vec<HighlightedRange>>, | ||
229 | } | ||
230 | |||
231 | /// We use a stack to implement the flattening logic for the highlighted | ||
232 | /// syntax ranges. | ||
233 | impl 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 | |||
294 | fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { | 301 | fn 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 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
3 | use ra_syntax::{ | 3 | use 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 @@ | |||
1 | This document is an index of features that the rust-analyzer language server | ||
2 | provides. Shortcuts are for the default VS Code layout. If there's no shortcut, | ||
3 | you 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 | |||
10 | Toggle inlay hints view for the current workspace. | ||
11 | It is recommended to assign a shortcut for this command to quickly turn off | ||
12 | inlay hints when they prevent you from reading/writing the code. | ||
13 | |||
14 | ### Magic Completions | ||
15 | |||
16 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | ||
17 | completions as well: | ||
18 | |||
19 | Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor | ||
20 | is placed at the appropriate position. Even though `if` is easy to type, you | ||
21 | still want to complete it, to get ` { }` for free! `return` is inserted with a | ||
22 | space or `;` depending on the return type of the function. | ||
23 | |||
24 | When completing a function call, `()` are automatically inserted. If a function | ||
25 | takes arguments, the cursor is positioned inside the parenthesis. | ||
26 | |||
27 | There 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 | |||
38 | There 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)] | ||
51 | mod tests { | ||
52 | use super::*; | ||
53 | |||
54 | #[test] | ||
55 | fn test_fn() {} | ||
56 | } | ||
57 | ``` | ||
58 | |||
59 | ### Code Highlighting | ||
60 | |||
61 | Experimental feature to let rust-analyzer highlight Rust code instead of using the | ||
62 | default highlighter. | ||
63 | |||
64 | #### Rainbow Highlighting | ||
65 | |||
66 | Experimental feature that, given code highlighting using rust-analyzer is | ||
67 | active, will pick unique colors for identifiers. | ||
68 | |||
69 | ### Code hints | ||
70 | |||
71 | Rust-analyzer has two types of hints to show the information about the code: | ||
72 | |||
73 | * hover hints, appearing on hover on any element. | ||
74 | |||
75 | These contain extended information on the hovered language item. | ||
76 | |||
77 | * inlay hints, shown near the element hinted directly in the editor. | ||
78 | |||
79 | Two 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 | |||
87 | In 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. | ||
95 | This 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 | |||
5 | Shows 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 | |||
88 | Shows additional information, like type of an expression or documentation for definition when "focusing" code. | ||
89 | Focusing 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 | |||
96 | rust-analyzer shows additional information inline with the source code. | ||
97 | Editors usually render this using read-only virtual text snippets interspersed with code. | ||
98 | |||
99 | rust-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. | ||
106 | This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: | ||
107 | https://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 | |||
133 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | ||
134 | completions as well: | ||
135 | |||
136 | Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor | ||
137 | is placed at the appropriate position. Even though `if` is easy to type, you | ||
138 | still want to complete it, to get ` { }` for free! `return` is inserted with a | ||
139 | space or `;` depending on the return type of the function. | ||
140 | |||
141 | When completing a function call, `()` are automatically inserted. If a function | ||
142 | takes arguments, the cursor is positioned inside the parenthesis. | ||
143 | |||
144 | There 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 | |||
155 | There 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)] | ||
166 | mod 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 | |||
233 | rust-analyzer highlights the code semantically. | ||
234 | For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. | ||
235 | rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. | ||
236 | It's up to the client to map those to specific colors. | ||
237 | |||
238 | The general rule is that a reference to an entity gets colored the same way as the entity itself. | ||
239 | We 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 | |||
260 | Shows 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 | |||
273 | Search and replace with named wildcards that will match any expression. | ||
274 | The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. | ||
275 | A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. | ||
276 | Available 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 | ||
282 | String::from(foo(y + 5, z)) | ||
283 | |||
284 | // AFTER | ||
285 | String::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 | ||
51 | fn is_valid_feature_name(feature: &str) -> bool { | 51 | fn 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 | } |