aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/diagnostics.rs62
-rw-r--r--crates/ide/src/diagnostics/fixes.rs38
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs2
-rw-r--r--crates/ide/src/display/navigation_target.rs8
-rw-r--r--crates/ide/src/doc_links.rs58
-rw-r--r--crates/ide/src/expand_macro.rs23
-rw-r--r--crates/ide/src/goto_definition.rs18
-rw-r--r--crates/ide/src/lib.rs40
-rw-r--r--crates/ide/src/move_item.rs100
-rw-r--r--crates/ide/src/prime_caches.rs4
-rw-r--r--crates/ide/src/references/rename.rs103
-rw-r--r--crates/ide/src/runnables.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs186
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs16
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs75
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html14
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html34
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs100
-rw-r--r--crates/ide/src/typing.rs200
-rw-r--r--crates/ide/src/typing/on_enter.rs210
23 files changed, 858 insertions, 459 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 4f0b4a62e..1c911a8b2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -84,6 +84,7 @@ pub struct DiagnosticsConfig {
84pub(crate) fn diagnostics( 84pub(crate) fn diagnostics(
85 db: &RootDatabase, 85 db: &RootDatabase,
86 config: &DiagnosticsConfig, 86 config: &DiagnosticsConfig,
87 resolve: bool,
87 file_id: FileId, 88 file_id: FileId,
88) -> Vec<Diagnostic> { 89) -> Vec<Diagnostic> {
89 let _p = profile::span("diagnostics"); 90 let _p = profile::span("diagnostics");
@@ -107,25 +108,25 @@ pub(crate) fn diagnostics(
107 let res = RefCell::new(res); 108 let res = RefCell::new(res);
108 let sink_builder = DiagnosticSinkBuilder::new() 109 let sink_builder = DiagnosticSinkBuilder::new()
109 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 110 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
110 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 111 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
111 }) 112 })
112 .on::<hir::diagnostics::MissingFields, _>(|d| { 113 .on::<hir::diagnostics::MissingFields, _>(|d| {
113 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 114 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
114 }) 115 })
115 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { 116 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
116 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 117 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
117 }) 118 })
118 .on::<hir::diagnostics::NoSuchField, _>(|d| { 119 .on::<hir::diagnostics::NoSuchField, _>(|d| {
119 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 120 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
120 }) 121 })
121 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { 122 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
122 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 123 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
123 }) 124 })
124 .on::<hir::diagnostics::IncorrectCase, _>(|d| { 125 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
125 res.borrow_mut().push(warning_with_fix(d, &sema)); 126 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
126 }) 127 })
127 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { 128 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
128 res.borrow_mut().push(warning_with_fix(d, &sema)); 129 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
129 }) 130 })
130 .on::<hir::diagnostics::InactiveCode, _>(|d| { 131 .on::<hir::diagnostics::InactiveCode, _>(|d| {
131 // If there's inactive code somewhere in a macro, don't propagate to the call-site. 132 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
@@ -152,7 +153,7 @@ pub(crate) fn diagnostics(
152 // Override severity and mark as unused. 153 // Override severity and mark as unused.
153 res.borrow_mut().push( 154 res.borrow_mut().push(
154 Diagnostic::hint(range, d.message()) 155 Diagnostic::hint(range, d.message())
155 .with_fix(d.fix(&sema)) 156 .with_fix(d.fix(&sema, resolve))
156 .with_code(Some(d.code())), 157 .with_code(Some(d.code())),
157 ); 158 );
158 }) 159 })
@@ -208,15 +209,23 @@ pub(crate) fn diagnostics(
208 res.into_inner() 209 res.into_inner()
209} 210}
210 211
211fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 212fn diagnostic_with_fix<D: DiagnosticWithFix>(
213 d: &D,
214 sema: &Semantics<RootDatabase>,
215 resolve: bool,
216) -> Diagnostic {
212 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 217 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
213 .with_fix(d.fix(&sema)) 218 .with_fix(d.fix(&sema, resolve))
214 .with_code(Some(d.code())) 219 .with_code(Some(d.code()))
215} 220}
216 221
217fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 222fn warning_with_fix<D: DiagnosticWithFix>(
223 d: &D,
224 sema: &Semantics<RootDatabase>,
225 resolve: bool,
226) -> Diagnostic {
218 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 227 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
219 .with_fix(d.fix(&sema)) 228 .with_fix(d.fix(&sema, resolve))
220 .with_code(Some(d.code())) 229 .with_code(Some(d.code()))
221} 230}
222 231
@@ -271,13 +280,19 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
271} 280}
272 281
273fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { 282fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
283 let mut res = unresolved_fix(id, label, target);
284 res.source_change = Some(source_change);
285 res
286}
287
288fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
274 assert!(!id.contains(' ')); 289 assert!(!id.contains(' '));
275 Assist { 290 Assist {
276 id: AssistId(id, AssistKind::QuickFix), 291 id: AssistId(id, AssistKind::QuickFix),
277 label: Label::new(label), 292 label: Label::new(label),
278 group: None, 293 group: None,
279 target, 294 target,
280 source_change: Some(source_change), 295 source_change: None,
281 } 296 }
282} 297}
283 298
@@ -299,7 +314,7 @@ mod tests {
299 314
300 let (analysis, file_position) = fixture::position(ra_fixture_before); 315 let (analysis, file_position) = fixture::position(ra_fixture_before);
301 let diagnostic = analysis 316 let diagnostic = analysis
302 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) 317 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
303 .unwrap() 318 .unwrap()
304 .pop() 319 .pop()
305 .unwrap(); 320 .unwrap();
@@ -328,7 +343,7 @@ mod tests {
328 fn check_no_fix(ra_fixture: &str) { 343 fn check_no_fix(ra_fixture: &str) {
329 let (analysis, file_position) = fixture::position(ra_fixture); 344 let (analysis, file_position) = fixture::position(ra_fixture);
330 let diagnostic = analysis 345 let diagnostic = analysis
331 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) 346 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
332 .unwrap() 347 .unwrap()
333 .pop() 348 .pop()
334 .unwrap(); 349 .unwrap();
@@ -342,7 +357,7 @@ mod tests {
342 let diagnostics = files 357 let diagnostics = files
343 .into_iter() 358 .into_iter()
344 .flat_map(|file_id| { 359 .flat_map(|file_id| {
345 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() 360 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap()
346 }) 361 })
347 .collect::<Vec<_>>(); 362 .collect::<Vec<_>>();
348 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 363 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
@@ -350,7 +365,8 @@ mod tests {
350 365
351 fn check_expect(ra_fixture: &str, expect: Expect) { 366 fn check_expect(ra_fixture: &str, expect: Expect) {
352 let (analysis, file_id) = fixture::file(ra_fixture); 367 let (analysis, file_id) = fixture::file(ra_fixture);
353 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); 368 let diagnostics =
369 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
354 expect.assert_debug_eq(&diagnostics) 370 expect.assert_debug_eq(&diagnostics)
355 } 371 }
356 372
@@ -709,7 +725,7 @@ fn test_fn() {
709 expect![[r#" 725 expect![[r#"
710 [ 726 [
711 Diagnostic { 727 Diagnostic {
712 message: "unresolved macro call", 728 message: "unresolved macro `foo::bar!`",
713 range: 5..8, 729 range: 5..8,
714 severity: Error, 730 severity: Error,
715 fix: None, 731 fix: None,
@@ -895,10 +911,11 @@ struct Foo {
895 911
896 let (analysis, file_id) = fixture::file(r#"mod foo;"#); 912 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
897 913
898 let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); 914 let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap();
899 assert!(diagnostics.is_empty()); 915 assert!(diagnostics.is_empty());
900 916
901 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); 917 let diagnostics =
918 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
902 assert!(!diagnostics.is_empty()); 919 assert!(!diagnostics.is_empty());
903 } 920 }
904 921
@@ -1004,8 +1021,9 @@ impl TestStruct {
1004 let expected = r#"fn foo() {}"#; 1021 let expected = r#"fn foo() {}"#;
1005 1022
1006 let (analysis, file_position) = fixture::position(input); 1023 let (analysis, file_position) = fixture::position(input);
1007 let diagnostics = 1024 let diagnostics = analysis
1008 analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); 1025 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
1026 .unwrap();
1009 assert_eq!(diagnostics.len(), 1); 1027 assert_eq!(diagnostics.len(), 1);
1010 1028
1011 check_fix(input, expected); 1029 check_fix(input, expected);
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 69cf5288c..7be8b3459 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -20,17 +20,26 @@ use syntax::{
20}; 20};
21use text_edit::TextEdit; 21use text_edit::TextEdit;
22 22
23use crate::{diagnostics::fix, references::rename::rename_with_semantics, Assist, FilePosition}; 23use crate::{
24 diagnostics::{fix, unresolved_fix},
25 references::rename::rename_with_semantics,
26 Assist, FilePosition,
27};
24 28
25/// A [Diagnostic] that potentially has a fix available. 29/// A [Diagnostic] that potentially has a fix available.
26/// 30///
27/// [Diagnostic]: hir::diagnostics::Diagnostic 31/// [Diagnostic]: hir::diagnostics::Diagnostic
28pub(crate) trait DiagnosticWithFix: Diagnostic { 32pub(crate) trait DiagnosticWithFix: Diagnostic {
29 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist>; 33 /// `resolve` determines if the diagnostic should fill in the `edit` field
34 /// of the assist.
35 ///
36 /// If `resolve` is false, the edit will be computed later, on demand, and
37 /// can be omitted.
38 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>;
30} 39}
31 40
32impl DiagnosticWithFix for UnresolvedModule { 41impl DiagnosticWithFix for UnresolvedModule {
33 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 42 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
34 let root = sema.db.parse_or_expand(self.file)?; 43 let root = sema.db.parse_or_expand(self.file)?;
35 let unresolved_module = self.decl.to_node(&root); 44 let unresolved_module = self.decl.to_node(&root);
36 Some(fix( 45 Some(fix(
@@ -50,7 +59,7 @@ impl DiagnosticWithFix for UnresolvedModule {
50} 59}
51 60
52impl DiagnosticWithFix for NoSuchField { 61impl DiagnosticWithFix for NoSuchField {
53 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 62 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
54 let root = sema.db.parse_or_expand(self.file)?; 63 let root = sema.db.parse_or_expand(self.file)?;
55 missing_record_expr_field_fix( 64 missing_record_expr_field_fix(
56 &sema, 65 &sema,
@@ -61,7 +70,7 @@ impl DiagnosticWithFix for NoSuchField {
61} 70}
62 71
63impl DiagnosticWithFix for MissingFields { 72impl DiagnosticWithFix for MissingFields {
64 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 73 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
65 // Note that although we could add a diagnostics to 74 // Note that although we could add a diagnostics to
66 // fill the missing tuple field, e.g : 75 // fill the missing tuple field, e.g :
67 // `struct A(usize);` 76 // `struct A(usize);`
@@ -97,7 +106,7 @@ impl DiagnosticWithFix for MissingFields {
97} 106}
98 107
99impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { 108impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
100 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 109 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
101 let root = sema.db.parse_or_expand(self.file)?; 110 let root = sema.db.parse_or_expand(self.file)?;
102 let tail_expr = self.expr.to_node(&root); 111 let tail_expr = self.expr.to_node(&root);
103 let tail_expr_range = tail_expr.syntax().text_range(); 112 let tail_expr_range = tail_expr.syntax().text_range();
@@ -110,7 +119,7 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
110} 119}
111 120
112impl DiagnosticWithFix for RemoveThisSemicolon { 121impl DiagnosticWithFix for RemoveThisSemicolon {
113 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 122 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
114 let root = sema.db.parse_or_expand(self.file)?; 123 let root = sema.db.parse_or_expand(self.file)?;
115 124
116 let semicolon = self 125 let semicolon = self
@@ -130,7 +139,7 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
130} 139}
131 140
132impl DiagnosticWithFix for IncorrectCase { 141impl DiagnosticWithFix for IncorrectCase {
133 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 142 fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> {
134 let root = sema.db.parse_or_expand(self.file)?; 143 let root = sema.db.parse_or_expand(self.file)?;
135 let name_node = self.ident.to_node(&root); 144 let name_node = self.ident.to_node(&root);
136 145
@@ -138,16 +147,19 @@ impl DiagnosticWithFix for IncorrectCase {
138 let frange = name_node.original_file_range(sema.db); 147 let frange = name_node.original_file_range(sema.db);
139 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 148 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
140 149
141 let rename_changes =
142 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?;
143
144 let label = format!("Rename to {}", self.suggested_text); 150 let label = format!("Rename to {}", self.suggested_text);
145 Some(fix("change_case", &label, rename_changes, frange.range)) 151 let mut res = unresolved_fix("change_case", &label, frange.range);
152 if resolve {
153 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
154 res.source_change = Some(source_change.ok().unwrap_or_default());
155 }
156
157 Some(res)
146 } 158 }
147} 159}
148 160
149impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { 161impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
150 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Assist> { 162 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
151 let root = sema.db.parse_or_expand(self.file)?; 163 let root = sema.db.parse_or_expand(self.file)?;
152 let next_expr = self.next_expr.to_node(&root); 164 let next_expr = self.next_expr.to_node(&root);
153 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; 165 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index 5482b7287..7d39f4fbe 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -50,7 +50,7 @@ impl Diagnostic for UnlinkedFile {
50} 50}
51 51
52impl DiagnosticWithFix for UnlinkedFile { 52impl DiagnosticWithFix for UnlinkedFile {
53 fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Assist> { 53 fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
54 // If there's an existing module that could add a `mod` item to include the unlinked file, 54 // If there's an existing module that could add a `mod` item to include the unlinked file,
55 // suggest that as a fix. 55 // suggest that as a fix.
56 56
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 364be260c..2079c22a3 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -20,7 +20,7 @@ use syntax::{
20 20
21use crate::FileSymbol; 21use crate::FileSymbol;
22 22
23/// `NavigationTarget` represents and element in the editor's UI which you can 23/// `NavigationTarget` represents an element in the editor's UI which you can
24/// click on to navigate to a particular piece of code. 24/// click on to navigate to a particular piece of code.
25/// 25///
26/// Typically, a `NavigationTarget` corresponds to some element in the source 26/// Typically, a `NavigationTarget` corresponds to some element in the source
@@ -35,12 +35,10 @@ pub struct NavigationTarget {
35 /// Clients should use this range to answer "is the cursor inside the 35 /// Clients should use this range to answer "is the cursor inside the
36 /// element?" question. 36 /// element?" question.
37 pub full_range: TextRange, 37 pub full_range: TextRange,
38 /// A "most interesting" range withing the `full_range`. 38 /// A "most interesting" range within the `full_range`.
39 /// 39 ///
40 /// Typically, `full_range` is the whole syntax node, including doc 40 /// Typically, `full_range` is the whole syntax node, including doc
41 /// comments, and `focus_range` is the range of the identifier. "Most 41 /// comments, and `focus_range` is the range of the identifier.
42 /// interesting" range within the full range, typically the range of
43 /// identifier.
44 /// 42 ///
45 /// Clients should place the cursor on this range when navigating to this target. 43 /// Clients should place the cursor on this range when navigating to this target.
46 pub focus_range: Option<TextRange>, 44 pub focus_range: Option<TextRange>,
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index c5dc14a23..cb5a8e19a 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -108,13 +108,13 @@ pub(crate) fn external_docs(
108 let node = token.parent()?; 108 let node = token.parent()?;
109 let definition = match_ast! { 109 let definition = match_ast! {
110 match node { 110 match node {
111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db))?,
112 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), 112 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db))?,
113 _ => None, 113 _ => return None,
114 } 114 }
115 }; 115 };
116 116
117 get_doc_link(db, definition?) 117 get_doc_link(db, definition)
118} 118}
119 119
120/// Extracts all links from a given markdown text. 120/// Extracts all links from a given markdown text.
@@ -214,20 +214,20 @@ fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, Cow
214// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented 214// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented
215// https://github.com/rust-lang/rfcs/pull/2988 215// https://github.com/rust-lang/rfcs/pull/2988
216fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { 216fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
217 // Get the outermost definition for the moduledef. This is used to resolve the public path to the type, 217 // Get the outermost definition for the module def. This is used to resolve the public path to the type,
218 // then we can join the method, field, etc onto it if required. 218 // then we can join the method, field, etc onto it if required.
219 let target_def: ModuleDef = match definition { 219 let target_def: ModuleDef = match definition {
220 Definition::ModuleDef(moddef) => match moddef { 220 Definition::ModuleDef(def) => match def {
221 ModuleDef::Function(f) => f 221 ModuleDef::Function(f) => f
222 .as_assoc_item(db) 222 .as_assoc_item(db)
223 .and_then(|assoc| match assoc.container(db) { 223 .and_then(|assoc| match assoc.container(db) {
224 AssocItemContainer::Trait(t) => Some(t.into()), 224 AssocItemContainer::Trait(t) => Some(t.into()),
225 AssocItemContainer::Impl(impld) => { 225 AssocItemContainer::Impl(impl_) => {
226 impld.self_ty(db).as_adt().map(|adt| adt.into()) 226 impl_.self_ty(db).as_adt().map(|adt| adt.into())
227 } 227 }
228 }) 228 })
229 .unwrap_or_else(|| f.clone().into()), 229 .unwrap_or_else(|| def),
230 moddef => moddef, 230 def => def,
231 }, 231 },
232 Definition::Field(f) => f.parent_def(db).into(), 232 Definition::Field(f) => f.parent_def(db).into(),
233 // FIXME: Handle macros 233 // FIXME: Handle macros
@@ -236,17 +236,28 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
236 236
237 let ns = ItemInNs::from(target_def); 237 let ns = ItemInNs::from(target_def);
238 238
239 let module = definition.module(db)?; 239 let krate = match definition {
240 let krate = module.krate(); 240 // Definition::module gives back the parent module, we don't want that as it fails for root modules
241 Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(),
242 _ => definition.module(db)?.krate(),
243 };
241 let import_map = db.import_map(krate.into()); 244 let import_map = db.import_map(krate.into());
242 let base = once(krate.display_name(db)?.to_string()) 245
243 .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) 246 let mut base = krate.display_name(db)?.to_string();
244 .join("/") 247 let is_root_module = matches!(
245 + "/"; 248 definition,
249 Definition::ModuleDef(ModuleDef::Module(module)) if krate.root_module(db) == module
250 );
251 if !is_root_module {
252 base = once(base)
253 .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string()))
254 .join("/");
255 }
256 base += "/";
246 257
247 let filename = get_symbol_filename(db, &target_def); 258 let filename = get_symbol_filename(db, &target_def);
248 let fragment = match definition { 259 let fragment = match definition {
249 Definition::ModuleDef(moddef) => match moddef { 260 Definition::ModuleDef(def) => match def {
250 ModuleDef::Function(f) => { 261 ModuleDef::Function(f) => {
251 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) 262 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f)))
252 } 263 }
@@ -533,6 +544,19 @@ mod tests {
533 } 544 }
534 545
535 #[test] 546 #[test]
547 fn test_doc_url_crate() {
548 check(
549 r#"
550//- /main.rs crate:main deps:test
551use test$0::Foo;
552//- /lib.rs crate:test
553pub struct Foo;
554"#,
555 expect![[r#"https://docs.rs/test/*/test/index.html"#]],
556 );
557 }
558
559 #[test]
536 fn test_doc_url_struct() { 560 fn test_doc_url_struct() {
537 check( 561 check(
538 r#" 562 r#"
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index be0ee03bf..eebae5ebe 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -3,9 +3,7 @@ use std::iter;
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::{find_node_at_offset, SyntaxRewriter}, 6 algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*,
7 ast, AstNode, NodeOrToken, SyntaxKind,
8 SyntaxKind::*,
9 SyntaxNode, WalkEvent, T, 7 SyntaxNode, WalkEvent, T,
10}; 8};
11 9
@@ -46,26 +44,23 @@ fn expand_macro_recur(
46 sema: &Semantics<RootDatabase>, 44 sema: &Semantics<RootDatabase>,
47 macro_call: &ast::MacroCall, 45 macro_call: &ast::MacroCall,
48) -> Option<SyntaxNode> { 46) -> Option<SyntaxNode> {
49 let mut expanded = sema.expand(macro_call)?; 47 let expanded = sema.expand(macro_call)?.clone_for_update();
50 48
51 let children = expanded.descendants().filter_map(ast::MacroCall::cast); 49 let children = expanded.descendants().filter_map(ast::MacroCall::cast);
52 let mut rewriter = SyntaxRewriter::default(); 50 let mut replacements = Vec::new();
53 51
54 for child in children.into_iter() { 52 for child in children {
55 if let Some(new_node) = expand_macro_recur(sema, &child) { 53 if let Some(new_node) = expand_macro_recur(sema, &child) {
56 // Replace the whole node if it is root 54 // check if the whole original syntax is replaced
57 // `replace_descendants` will not replace the parent node
58 // but `SyntaxNode::descendants include itself
59 if expanded == *child.syntax() { 55 if expanded == *child.syntax() {
60 expanded = new_node; 56 return Some(new_node);
61 } else {
62 rewriter.replace(child.syntax(), &new_node)
63 } 57 }
58 replacements.push((child, new_node));
64 } 59 }
65 } 60 }
66 61
67 let res = rewriter.rewrite(&expanded); 62 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
68 Some(res) 63 Some(expanded)
69} 64}
70 65
71// FIXME: It would also be cool to share logic here and in the mbe tests, 66// FIXME: It would also be cool to share logic here and in the mbe tests,
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index d057d5402..a04333e63 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -110,6 +110,13 @@ mod tests {
110 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); 110 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
111 } 111 }
112 112
113 fn check_unresolved(ra_fixture: &str) {
114 let (analysis, position) = fixture::position(ra_fixture);
115 let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
116
117 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
118 }
119
113 #[test] 120 #[test]
114 fn goto_def_for_extern_crate() { 121 fn goto_def_for_extern_crate() {
115 check( 122 check(
@@ -927,17 +934,12 @@ fn f() -> impl Iterator<Item$0 = u8> {}
927 } 934 }
928 935
929 #[test] 936 #[test]
930 #[should_panic = "unresolved reference"]
931 fn unknown_assoc_ty() { 937 fn unknown_assoc_ty() {
932 check( 938 check_unresolved(
933 r#" 939 r#"
934trait Iterator { 940trait Iterator { type Item; }
935 type Item;
936 //^^^^
937}
938
939fn f() -> impl Iterator<Invalid$0 = u8> {} 941fn f() -> impl Iterator<Invalid$0 = u8> {}
940 "#, 942"#,
941 ) 943 )
942 } 944 }
943 945
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 0615b26d3..99e45633e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -82,7 +82,7 @@ pub use crate::{
82 references::{rename::RenameError, ReferenceSearchResult}, 82 references::{rename::RenameError, ReferenceSearchResult},
83 runnables::{Runnable, RunnableKind, TestId}, 83 runnables::{Runnable, RunnableKind, TestId},
84 syntax_highlighting::{ 84 syntax_highlighting::{
85 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, 85 tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
86 HlRange, 86 HlRange,
87 }, 87 },
88}; 88};
@@ -244,6 +244,12 @@ impl Analysis {
244 self.with_db(|db| db.parse(file_id).tree()) 244 self.with_db(|db| db.parse(file_id).tree())
245 } 245 }
246 246
247 /// Returns true if this file belongs to an immutable library.
248 pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> {
249 use ide_db::base_db::SourceDatabaseExt;
250 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
251 }
252
247 /// Gets the file's `LineIndex`: data structure to convert between absolute 253 /// Gets the file's `LineIndex`: data structure to convert between absolute
248 /// offsets and line/column representation. 254 /// offsets and line/column representation.
249 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { 255 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> {
@@ -526,9 +532,39 @@ impl Analysis {
526 pub fn diagnostics( 532 pub fn diagnostics(
527 &self, 533 &self,
528 config: &DiagnosticsConfig, 534 config: &DiagnosticsConfig,
535 resolve: bool,
529 file_id: FileId, 536 file_id: FileId,
530 ) -> Cancelable<Vec<Diagnostic>> { 537 ) -> Cancelable<Vec<Diagnostic>> {
531 self.with_db(|db| diagnostics::diagnostics(db, config, file_id)) 538 self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id))
539 }
540
541 /// Convenience function to return assists + quick fixes for diagnostics
542 pub fn assists_with_fixes(
543 &self,
544 assist_config: &AssistConfig,
545 diagnostics_config: &DiagnosticsConfig,
546 resolve: bool,
547 frange: FileRange,
548 ) -> Cancelable<Vec<Assist>> {
549 let include_fixes = match &assist_config.allowed {
550 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
551 None => true,
552 };
553
554 self.with_db(|db| {
555 let mut res = Assist::get(db, assist_config, resolve, frange);
556 ssr::add_ssr_assist(db, &mut res, resolve, frange);
557
558 if include_fixes {
559 res.extend(
560 diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id)
561 .into_iter()
562 .filter_map(|it| it.fix)
563 .filter(|it| it.target.intersect(frange.range).is_some()),
564 );
565 }
566 res
567 })
532 } 568 }
533 569
534 /// Returns the edit required to rename reference at the position to the new 570 /// Returns the edit required to rename reference at the position to the new
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 8d37f4f92..246f10a0a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,4 +1,4 @@
1use std::iter::once; 1use std::{iter::once, mem};
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
@@ -102,7 +102,7 @@ fn move_in_direction(
102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
105 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(range, node, &match direction {
106 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
107 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
108 }?)) 108 }?))
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
125 .next(); 125 .next();
126 126
127 if let Some((l, r)) = list_lookup { 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(l.syntax(), r.syntax())) 128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else { 129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum). 130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example), 131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
@@ -134,11 +134,38 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
134 } 134 }
135} 135}
136 136
137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes<'a>(
138 range: TextRange,
139 mut first: &'a SyntaxNode,
140 mut second: &'a SyntaxNode,
141) -> TextEdit {
142 let cursor_offset = if range.is_empty() {
143 // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges
144 if first.text_range().contains_range(range) {
145 Some(range.start() - first.text_range().start())
146 } else if second.text_range().contains_range(range) {
147 mem::swap(&mut first, &mut second);
148 Some(range.start() - first.text_range().start())
149 } else {
150 None
151 }
152 } else {
153 None
154 };
155
156 let first_with_cursor = match cursor_offset {
157 Some(offset) => {
158 let mut item_text = first.text().to_string();
159 item_text.insert_str(offset.into(), "$0");
160 item_text
161 }
162 None => first.text().to_string(),
163 };
164
138 let mut edit = TextEditBuilder::default(); 165 let mut edit = TextEditBuilder::default();
139 166
140 algo::diff(first, second).into_text_edit(&mut edit); 167 algo::diff(first, second).into_text_edit(&mut edit);
141 algo::diff(second, first).into_text_edit(&mut edit); 168 edit.replace(second.text_range(), first_with_cursor);
142 169
143 edit.finish() 170 edit.finish()
144} 171}
@@ -188,7 +215,7 @@ fn main() {
188 expect![[r#" 215 expect![[r#"
189fn main() { 216fn main() {
190 match true { 217 match true {
191 false => { 218 false =>$0 {
192 println!("Test"); 219 println!("Test");
193 }, 220 },
194 true => { 221 true => {
@@ -222,7 +249,7 @@ fn main() {
222 false => { 249 false => {
223 println!("Test"); 250 println!("Test");
224 }, 251 },
225 true => { 252 true =>$0 {
226 println!("Hello, world"); 253 println!("Hello, world");
227 } 254 }
228 }; 255 };
@@ -274,7 +301,7 @@ fn main() {
274 "#, 301 "#,
275 expect![[r#" 302 expect![[r#"
276fn main() { 303fn main() {
277 let test2 = 456; 304 let test2$0 = 456;
278 let test = 123; 305 let test = 123;
279} 306}
280 "#]], 307 "#]],
@@ -293,7 +320,7 @@ fn main() {
293 "#, 320 "#,
294 expect![[r#" 321 expect![[r#"
295fn main() { 322fn main() {
296 println!("All I want to say is..."); 323 println!("All I want to say is...");$0
297 println!("Hello, world"); 324 println!("Hello, world");
298} 325}
299 "#]], 326 "#]],
@@ -313,7 +340,7 @@ fn main() {
313fn main() { 340fn main() {
314 if true { 341 if true {
315 println!("Test"); 342 println!("Test");
316 } 343 }$0
317 344
318 println!("Hello, world"); 345 println!("Hello, world");
319} 346}
@@ -334,7 +361,7 @@ fn main() {
334fn main() { 361fn main() {
335 for i in 0..10 { 362 for i in 0..10 {
336 println!("Test"); 363 println!("Test");
337 } 364 }$0
338 365
339 println!("Hello, world"); 366 println!("Hello, world");
340} 367}
@@ -355,7 +382,7 @@ fn main() {
355fn main() { 382fn main() {
356 loop { 383 loop {
357 println!("Test"); 384 println!("Test");
358 } 385 }$0
359 386
360 println!("Hello, world"); 387 println!("Hello, world");
361} 388}
@@ -376,7 +403,7 @@ fn main() {
376fn main() { 403fn main() {
377 while true { 404 while true {
378 println!("Test"); 405 println!("Test");
379 } 406 }$0
380 407
381 println!("Hello, world"); 408 println!("Hello, world");
382} 409}
@@ -393,7 +420,7 @@ fn main() {
393 "#, 420 "#,
394 expect![[r#" 421 expect![[r#"
395fn main() { 422fn main() {
396 return 123; 423 return 123;$0
397 424
398 println!("Hello, world"); 425 println!("Hello, world");
399} 426}
@@ -430,7 +457,7 @@ fn main() {}
430fn foo() {}$0$0 457fn foo() {}$0$0
431 "#, 458 "#,
432 expect![[r#" 459 expect![[r#"
433fn foo() {} 460fn foo() {}$0
434 461
435fn main() {} 462fn main() {}
436 "#]], 463 "#]],
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{}
451 expect![[r#" 478 expect![[r#"
452struct Yay; 479struct Yay;
453 480
454impl Wow for Yay {} 481impl Wow for Yay $0{}
455 482
456trait Wow {} 483trait Wow {}
457 "#]], 484 "#]],
@@ -467,7 +494,7 @@ use std::vec::Vec;
467use std::collections::HashMap$0$0; 494use std::collections::HashMap$0$0;
468 "#, 495 "#,
469 expect![[r#" 496 expect![[r#"
470use std::collections::HashMap; 497use std::collections::HashMap$0;
471use std::vec::Vec; 498use std::vec::Vec;
472 "#]], 499 "#]],
473 Direction::Up, 500 Direction::Up,
@@ -502,7 +529,7 @@ fn main() {
502 } 529 }
503 530
504 #[test] 531 #[test]
505 fn test_moves_param_up() { 532 fn test_moves_param() {
506 check( 533 check(
507 r#" 534 r#"
508fn test(one: i32, two$0$0: u32) {} 535fn test(one: i32, two$0$0: u32) {}
@@ -512,7 +539,7 @@ fn main() {
512} 539}
513 "#, 540 "#,
514 expect![[r#" 541 expect![[r#"
515fn test(two: u32, one: i32) {} 542fn test(two$0: u32, one: i32) {}
516 543
517fn main() { 544fn main() {
518 test(123, 456); 545 test(123, 456);
@@ -520,6 +547,15 @@ fn main() {
520 "#]], 547 "#]],
521 Direction::Up, 548 Direction::Up,
522 ); 549 );
550 check(
551 r#"
552fn f($0$0arg: u8, arg2: u16) {}
553 "#,
554 expect![[r#"
555fn f(arg2: u16, $0arg: u8) {}
556 "#]],
557 Direction::Down,
558 );
523 } 559 }
524 560
525 #[test] 561 #[test]
@@ -536,7 +572,7 @@ fn main() {
536fn test(one: i32, two: u32) {} 572fn test(one: i32, two: u32) {}
537 573
538fn main() { 574fn main() {
539 test(456, 123); 575 test(456$0, 123);
540} 576}
541 "#]], 577 "#]],
542 Direction::Up, 578 Direction::Up,
@@ -557,7 +593,7 @@ fn main() {
557fn test(one: i32, two: u32) {} 593fn test(one: i32, two: u32) {}
558 594
559fn main() { 595fn main() {
560 test(456, 123); 596 test(456, 123$0);
561} 597}
562 "#]], 598 "#]],
563 Direction::Down, 599 Direction::Down,
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
594fn main() {} 630fn main() {}
595 "#, 631 "#,
596 expect![[r#" 632 expect![[r#"
597struct Test<B, A>(A, B); 633struct Test<B$0, A>(A, B);
598 634
599fn main() {} 635fn main() {}
600 "#]], 636 "#]],
@@ -616,7 +652,7 @@ fn main() {
616struct Test<A, B>(A, B); 652struct Test<A, B>(A, B);
617 653
618fn main() { 654fn main() {
619 let t = Test::<&str, i32>(123, "yay"); 655 let t = Test::<&str$0, i32>(123, "yay");
620} 656}
621 "#]], 657 "#]],
622 Direction::Up, 658 Direction::Up,
@@ -636,7 +672,7 @@ fn main() {}
636 "#, 672 "#,
637 expect![[r#" 673 expect![[r#"
638enum Hello { 674enum Hello {
639 Two, 675 Two$0,
640 One 676 One
641} 677}
642 678
@@ -663,7 +699,7 @@ trait One {}
663 699
664trait Two {} 700trait Two {}
665 701
666fn test<T: Two + One>(t: T) {} 702fn test<T: Two$0 + One>(t: T) {}
667 703
668fn main() {} 704fn main() {}
669 "#]], 705 "#]],
@@ -709,7 +745,7 @@ trait Yay {
709impl Yay for Test { 745impl Yay for Test {
710 type One = i32; 746 type One = i32;
711 747
712 fn inner() { 748 fn inner() {$0
713 println!("Mmmm"); 749 println!("Mmmm");
714 } 750 }
715 751
@@ -736,7 +772,7 @@ fn test() {
736 "#, 772 "#,
737 expect![[r#" 773 expect![[r#"
738fn test() { 774fn test() {
739 mod hi { 775 mod hi {$0
740 fn inner() {} 776 fn inner() {}
741 } 777 }
742 778
@@ -764,7 +800,7 @@ fn main() {}
764 expect![[r#" 800 expect![[r#"
765fn main() {} 801fn main() {}
766 802
767#[derive(Debug)] 803$0#[derive(Debug)]
768enum FooBar { 804enum FooBar {
769 Foo, 805 Foo,
770 Bar, 806 Bar,
@@ -784,7 +820,7 @@ fn main() {}
784 expect![[r#" 820 expect![[r#"
785fn main() {} 821fn main() {}
786 822
787enum FooBar { 823$0enum FooBar {
788 Foo, 824 Foo,
789 Bar, 825 Bar,
790} 826}
@@ -804,7 +840,7 @@ fn main() {}
804 expect![[r#" 840 expect![[r#"
805struct Test; 841struct Test;
806 842
807impl SomeTrait for Test {} 843$0impl SomeTrait for Test {}
808 844
809trait SomeTrait {} 845trait SomeTrait {}
810 846
@@ -831,7 +867,7 @@ fn main() {}
831enum FooBar { 867enum FooBar {
832 Foo, 868 Foo,
833 Bar, 869 Bar,
834} 870}$0
835 "#]], 871 "#]],
836 Direction::Down, 872 Direction::Down,
837 ); 873 );
@@ -848,7 +884,7 @@ fn main() {}
848 expect![[r#" 884 expect![[r#"
849struct Test; 885struct Test;
850 886
851impl SomeTrait for Test {} 887impl SomeTrait for Test {}$0
852 888
853trait SomeTrait {} 889trait SomeTrait {}
854 890
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index ea0acfaa0..d912a01b8 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -27,6 +27,8 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
27 let topo = &graph.crates_in_topological_order(); 27 let topo = &graph.crates_in_topological_order();
28 28
29 cb(PrimeCachesProgress::Started); 29 cb(PrimeCachesProgress::Started);
30 // Take care to emit the finish signal even when the computation is canceled.
31 let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
30 32
31 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. 33 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
32 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks 34 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
@@ -41,6 +43,4 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
41 }); 43 });
42 db.crate_def_map(*krate); 44 db.crate_def_map(*krate);
43 } 45 }
44
45 cb(PrimeCachesProgress::Finished);
46} 46}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 2408a0181..175e7a31d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -50,6 +50,17 @@ pub(crate) fn prepare_rename(
50 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
51 let source_file = sema.parse(position.file_id); 51 let source_file = sema.parse(position.file_id);
52 let syntax = source_file.syntax(); 52 let syntax = source_file.syntax();
53
54 let def = find_definition(&sema, syntax, position)?;
55 match def {
56 Definition::SelfType(_) => bail!("Cannot rename `Self`"),
57 Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"),
58 _ => {}
59 };
60 let nav =
61 def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
62 nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
63
53 let name_like = sema 64 let name_like = sema
54 .find_node_at_offset_with_descend(&syntax, position.offset) 65 .find_node_at_offset_with_descend(&syntax, position.offset)
55 .ok_or_else(|| format_err!("No references found at position"))?; 66 .ok_or_else(|| format_err!("No references found at position"))?;
@@ -507,7 +518,8 @@ fn source_edit_from_def(
507 def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; 518 def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
508 519
509 let mut replacement_text = String::new(); 520 let mut replacement_text = String::new();
510 let mut repl_range = nav.focus_or_full_range(); 521 let mut repl_range =
522 nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
511 if let Definition::Local(local) = def { 523 if let Definition::Local(local) = def {
512 if let Either::Left(pat) = local.source(sema.db).value { 524 if let Either::Left(pat) = local.source(sema.db).value {
513 if matches!( 525 if matches!(
@@ -626,6 +638,49 @@ foo!(Foo$0);",
626 } 638 }
627 639
628 #[test] 640 #[test]
641 fn test_prepare_rename_tuple_field() {
642 check_prepare(
643 r#"
644struct Foo(i32);
645
646fn baz() {
647 let mut x = Foo(4);
648 x.0$0 = 5;
649}
650"#,
651 expect![[r#"No identifier available to rename"#]],
652 );
653 }
654
655 #[test]
656 fn test_prepare_rename_builtin() {
657 check_prepare(
658 r#"
659fn foo() {
660 let x: i32$0 = 0;
661}
662"#,
663 expect![[r#"Cannot rename builtin type"#]],
664 );
665 }
666
667 #[test]
668 fn test_prepare_rename_self() {
669 check_prepare(
670 r#"
671struct Foo {}
672
673impl Foo {
674 fn foo(self) -> Self$0 {
675 self
676 }
677}
678"#,
679 expect![[r#"Cannot rename `Self`"#]],
680 );
681 }
682
683 #[test]
629 fn test_rename_to_underscore() { 684 fn test_rename_to_underscore() {
630 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); 685 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
631 } 686 }
@@ -1787,4 +1842,50 @@ fn foo() {
1787"#, 1842"#,
1788 ) 1843 )
1789 } 1844 }
1845
1846 #[test]
1847 fn test_rename_tuple_field() {
1848 check(
1849 "foo",
1850 r#"
1851struct Foo(i32);
1852
1853fn baz() {
1854 let mut x = Foo(4);
1855 x.0$0 = 5;
1856}
1857"#,
1858 "error: No identifier available to rename",
1859 );
1860 }
1861
1862 #[test]
1863 fn test_rename_builtin() {
1864 check(
1865 "foo",
1866 r#"
1867fn foo() {
1868 let x: i32$0 = 0;
1869}
1870"#,
1871 "error: Cannot rename builtin type",
1872 );
1873 }
1874
1875 #[test]
1876 fn test_rename_self() {
1877 check(
1878 "foo",
1879 r#"
1880struct Foo {}
1881
1882impl Foo {
1883 fn foo(self) -> Self$0 {
1884 self
1885 }
1886}
1887"#,
1888 "error: Cannot rename `Self`",
1889 );
1890 }
1790} 1891}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 3eb9e27ee..f76715d84 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -304,11 +304,11 @@ fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Op
304 let name = adt.name(sema.db); 304 let name = adt.name(sema.db);
305 let idx = path.rfind(':').map_or(0, |idx| idx + 1); 305 let idx = path.rfind(':').map_or(0, |idx| idx + 1);
306 let (prefix, suffix) = path.split_at(idx); 306 let (prefix, suffix) = path.split_at(idx);
307 let mut ty_params = ty.type_parameters().peekable(); 307 let mut ty_args = ty.type_arguments().peekable();
308 let params = if ty_params.peek().is_some() { 308 let params = if ty_args.peek().is_some() {
309 format!( 309 format!(
310 "<{}>", 310 "<{}>",
311 ty_params.format_with(", ", |ty, cb| cb(&ty.display(sema.db))) 311 ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))
312 ) 312 )
313 } else { 313 } else {
314 String::new() 314 String::new()
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index e921784bf..b586dcc17 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -1,6 +1,6 @@
1//! Computes color for a single element. 1//! Computes color for a single element.
2 2
3use hir::{AsAssocItem, AssocItemContainer, Semantics, VariantDef}; 3use hir::{AsAssocItem, Semantics};
4use ide_db::{ 4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase, SymbolKind, 6 RootDatabase, SymbolKind,
@@ -12,7 +12,10 @@ use syntax::{
12 SyntaxNode, SyntaxToken, T, 12 SyntaxNode, SyntaxToken, T,
13}; 13};
14 14
15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag}; 15use crate::{
16 syntax_highlighting::tags::{HlOperator, HlPunct},
17 Highlight, HlMod, HlTag,
18};
16 19
17pub(super) fn element( 20pub(super) fn element(
18 sema: &Semantics<RootDatabase>, 21 sema: &Semantics<RootDatabase>,
@@ -42,28 +45,26 @@ pub(super) fn element(
42 }; 45 };
43 46
44 match name_kind { 47 match name_kind {
45 Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(), 48 Some(NameClass::ExternCrate(_)) => SymbolKind::Module.into(),
46 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 49 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
47 Some(NameClass::ConstReference(def)) => highlight_def(db, def), 50 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
48 Some(NameClass::PatFieldShorthand { field_ref, .. }) => { 51 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
49 let mut h = HlTag::Symbol(SymbolKind::Field).into(); 52 let mut h = HlTag::Symbol(SymbolKind::Field).into();
50 if let Definition::Field(field) = field_ref { 53 if let Definition::Field(field) = field_ref {
51 if let VariantDef::Union(_) = field.parent_def(db) { 54 if let hir::VariantDef::Union(_) = field.parent_def(db) {
52 h |= HlMod::Unsafe; 55 h |= HlMod::Unsafe;
53 } 56 }
54 } 57 }
55
56 h 58 h
57 } 59 }
58 None => highlight_name_by_syntax(name) | HlMod::Definition, 60 None => highlight_name_by_syntax(name) | HlMod::Definition,
59 } 61 }
60 } 62 }
61
62 // Highlight references like the definitions they resolve to 63 // Highlight references like the definitions they resolve to
63 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { 64 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
64 // even though we track whether we are in an attribute or not we still need this special case 65 // even though we track whether we are in an attribute or not we still need this special case
65 // as otherwise we would emit unresolved references for name refs inside attributes 66 // as otherwise we would emit unresolved references for name refs inside attributes
66 Highlight::from(HlTag::Symbol(SymbolKind::Function)) 67 SymbolKind::Function.into()
67 } 68 }
68 NAME_REF => { 69 NAME_REF => {
69 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 70 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
@@ -71,7 +72,7 @@ pub(super) fn element(
71 let is_self = name_ref.self_token().is_some(); 72 let is_self = name_ref.self_token().is_some();
72 let h = match NameRefClass::classify(sema, &name_ref) { 73 let h = match NameRefClass::classify(sema, &name_ref) {
73 Some(name_kind) => match name_kind { 74 Some(name_kind) => match name_kind {
74 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(), 75 NameRefClass::ExternCrate(_) => SymbolKind::Module.into(),
75 NameRefClass::Definition(def) => { 76 NameRefClass::Definition(def) => {
76 if let Definition::Local(local) = &def { 77 if let Definition::Local(local) = &def {
77 if let Some(name) = local.name(db) { 78 if let Some(name) = local.name(db) {
@@ -92,7 +93,7 @@ pub(super) fn element(
92 if let Some(parent) = name_ref.syntax().parent() { 93 if let Some(parent) = name_ref.syntax().parent() {
93 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { 94 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
94 if let Definition::Field(field) = def { 95 if let Definition::Field(field) = def {
95 if let VariantDef::Union(_) = field.parent_def(db) { 96 if let hir::VariantDef::Union(_) = field.parent_def(db) {
96 h |= HlMod::Unsafe; 97 h |= HlMod::Unsafe;
97 } 98 }
98 } 99 }
@@ -101,9 +102,7 @@ pub(super) fn element(
101 102
102 h 103 h
103 } 104 }
104 NameRefClass::FieldShorthand { .. } => { 105 NameRefClass::FieldShorthand { .. } => SymbolKind::Field.into(),
105 HlTag::Symbol(SymbolKind::Field).into()
106 }
107 }, 106 },
108 None if syntactic_name_ref_highlighting => { 107 None if syntactic_name_ref_highlighting => {
109 highlight_name_ref_by_syntax(name_ref, sema) 108 highlight_name_ref_by_syntax(name_ref, sema)
@@ -111,7 +110,7 @@ pub(super) fn element(
111 None => HlTag::UnresolvedReference.into(), 110 None => HlTag::UnresolvedReference.into(),
112 }; 111 };
113 if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self { 112 if h.tag == HlTag::Symbol(SymbolKind::Module) && is_self {
114 HlTag::Symbol(SymbolKind::SelfParam).into() 113 SymbolKind::SelfParam.into()
115 } else { 114 } else {
116 h 115 h
117 } 116 }
@@ -132,7 +131,7 @@ pub(super) fn element(
132 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), 131 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
133 BYTE => HlTag::ByteLiteral.into(), 132 BYTE => HlTag::ByteLiteral.into(),
134 CHAR => HlTag::CharLiteral.into(), 133 CHAR => HlTag::CharLiteral.into(),
135 QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, 134 QUESTION => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
136 LIFETIME => { 135 LIFETIME => {
137 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); 136 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
138 137
@@ -140,69 +139,78 @@ pub(super) fn element(
140 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition, 139 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
141 None => match NameRefClass::classify_lifetime(sema, &lifetime) { 140 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
142 Some(NameRefClass::Definition(def)) => highlight_def(db, def), 141 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
143 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)), 142 _ => SymbolKind::LifetimeParam.into(),
144 }, 143 },
145 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition, 144 _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition,
146 } 145 }
147 } 146 }
148 p if p.is_punct() => match p { 147 p if p.is_punct() => match p {
148 T![&] if parent_matches::<ast::BinExpr>(&element) => HlOperator::Bitwise.into(),
149 T![&] => { 149 T![&] => {
150 let h = HlTag::Operator.into(); 150 let h = HlTag::Operator(HlOperator::Other).into();
151 let is_unsafe = element 151 let is_unsafe = element
152 .parent() 152 .parent()
153 .and_then(ast::RefExpr::cast) 153 .and_then(ast::RefExpr::cast)
154 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) 154 .map_or(false, |ref_expr| sema.is_unsafe_ref_expr(&ref_expr));
155 .unwrap_or(false);
156 if is_unsafe { 155 if is_unsafe {
157 h | HlMod::Unsafe 156 h | HlMod::Unsafe
158 } else { 157 } else {
159 h 158 h
160 } 159 }
161 } 160 }
162 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), 161 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlOperator::Other.into(),
163 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { 162 T![!] if parent_matches::<ast::MacroCall>(&element) => SymbolKind::Macro.into(),
164 HlTag::Symbol(SymbolKind::Macro).into() 163 T![!] if parent_matches::<ast::NeverType>(&element) => HlTag::BuiltinType.into(),
165 } 164 T![!] if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Logical.into(),
166 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { 165 T![*] if parent_matches::<ast::PtrType>(&element) => HlTag::Keyword.into(),
167 HlTag::BuiltinType.into() 166 T![*] if parent_matches::<ast::PrefixExpr>(&element) => {
168 }
169 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
170 HlTag::Keyword.into()
171 }
172 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
173 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; 167 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
174 168
175 let expr = prefix_expr.expr()?; 169 let expr = prefix_expr.expr()?;
176 let ty = sema.type_of_expr(&expr)?; 170 let ty = sema.type_of_expr(&expr)?;
177 if ty.is_raw_ptr() { 171 if ty.is_raw_ptr() {
178 HlTag::Operator | HlMod::Unsafe 172 HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
179 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { 173 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
180 HlTag::Operator.into() 174 HlOperator::Other.into()
181 } else { 175 } else {
182 HlTag::Punctuation(HlPunct::Other).into() 176 HlPunct::Other.into()
183 } 177 }
184 } 178 }
185 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { 179 T![-] if parent_matches::<ast::PrefixExpr>(&element) => {
186 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; 180 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
187 181
188 let expr = prefix_expr.expr()?; 182 let expr = prefix_expr.expr()?;
189 match expr { 183 match expr {
190 ast::Expr::Literal(_) => HlTag::NumericLiteral, 184 ast::Expr::Literal(_) => HlTag::NumericLiteral,
191 _ => HlTag::Operator, 185 _ => HlTag::Operator(HlOperator::Other),
192 } 186 }
193 .into() 187 .into()
194 } 188 }
195 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { 189 _ if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Other.into(),
196 HlTag::Operator.into() 190 T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=]
191 if parent_matches::<ast::BinExpr>(&element) =>
192 {
193 HlOperator::Arithmetic.into()
194 }
195 T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=]
196 if parent_matches::<ast::BinExpr>(&element) =>
197 {
198 HlOperator::Bitwise.into()
197 } 199 }
198 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(), 200 T![&&] | T![||] if parent_matches::<ast::BinExpr>(&element) => {
199 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { 201 HlOperator::Logical.into()
200 HlTag::Operator.into()
201 } 202 }
202 _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(), 203 T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
203 _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(), 204 if parent_matches::<ast::BinExpr>(&element) =>
204 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), 205 {
205 kind => HlTag::Punctuation(match kind { 206 HlOperator::Comparison.into()
207 }
208 _ if parent_matches::<ast::BinExpr>(&element) => HlOperator::Other.into(),
209 _ if parent_matches::<ast::RangeExpr>(&element) => HlOperator::Other.into(),
210 _ if parent_matches::<ast::RangePat>(&element) => HlOperator::Other.into(),
211 _ if parent_matches::<ast::RestPat>(&element) => HlOperator::Other.into(),
212 _ if parent_matches::<ast::Attr>(&element) => HlTag::Attribute.into(),
213 kind => match kind {
206 T!['['] | T![']'] => HlPunct::Bracket, 214 T!['['] | T![']'] => HlPunct::Bracket,
207 T!['{'] | T!['}'] => HlPunct::Brace, 215 T!['{'] | T!['}'] => HlPunct::Brace,
208 T!['('] | T![')'] => HlPunct::Parenthesis, 216 T!['('] | T![')'] => HlPunct::Parenthesis,
@@ -212,22 +220,24 @@ pub(super) fn element(
212 T![;] => HlPunct::Semi, 220 T![;] => HlPunct::Semi,
213 T![.] => HlPunct::Dot, 221 T![.] => HlPunct::Dot,
214 _ => HlPunct::Other, 222 _ => HlPunct::Other,
215 }) 223 }
216 .into(), 224 .into(),
217 }, 225 },
218 226
219 k if k.is_keyword() => { 227 k if k.is_keyword() => {
220 let h = Highlight::new(HlTag::Keyword); 228 let h = Highlight::new(HlTag::Keyword);
221 match k { 229 match k {
222 T![break] 230 T![await]
231 | T![break]
223 | T![continue] 232 | T![continue]
224 | T![else] 233 | T![else]
225 | T![if] 234 | T![if]
235 | T![in]
226 | T![loop] 236 | T![loop]
227 | T![match] 237 | T![match]
228 | T![return] 238 | T![return]
229 | T![while] 239 | T![while]
230 | T![in] => h | HlMod::ControlFlow, 240 | T![yield] => h | HlMod::ControlFlow,
231 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, 241 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow,
232 T![unsafe] => h | HlMod::Unsafe, 242 T![unsafe] => h | HlMod::Unsafe,
233 T![true] | T![false] => HlTag::BoolLiteral.into(), 243 T![true] | T![false] => HlTag::BoolLiteral.into(),
@@ -266,7 +276,6 @@ pub(super) fn element(
266 hash((name, shadow_count)) 276 hash((name, shadow_count))
267 } 277 }
268} 278}
269
270fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { 279fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
271 match def { 280 match def {
272 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro), 281 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro),
@@ -277,17 +286,22 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
277 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function)); 286 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
278 if let Some(item) = func.as_assoc_item(db) { 287 if let Some(item) = func.as_assoc_item(db) {
279 h |= HlMod::Associated; 288 h |= HlMod::Associated;
280 if func.self_param(db).is_none() { 289 match func.self_param(db) {
281 h |= HlMod::Static 290 Some(sp) => {
291 if let hir::Access::Exclusive = sp.access(db) {
292 h |= HlMod::Mutable;
293 }
294 }
295 None => h |= HlMod::Static,
282 } 296 }
283 297
284 match item.container(db) { 298 match item.container(db) {
285 AssocItemContainer::Impl(i) => { 299 hir::AssocItemContainer::Impl(i) => {
286 if i.trait_(db).is_some() { 300 if i.trait_(db).is_some() {
287 h |= HlMod::Trait; 301 h |= HlMod::Trait;
288 } 302 }
289 } 303 }
290 AssocItemContainer::Trait(_t) => { 304 hir::AssocItemContainer::Trait(_t) => {
291 h |= HlMod::Trait; 305 h |= HlMod::Trait;
292 } 306 }
293 } 307 }
@@ -307,12 +321,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
307 if let Some(item) = konst.as_assoc_item(db) { 321 if let Some(item) = konst.as_assoc_item(db) {
308 h |= HlMod::Associated; 322 h |= HlMod::Associated;
309 match item.container(db) { 323 match item.container(db) {
310 AssocItemContainer::Impl(i) => { 324 hir::AssocItemContainer::Impl(i) => {
311 if i.trait_(db).is_some() { 325 if i.trait_(db).is_some() {
312 h |= HlMod::Trait; 326 h |= HlMod::Trait;
313 } 327 }
314 } 328 }
315 AssocItemContainer::Trait(_t) => { 329 hir::AssocItemContainer::Trait(_t) => {
316 h |= HlMod::Trait; 330 h |= HlMod::Trait;
317 } 331 }
318 } 332 }
@@ -326,12 +340,12 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
326 if let Some(item) = type_.as_assoc_item(db) { 340 if let Some(item) = type_.as_assoc_item(db) {
327 h |= HlMod::Associated; 341 h |= HlMod::Associated;
328 match item.container(db) { 342 match item.container(db) {
329 AssocItemContainer::Impl(i) => { 343 hir::AssocItemContainer::Impl(i) => {
330 if i.trait_(db).is_some() { 344 if i.trait_(db).is_some() {
331 h |= HlMod::Trait; 345 h |= HlMod::Trait;
332 } 346 }
333 } 347 }
334 AssocItemContainer::Trait(_t) => { 348 hir::AssocItemContainer::Trait(_t) => {
335 h |= HlMod::Trait; 349 h |= HlMod::Trait;
336 } 350 }
337 } 351 }
@@ -390,7 +404,7 @@ fn highlight_method_call(
390 method_call: &ast::MethodCallExpr, 404 method_call: &ast::MethodCallExpr,
391) -> Option<Highlight> { 405) -> Option<Highlight> {
392 let func = sema.resolve_method_call(&method_call)?; 406 let func = sema.resolve_method_call(&method_call)?;
393 let mut h = HlTag::Symbol(SymbolKind::Function).into(); 407 let mut h = SymbolKind::Function.into();
394 h |= HlMod::Associated; 408 h |= HlMod::Associated;
395 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) { 409 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
396 h |= HlMod::Unsafe; 410 h |= HlMod::Unsafe;
@@ -426,20 +440,20 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
426 }; 440 };
427 441
428 let tag = match parent.kind() { 442 let tag = match parent.kind() {
429 STRUCT => HlTag::Symbol(SymbolKind::Struct), 443 STRUCT => SymbolKind::Struct,
430 ENUM => HlTag::Symbol(SymbolKind::Enum), 444 ENUM => SymbolKind::Enum,
431 VARIANT => HlTag::Symbol(SymbolKind::Variant), 445 VARIANT => SymbolKind::Variant,
432 UNION => HlTag::Symbol(SymbolKind::Union), 446 UNION => SymbolKind::Union,
433 TRAIT => HlTag::Symbol(SymbolKind::Trait), 447 TRAIT => SymbolKind::Trait,
434 TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias), 448 TYPE_ALIAS => SymbolKind::TypeAlias,
435 TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam), 449 TYPE_PARAM => SymbolKind::TypeParam,
436 RECORD_FIELD => HlTag::Symbol(SymbolKind::Field), 450 RECORD_FIELD => SymbolKind::Field,
437 MODULE => HlTag::Symbol(SymbolKind::Module), 451 MODULE => SymbolKind::Module,
438 FN => HlTag::Symbol(SymbolKind::Function), 452 FN => SymbolKind::Function,
439 CONST => HlTag::Symbol(SymbolKind::Const), 453 CONST => SymbolKind::Const,
440 STATIC => HlTag::Symbol(SymbolKind::Static), 454 STATIC => SymbolKind::Static,
441 IDENT_PAT => HlTag::Symbol(SymbolKind::Local), 455 IDENT_PAT => SymbolKind::Local,
442 _ => default, 456 _ => return default.into(),
443 }; 457 };
444 458
445 tag.into() 459 tag.into()
@@ -457,20 +471,15 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
457 METHOD_CALL_EXPR => { 471 METHOD_CALL_EXPR => {
458 return ast::MethodCallExpr::cast(parent) 472 return ast::MethodCallExpr::cast(parent)
459 .and_then(|it| highlight_method_call(sema, &it)) 473 .and_then(|it| highlight_method_call(sema, &it))
460 .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into()); 474 .unwrap_or_else(|| SymbolKind::Function.into());
461 } 475 }
462 FIELD_EXPR => { 476 FIELD_EXPR => {
463 let h = HlTag::Symbol(SymbolKind::Field); 477 let h = HlTag::Symbol(SymbolKind::Field);
464 let is_union = ast::FieldExpr::cast(parent) 478 let is_union = ast::FieldExpr::cast(parent)
465 .and_then(|field_expr| { 479 .and_then(|field_expr| sema.resolve_field(&field_expr))
466 let field = sema.resolve_field(&field_expr)?; 480 .map_or(false, |field| {
467 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { 481 matches!(field.parent_def(sema.db), hir::VariantDef::Union(_))
468 true 482 });
469 } else {
470 false
471 })
472 })
473 .unwrap_or(false);
474 if is_union { 483 if is_union {
475 h | HlMod::Unsafe 484 h | HlMod::Unsafe
476 } else { 485 } else {
@@ -487,9 +496,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
487 _ => { 496 _ => {
488 // within path, decide whether it is module or adt by checking for uppercase name 497 // within path, decide whether it is module or adt by checking for uppercase name
489 return if name.text().chars().next().unwrap_or_default().is_uppercase() { 498 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
490 HlTag::Symbol(SymbolKind::Struct) 499 SymbolKind::Struct
491 } else { 500 } else {
492 HlTag::Symbol(SymbolKind::Module) 501 SymbolKind::Module
493 } 502 }
494 .into(); 503 .into();
495 } 504 }
@@ -500,11 +509,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
500 }; 509 };
501 510
502 match parent.kind() { 511 match parent.kind() {
503 CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(), 512 CALL_EXPR => SymbolKind::Function.into(),
504 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { 513 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
505 HlTag::Symbol(SymbolKind::Struct) 514 SymbolKind::Struct
506 } else { 515 } else {
507 HlTag::Symbol(SymbolKind::Const) 516 SymbolKind::Const
508 } 517 }
509 .into(), 518 .into(),
510 } 519 }
@@ -539,6 +548,11 @@ fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[Sy
539 kinds.len() == 0 548 kinds.len() == 0
540} 549}
541 550
551#[inline]
552fn parent_matches<N: AstNode>(element: &SyntaxElement) -> bool {
553 element.parent().map_or(false, |it| N::can_cast(it.kind()))
554}
555
542fn is_child_of_impl(element: &SyntaxElement) -> bool { 556fn is_child_of_impl(element: &SyntaxElement) -> bool {
543 match element.parent() { 557 match element.parent() {
544 Some(e) => e.kind() == IMPL, 558 Some(e) => e.kind() == IMPL,
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 04fafd244..bc221d599 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -4,7 +4,7 @@ use std::mem;
4 4
5use either::Either; 5use either::Either;
6use hir::{InFile, Semantics}; 6use hir::{InFile, Semantics};
7use ide_db::{call_info::ActiveParameter, SymbolKind}; 7use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode}, 9 ast::{self, AstNode},
10 AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, 10 AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
@@ -78,17 +78,6 @@ pub(super) fn ra_fixture(
78} 78}
79 79
80const RUSTDOC_FENCE: &'static str = "```"; 80const RUSTDOC_FENCE: &'static str = "```";
81const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
82 "",
83 "rust",
84 "should_panic",
85 "ignore",
86 "no_run",
87 "compile_fail",
88 "edition2015",
89 "edition2018",
90 "edition2021",
91];
92 81
93/// Injection of syntax highlighting of doctests. 82/// Injection of syntax highlighting of doctests.
94pub(super) fn doc_comment( 83pub(super) fn doc_comment(
@@ -174,8 +163,7 @@ pub(super) fn doc_comment(
174 is_codeblock = !is_codeblock; 163 is_codeblock = !is_codeblock;
175 // Check whether code is rust by inspecting fence guards 164 // Check whether code is rust by inspecting fence guards
176 let guards = &line[idx + RUSTDOC_FENCE.len()..]; 165 let guards = &line[idx + RUSTDOC_FENCE.len()..];
177 let is_rust = 166 let is_rust = is_rust_fence(guards);
178 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
179 is_doctest = is_codeblock && is_rust; 167 is_doctest = is_codeblock && is_rust;
180 continue; 168 continue;
181 } 169 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 1cec991aa..a304b3250 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -28,7 +28,7 @@ pub enum HlTag {
28 FormatSpecifier, 28 FormatSpecifier,
29 Keyword, 29 Keyword,
30 NumericLiteral, 30 NumericLiteral,
31 Operator, 31 Operator(HlOperator),
32 Punctuation(HlPunct), 32 Punctuation(HlPunct),
33 StringLiteral, 33 StringLiteral,
34 UnresolvedReference, 34 UnresolvedReference,
@@ -40,28 +40,33 @@ pub enum HlTag {
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)] 41#[repr(u8)]
42pub enum HlMod { 42pub enum HlMod {
43 /// Used for items in traits and impls.
44 Associated = 0,
43 /// Used to differentiate individual elements within attributes. 45 /// Used to differentiate individual elements within attributes.
44 Attribute = 0, 46 Attribute,
47 /// Callable item or value.
48 Callable,
49 /// Value that is being consumed in a function call
50 Consuming,
45 /// Used with keywords like `if` and `break`. 51 /// Used with keywords like `if` and `break`.
46 ControlFlow, 52 ControlFlow,
47 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 53 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
48 /// not. 54 /// not.
49 Definition, 55 Definition,
56 /// Doc-strings like this one.
50 Documentation, 57 Documentation,
58 /// Highlighting injection like rust code in doc strings or ra_fixture.
51 Injected, 59 Injected,
52 Mutable,
53 Consuming,
54 Callable,
55 /// Used for associated functions
56 Static,
57 /// Used for items in impls&traits.
58 Associated,
59 /// Used for intra doc links in doc injection. 60 /// Used for intra doc links in doc injection.
60 IntraDocLink, 61 IntraDocLink,
62 /// Mutable binding.
63 Mutable,
64 /// Used for associated functions.
65 Static,
61 /// Used for items in traits and trait impls. 66 /// Used for items in traits and trait impls.
62 Trait, 67 Trait,
63 68 // Keep this last!
64 /// Keep this last! 69 /// Used for unsafe functions, mutable statics, union accesses and unsafe operations.
65 Unsafe, 70 Unsafe,
66} 71}
67 72
@@ -87,6 +92,20 @@ pub enum HlPunct {
87 Other, 92 Other,
88} 93}
89 94
95#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
96pub enum HlOperator {
97 /// |, &, !, ^, |=, &=, ^=
98 Bitwise,
99 /// +, -, *, /, +=, -=, *=, /=
100 Arithmetic,
101 /// &&, ||, !
102 Logical,
103 /// >, <, ==, >=, <=, !=
104 Comparison,
105 ///
106 Other,
107}
108
90impl HlTag { 109impl HlTag {
91 fn as_str(self) -> &'static str { 110 fn as_str(self) -> &'static str {
92 match self { 111 match self {
@@ -133,7 +152,13 @@ impl HlTag {
133 HlPunct::Other => "punctuation", 152 HlPunct::Other => "punctuation",
134 }, 153 },
135 HlTag::NumericLiteral => "numeric_literal", 154 HlTag::NumericLiteral => "numeric_literal",
136 HlTag::Operator => "operator", 155 HlTag::Operator(op) => match op {
156 HlOperator::Bitwise => "bitwise",
157 HlOperator::Arithmetic => "arithmetic",
158 HlOperator::Logical => "logical",
159 HlOperator::Comparison => "comparison",
160 HlOperator::Other => "operator",
161 },
137 HlTag::StringLiteral => "string_literal", 162 HlTag::StringLiteral => "string_literal",
138 HlTag::UnresolvedReference => "unresolved_reference", 163 HlTag::UnresolvedReference => "unresolved_reference",
139 HlTag::None => "none", 164 HlTag::None => "none",
@@ -149,17 +174,17 @@ impl fmt::Display for HlTag {
149 174
150impl HlMod { 175impl HlMod {
151 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[ 176 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
177 HlMod::Associated,
152 HlMod::Attribute, 178 HlMod::Attribute,
179 HlMod::Callable,
180 HlMod::Consuming,
153 HlMod::ControlFlow, 181 HlMod::ControlFlow,
154 HlMod::Definition, 182 HlMod::Definition,
155 HlMod::Documentation, 183 HlMod::Documentation,
156 HlMod::IntraDocLink,
157 HlMod::Injected, 184 HlMod::Injected,
185 HlMod::IntraDocLink,
158 HlMod::Mutable, 186 HlMod::Mutable,
159 HlMod::Consuming,
160 HlMod::Callable,
161 HlMod::Static, 187 HlMod::Static,
162 HlMod::Associated,
163 HlMod::Trait, 188 HlMod::Trait,
164 HlMod::Unsafe, 189 HlMod::Unsafe,
165 ]; 190 ];
@@ -209,6 +234,24 @@ impl From<HlTag> for Highlight {
209 } 234 }
210} 235}
211 236
237impl From<HlOperator> for Highlight {
238 fn from(op: HlOperator) -> Highlight {
239 Highlight::new(HlTag::Operator(op))
240 }
241}
242
243impl From<HlPunct> for Highlight {
244 fn from(punct: HlPunct) -> Highlight {
245 Highlight::new(HlTag::Punctuation(punct))
246 }
247}
248
249impl From<SymbolKind> for Highlight {
250 fn from(sym: SymbolKind) -> Highlight {
251 Highlight::new(HlTag::Symbol(sym))
252 }
253}
254
212impl Highlight { 255impl Highlight {
213 pub(crate) fn new(tag: HlTag) -> Highlight { 256 pub(crate) fn new(tag: HlTag) -> Highlight {
214 Highlight { tag, mods: HlMods::default() } 257 Highlight { tag, mods: HlMods::default() }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 8cde3906c..a0ea1db34 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -42,17 +42,17 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span> 42<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span>
43 43
44<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span> 44<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration static">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
46 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 46 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
47<span class="brace">}</span> 47<span class="brace">}</span>
48 48
49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span> 49<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
50 <span class="keyword">fn</span> <span class="function declaration static associated trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword">fn</span> <span class="function associated declaration static trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51 <span class="keyword">fn</span> <span class="function declaration associated trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 51 <span class="keyword">fn</span> <span class="function associated declaration trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
52<span class="brace">}</span> 52<span class="brace">}</span>
53 53
54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span> 54<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration static trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 56 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
57<span class="brace">}</span> 57<span class="brace">}</span>
58 </code></pre> \ No newline at end of file 58 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index b6d1cac4e..638f42c2f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -50,7 +50,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
50 <span class="comment">// KILLER WHALE</span> 50 <span class="comment">// KILLER WHALE</span>
51 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span> 51 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span>
52 <span class="comment documentation">/// ```</span> 52 <span class="comment documentation">/// ```</span>
53 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span> 53 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant associated declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span>
54 54
55 <span class="comment documentation">/// Constructs a new `Foo`.</span> 55 <span class="comment documentation">/// Constructs a new `Foo`.</span>
56 <span class="comment documentation">///</span> 56 <span class="comment documentation">///</span>
@@ -60,7 +60,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
60 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span> 60 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
61 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 61 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
62 <span class="comment documentation">/// ```</span> 62 <span class="comment documentation">/// ```</span>
63 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span> 63 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function associated declaration static">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span>
64 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span> 64 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span>
65 <span class="brace">}</span> 65 <span class="brace">}</span>
66 66
@@ -76,7 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
76 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> 76 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
77 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 77 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
78 <span class="comment documentation">///</span> 78 <span class="comment documentation">///</span>
79 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span> 79 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
80 <span class="comment documentation">///</span> 80 <span class="comment documentation">///</span>
81 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span> 81 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span>
82 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 82 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
@@ -94,15 +94,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
94 <span class="comment documentation">/// ```sh</span> 94 <span class="comment documentation">/// ```sh</span>
95 <span class="comment documentation">/// echo 1</span> 95 <span class="comment documentation">/// echo 1</span>
96 <span class="comment documentation">/// ```</span> 96 <span class="comment documentation">/// ```</span>
97 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span> 97 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
98 <span class="bool_literal">true</span> 98 <span class="bool_literal">true</span>
99 <span class="brace">}</span> 99 <span class="brace">}</span>
100<span class="brace">}</span> 100<span class="brace">}</span>
101 101
102<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> 102<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span>
103<span class="comment documentation">/// This function is &gt; </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span> 103<span class="comment documentation">/// This function is &gt; </span><span class="function documentation injected intra_doc_link">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span>
104<span class="comment documentation">/// [`noop`](noop) is a macro below</span> 104<span class="comment documentation">/// [`noop`](noop) is a macro below</span>
105<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation intra_doc_link injected">[`module`]</span> 105<span class="comment documentation">/// </span><span class="struct documentation injected intra_doc_link">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation injected intra_doc_link">[`module`]</span>
106<span class="comment documentation">///</span> 106<span class="comment documentation">///</span>
107<span class="comment documentation">/// [`Item`]: module::Item</span> 107<span class="comment documentation">/// [`Item`]: module::Item</span>
108<span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span> 108<span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 7c6694a27..6202a03ce 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -42,7 +42,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 42<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span> 43 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span> 44 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
45 <span class="keyword">fn</span> <span class="function declaration static associated trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 45 <span class="keyword">fn</span> <span class="function associated declaration static trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> 46 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
47 <span class="brace">}</span> 47 <span class="brace">}</span>
48 <span class="brace">}</span><span class="string_literal">"#</span> 48 <span class="brace">}</span><span class="string_literal">"#</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 72910421d..68165bdbf 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -47,7 +47,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
47<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span> 47<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
48 48
49<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span> 49<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
50 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 50 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51<span class="brace">}</span> 51<span class="brace">}</span>
52 52
53<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span> 53<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
@@ -62,11 +62,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62<span class="brace">}</span> 62<span class="brace">}</span>
63 63
64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span> 64<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
65 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span> 65 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66<span class="brace">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span> 68<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 69 <span class="keyword">fn</span> <span class="function associated declaration trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
70<span class="brace">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> 72<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 973173254..df4192194 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -67,25 +67,25 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
67<span class="brace">}</span> 67<span class="brace">}</span>
68 68
69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span> 69<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
70 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span> 70 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
71<span class="brace">}</span> 71<span class="brace">}</span>
72 72
73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span> 73<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
74 <span class="keyword">fn</span> <span class="function declaration associated trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 74 <span class="keyword">fn</span> <span class="function associated declaration trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 75 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
76 <span class="brace">}</span> 76 <span class="brace">}</span>
77<span class="brace">}</span> 77<span class="brace">}</span>
78 78
79<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span> 79<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
80 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 80 <span class="keyword">fn</span> <span class="function associated declaration">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
81 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span> 81 <span class="value_param">f</span><span class="operator">.</span><span class="function associated consuming">baz</span><span class="parenthesis">(</span><span class="self_keyword consuming mutable">self</span><span class="parenthesis">)</span>
82 <span class="brace">}</span> 82 <span class="brace">}</span>
83 83
84 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 84 <span class="keyword">fn</span> <span class="function associated declaration mutable">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
85 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 85 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
86 <span class="brace">}</span> 86 <span class="brace">}</span>
87 87
88 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span> 88 <span class="keyword">fn</span> <span class="function associated declaration">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
89 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 89 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
90 <span class="brace">}</span> 90 <span class="brace">}</span>
91<span class="brace">}</span> 91<span class="brace">}</span>
@@ -96,15 +96,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
96<span class="brace">}</span> 96<span class="brace">}</span>
97 97
98<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span> 98<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span>
99 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 99 <span class="keyword">fn</span> <span class="function associated declaration">baz</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
100 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span> 100 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
101 <span class="brace">}</span> 101 <span class="brace">}</span>
102 102
103 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span> 103 <span class="keyword">fn</span> <span class="function associated declaration mutable">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
104 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span> 104 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
105 <span class="brace">}</span> 105 <span class="brace">}</span>
106 106
107 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span> 107 <span class="keyword">fn</span> <span class="function associated declaration">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
108 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 108 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
109 <span class="brace">}</span> 109 <span class="brace">}</span>
110<span class="brace">}</span> 110<span class="brace">}</span>
@@ -128,7 +128,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
128<span class="brace">}</span> 128<span class="brace">}</span>
129 129
130<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span> 130<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span>
131<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param declaration callable">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span> 131<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
132 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span> 132 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
133<span class="brace">}</span> 133<span class="brace">}</span>
134 134
@@ -199,21 +199,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
199 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> 199 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
200 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span> 200 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
201 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 201 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
202 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 202 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated mutable">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
203 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span> 203 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated consuming">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
204 204
205 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span> 205 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span>
206 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 206 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
207 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> 207 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated mutable">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
208 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span> 208 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span>
209 209
210 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span> 210 <span class="keyword">let</span> <span class="variable callable declaration">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
211 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span> 211 <span class="keyword">let</span> <span class="variable callable declaration">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
212 212
213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> 213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> 214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
215 215
216 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> 216 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
217 217
218 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span> 218 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span>
219 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span> 219 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span>
@@ -228,7 +228,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
228<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> 228<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
229 229
230<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span> 230<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
231 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span> 231 <span class="keyword">fn</span> <span class="function associated declaration">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
232 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span> 232 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
233 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span> 233 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
234 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span> 234 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index de2d22ac7..17cc6334b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -2,8 +2,7 @@ use std::time::Instant;
2 2
3use expect_test::{expect_file, ExpectFile}; 3use expect_test::{expect_file, ExpectFile};
4use ide_db::SymbolKind; 4use ide_db::SymbolKind;
5use stdx::format_to; 5use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
6use test_utils::{bench, bench_fixture, skip_slow_tests};
7 6
8use crate::{fixture, FileRange, HlTag, TextRange}; 7use crate::{fixture, FileRange, HlTag, TextRange};
9 8
@@ -266,90 +265,27 @@ fn syntax_highlighting_not_quadratic() {
266 return; 265 return;
267 } 266 }
268 267
269 let mut measures = Vec::new(); 268 let mut al = AssertLinear::default();
270 for i in 6..=10 { 269 while al.next_round() {
271 let n = 1 << i; 270 for i in 6..=10 {
272 let fixture = bench_fixture::big_struct_n(n); 271 let n = 1 << i;
273 let (analysis, file_id) = fixture::file(&fixture);
274 272
275 let time = Instant::now(); 273 let fixture = bench_fixture::big_struct_n(n);
274 let (analysis, file_id) = fixture::file(&fixture);
276 275
277 let hash = analysis 276 let time = Instant::now();
278 .highlight(file_id)
279 .unwrap()
280 .iter()
281 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
282 .count();
283 assert!(hash > n as usize);
284 277
285 let elapsed = time.elapsed(); 278 let hash = analysis
286 measures.push((n as f64, elapsed.as_millis() as f64)) 279 .highlight(file_id)
287 } 280 .unwrap()
281 .iter()
282 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
283 .count();
284 assert!(hash > n as usize);
288 285
289 assert_linear(&measures) 286 let elapsed = time.elapsed();
290} 287 al.sample(n as f64, elapsed.as_millis() as f64);
291
292/// Checks that a set of measurements looks like a liner function rather than
293/// like a quadratic function. Algorithm:
294///
295/// 1. Linearly scale input to be in [0; 1)
296/// 2. Using linear regression, compute the best linear function approximating
297/// the input.
298/// 3. Compute RMSE and maximal absolute error.
299/// 4. Check that errors are within tolerances and that the constant term is not
300/// too negative.
301///
302/// Ideally, we should use a proper "model selection" to directly compare
303/// quadratic and linear models, but that sounds rather complicated:
304///
305/// https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data
306fn assert_linear(xy: &[(f64, f64)]) {
307 let (mut xs, mut ys): (Vec<_>, Vec<_>) = xy.iter().copied().unzip();
308 normalize(&mut xs);
309 normalize(&mut ys);
310 let xy = xs.iter().copied().zip(ys.iter().copied());
311
312 // Linear regression: finding a and b to fit y = a + b*x.
313
314 let mean_x = mean(&xs);
315 let mean_y = mean(&ys);
316
317 let b = {
318 let mut num = 0.0;
319 let mut denom = 0.0;
320 for (x, y) in xy.clone() {
321 num += (x - mean_x) * (y - mean_y);
322 denom += (x - mean_x).powi(2);
323 } 288 }
324 num / denom
325 };
326
327 let a = mean_y - b * mean_x;
328
329 let mut plot = format!("y_pred = {:.3} + {:.3} * x\n\nx y y_pred\n", a, b);
330
331 let mut se = 0.0;
332 let mut max_error = 0.0f64;
333 for (x, y) in xy {
334 let y_pred = a + b * x;
335 se += (y - y_pred).powi(2);
336 max_error = max_error.max((y_pred - y).abs());
337
338 format_to!(plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred);
339 }
340
341 let rmse = (se / xs.len() as f64).sqrt();
342 format_to!(plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error);
343
344 assert!(rmse < 0.05 && max_error < 0.1 && a > -0.1, "\nLooks quadratic\n{}", plot);
345
346 fn normalize(xs: &mut Vec<f64>) {
347 let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
348 xs.iter_mut().for_each(|it| *it /= max);
349 }
350
351 fn mean(xs: &[f64]) -> f64 {
352 xs.iter().copied().sum::<f64>() / (xs.len() as f64)
353 } 289 }
354} 290}
355 291
@@ -371,7 +307,7 @@ fn benchmark_syntax_highlighting_parser() {
371 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) 307 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
372 .count() 308 .count()
373 }; 309 };
374 assert_eq!(hash, 1629); 310 assert_eq!(hash, 1632);
375} 311}
376 312
377#[test] 313#[test]
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 1378048e5..82c732390 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -222,8 +222,8 @@ mod tests {
222 assert_eq_text!(ra_fixture_after, &actual); 222 assert_eq_text!(ra_fixture_after, &actual);
223 } 223 }
224 224
225 fn type_char_noop(char_typed: char, before: &str) { 225 fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
226 let file_change = do_type_char(char_typed, before); 226 let file_change = do_type_char(char_typed, ra_fixture_before);
227 assert!(file_change.is_none()) 227 assert!(file_change.is_none())
228 } 228 }
229 229
@@ -240,16 +240,16 @@ mod tests {
240 // "); 240 // ");
241 type_char( 241 type_char(
242 '=', 242 '=',
243 r" 243 r#"
244fn foo() { 244fn foo() {
245 let foo $0 1 + 1 245 let foo $0 1 + 1
246} 246}
247", 247"#,
248 r" 248 r#"
249fn foo() { 249fn foo() {
250 let foo = 1 + 1; 250 let foo = 1 + 1;
251} 251}
252", 252"#,
253 ); 253 );
254 // do_check(r" 254 // do_check(r"
255 // fn foo() { 255 // fn foo() {
@@ -268,27 +268,27 @@ fn foo() {
268 fn indents_new_chain_call() { 268 fn indents_new_chain_call() {
269 type_char( 269 type_char(
270 '.', 270 '.',
271 r" 271 r#"
272 fn main() { 272fn main() {
273 xs.foo() 273 xs.foo()
274 $0 274 $0
275 } 275}
276 ", 276 "#,
277 r" 277 r#"
278 fn main() { 278fn main() {
279 xs.foo() 279 xs.foo()
280 . 280 .
281 } 281}
282 ", 282 "#,
283 ); 283 );
284 type_char_noop( 284 type_char_noop(
285 '.', 285 '.',
286 r" 286 r#"
287 fn main() { 287fn main() {
288 xs.foo() 288 xs.foo()
289 $0 289 $0
290 } 290}
291 ", 291 "#,
292 ) 292 )
293 } 293 }
294 294
@@ -297,26 +297,26 @@ fn foo() {
297 type_char( 297 type_char(
298 '.', 298 '.',
299 r" 299 r"
300 fn main() { 300fn main() {
301 xs.foo() 301 xs.foo()
302 $0; 302 $0;
303 } 303}
304 ",
305 r"
306 fn main() {
307 xs.foo()
308 .;
309 }
310 ", 304 ",
305 r#"
306fn main() {
307 xs.foo()
308 .;
309}
310 "#,
311 ); 311 );
312 type_char_noop( 312 type_char_noop(
313 '.', 313 '.',
314 r" 314 r#"
315 fn main() { 315fn main() {
316 xs.foo() 316 xs.foo()
317 $0; 317 $0;
318 } 318}
319 ", 319 "#,
320 ) 320 )
321 } 321 }
322 322
@@ -345,30 +345,30 @@ fn main() {
345 fn indents_continued_chain_call() { 345 fn indents_continued_chain_call() {
346 type_char( 346 type_char(
347 '.', 347 '.',
348 r" 348 r#"
349 fn main() { 349fn main() {
350 xs.foo() 350 xs.foo()
351 .first() 351 .first()
352 $0 352 $0
353 } 353}
354 ", 354 "#,
355 r" 355 r#"
356 fn main() { 356fn main() {
357 xs.foo() 357 xs.foo()
358 .first() 358 .first()
359 . 359 .
360 } 360}
361 ", 361 "#,
362 ); 362 );
363 type_char_noop( 363 type_char_noop(
364 '.', 364 '.',
365 r" 365 r#"
366 fn main() { 366fn main() {
367 xs.foo() 367 xs.foo()
368 .first() 368 .first()
369 $0 369 $0
370 } 370}
371 ", 371 "#,
372 ); 372 );
373 } 373 }
374 374
@@ -376,33 +376,33 @@ fn main() {
376 fn indents_middle_of_chain_call() { 376 fn indents_middle_of_chain_call() {
377 type_char( 377 type_char(
378 '.', 378 '.',
379 r" 379 r#"
380 fn source_impl() { 380fn source_impl() {
381 let var = enum_defvariant_list().unwrap() 381 let var = enum_defvariant_list().unwrap()
382 $0 382 $0
383 .nth(92) 383 .nth(92)
384 .unwrap(); 384 .unwrap();
385 } 385}
386 ", 386 "#,
387 r" 387 r#"
388 fn source_impl() { 388fn source_impl() {
389 let var = enum_defvariant_list().unwrap() 389 let var = enum_defvariant_list().unwrap()
390 . 390 .
391 .nth(92) 391 .nth(92)
392 .unwrap(); 392 .unwrap();
393 } 393}
394 ", 394 "#,
395 ); 395 );
396 type_char_noop( 396 type_char_noop(
397 '.', 397 '.',
398 r" 398 r#"
399 fn source_impl() { 399fn source_impl() {
400 let var = enum_defvariant_list().unwrap() 400 let var = enum_defvariant_list().unwrap()
401 $0 401 $0
402 .nth(92) 402 .nth(92)
403 .unwrap(); 403 .unwrap();
404 } 404}
405 ", 405 "#,
406 ); 406 );
407 } 407 }
408 408
@@ -410,25 +410,33 @@ fn main() {
410 fn dont_indent_freestanding_dot() { 410 fn dont_indent_freestanding_dot() {
411 type_char_noop( 411 type_char_noop(
412 '.', 412 '.',
413 r" 413 r#"
414 fn main() { 414fn main() {
415 $0 415 $0
416 } 416}
417 ", 417 "#,
418 ); 418 );
419 type_char_noop( 419 type_char_noop(
420 '.', 420 '.',
421 r" 421 r#"
422 fn main() { 422fn main() {
423 $0 423$0
424 } 424}
425 ", 425 "#,
426 ); 426 );
427 } 427 }
428 428
429 #[test] 429 #[test]
430 fn adds_space_after_return_type() { 430 fn adds_space_after_return_type() {
431 type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") 431 type_char(
432 '>',
433 r#"
434fn foo() -$0{ 92 }
435"#,
436 r#"
437fn foo() -> { 92 }
438"#,
439 );
432 } 440 }
433 441
434 #[test] 442 #[test]
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 6f1ce3689..7d2db201a 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -4,10 +4,11 @@
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::base_db::{FilePosition, SourceDatabase};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
7 ast::{self, AstToken}, 7 algo::find_node_at_offset,
8 ast::{self, edit::IndentLevel, AstToken},
8 AstNode, SmolStr, SourceFile, 9 AstNode, SmolStr, SourceFile,
9 SyntaxKind::*, 10 SyntaxKind::*,
10 SyntaxToken, TextRange, TextSize, TokenAtOffset, 11 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 12};
12 13
13use text_edit::TextEdit; 14use text_edit::TextEdit;
@@ -19,6 +20,7 @@ use text_edit::TextEdit;
19// - kbd:[Enter] inside triple-slash comments automatically inserts `///` 20// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
20// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` 21// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
21// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` 22// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
23// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
22// 24//
23// This action needs to be assigned to shortcut explicitly. 25// This action needs to be assigned to shortcut explicitly.
24// 26//
@@ -38,25 +40,43 @@ use text_edit::TextEdit;
38pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { 40pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
39 let parse = db.parse(position.file_id); 41 let parse = db.parse(position.file_id);
40 let file = parse.tree(); 42 let file = parse.tree();
41 let comment = file 43 let token = file.syntax().token_at_offset(position.offset).left_biased()?;
42 .syntax()
43 .token_at_offset(position.offset)
44 .left_biased()
45 .and_then(ast::Comment::cast)?;
46 44
45 if let Some(comment) = ast::Comment::cast(token.clone()) {
46 return on_enter_in_comment(&comment, &file, position.offset);
47 }
48
49 if token.kind() == L_CURLY {
50 // Typing enter after the `{` of a block expression, where the `}` is on the same line
51 if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
52 .and_then(|block| on_enter_in_block(block, position))
53 {
54 cov_mark::hit!(indent_block_contents);
55 return Some(edit);
56 }
57 }
58
59 None
60}
61
62fn on_enter_in_comment(
63 comment: &ast::Comment,
64 file: &ast::SourceFile,
65 offset: TextSize,
66) -> Option<TextEdit> {
47 if comment.kind().shape.is_block() { 67 if comment.kind().shape.is_block() {
48 return None; 68 return None;
49 } 69 }
50 70
51 let prefix = comment.prefix(); 71 let prefix = comment.prefix();
52 let comment_range = comment.syntax().text_range(); 72 let comment_range = comment.syntax().text_range();
53 if position.offset < comment_range.start() + TextSize::of(prefix) { 73 if offset < comment_range.start() + TextSize::of(prefix) {
54 return None; 74 return None;
55 } 75 }
56 76
57 let mut remove_trailing_whitespace = false; 77 let mut remove_trailing_whitespace = false;
58 // Continuing single-line non-doc comments (like this one :) ) is annoying 78 // Continuing single-line non-doc comments (like this one :) ) is annoying
59 if prefix == "//" && comment_range.end() == position.offset { 79 if prefix == "//" && comment_range.end() == offset {
60 if comment.text().ends_with(' ') { 80 if comment.text().ends_with(' ') {
61 cov_mark::hit!(continues_end_of_line_comment_with_space); 81 cov_mark::hit!(continues_end_of_line_comment_with_space);
62 remove_trailing_whitespace = true; 82 remove_trailing_whitespace = true;
@@ -70,14 +90,42 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
70 let delete = if remove_trailing_whitespace { 90 let delete = if remove_trailing_whitespace {
71 let trimmed_len = comment.text().trim_end().len() as u32; 91 let trimmed_len = comment.text().trim_end().len() as u32;
72 let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; 92 let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
73 TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset) 93 TextRange::new(offset - TextSize::from(trailing_whitespace_len), offset)
74 } else { 94 } else {
75 TextRange::empty(position.offset) 95 TextRange::empty(offset)
76 }; 96 };
77 let edit = TextEdit::replace(delete, inserted); 97 let edit = TextEdit::replace(delete, inserted);
78 Some(edit) 98 Some(edit)
79} 99}
80 100
101fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> {
102 let contents = block_contents(&block)?;
103
104 if block.syntax().text().contains_char('\n') {
105 return None;
106 }
107
108 let indent = IndentLevel::from_node(block.syntax());
109 let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
110 edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{}", indent))).ok()?;
111 Some(edit)
112}
113
114fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
115 let mut node = block.tail_expr().map(|e| e.syntax().clone());
116
117 for stmt in block.statements() {
118 if node.is_some() {
119 // More than 1 node in the block
120 return None;
121 }
122
123 node = Some(stmt.syntax().clone());
124 }
125
126 node
127}
128
81fn followed_by_comment(comment: &ast::Comment) -> bool { 129fn followed_by_comment(comment: &ast::Comment) -> bool {
82 let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) { 130 let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
83 Some(it) => it, 131 Some(it) => it,
@@ -296,4 +344,144 @@ fn main() {
296", 344",
297 ); 345 );
298 } 346 }
347
348 #[test]
349 fn indents_fn_body_block() {
350 cov_mark::check!(indent_block_contents);
351 do_check(
352 r#"
353fn f() {$0()}
354 "#,
355 r#"
356fn f() {
357 $0()
358}
359 "#,
360 );
361 }
362
363 #[test]
364 fn indents_block_expr() {
365 do_check(
366 r#"
367fn f() {
368 let x = {$0()};
369}
370 "#,
371 r#"
372fn f() {
373 let x = {
374 $0()
375 };
376}
377 "#,
378 );
379 }
380
381 #[test]
382 fn indents_match_arm() {
383 do_check(
384 r#"
385fn f() {
386 match 6 {
387 1 => {$0f()},
388 _ => (),
389 }
390}
391 "#,
392 r#"
393fn f() {
394 match 6 {
395 1 => {
396 $0f()
397 },
398 _ => (),
399 }
400}
401 "#,
402 );
403 }
404
405 #[test]
406 fn indents_block_with_statement() {
407 do_check(
408 r#"
409fn f() {$0a = b}
410 "#,
411 r#"
412fn f() {
413 $0a = b
414}
415 "#,
416 );
417 do_check(
418 r#"
419fn f() {$0fn f() {}}
420 "#,
421 r#"
422fn f() {
423 $0fn f() {}
424}
425 "#,
426 );
427 }
428
429 #[test]
430 fn indents_nested_blocks() {
431 do_check(
432 r#"
433fn f() {$0{}}
434 "#,
435 r#"
436fn f() {
437 $0{}
438}
439 "#,
440 );
441 }
442
443 #[test]
444 fn does_not_indent_empty_block() {
445 do_check_noop(
446 r#"
447fn f() {$0}
448 "#,
449 );
450 do_check_noop(
451 r#"
452fn f() {{$0}}
453 "#,
454 );
455 }
456
457 #[test]
458 fn does_not_indent_block_with_too_much_content() {
459 do_check_noop(
460 r#"
461fn f() {$0 a = b; ()}
462 "#,
463 );
464 do_check_noop(
465 r#"
466fn f() {$0 a = b; a = b; }
467 "#,
468 );
469 }
470
471 #[test]
472 fn does_not_indent_multiline_block() {
473 do_check_noop(
474 r#"
475fn f() {$0
476}
477 "#,
478 );
479 do_check_noop(
480 r#"
481fn f() {$0
482
483}
484 "#,
485 );
486 }
299} 487}