aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/diagnostics.rs43
-rw-r--r--crates/ide/src/diagnostics/fixes.rs51
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs7
-rw-r--r--crates/ide/src/inlay_hints.rs4
-rw-r--r--crates/ide/src/lib.rs39
-rw-r--r--crates/ide/src/prime_caches.rs1
-rw-r--r--crates/ide/src/runnables.rs6
-rw-r--r--crates/ide/src/ssr.rs54
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs2
10 files changed, 134 insertions, 75 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 1c911a8b2..b14f908b7 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -15,6 +15,7 @@ use hir::{
15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, 15 diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
16 InFile, Semantics, 16 InFile, Semantics,
17}; 17};
18use ide_assists::AssistResolveStrategy;
18use ide_db::{base_db::SourceDatabase, RootDatabase}; 19use ide_db::{base_db::SourceDatabase, RootDatabase};
19use itertools::Itertools; 20use itertools::Itertools;
20use rustc_hash::FxHashSet; 21use rustc_hash::FxHashSet;
@@ -84,7 +85,7 @@ pub struct DiagnosticsConfig {
84pub(crate) fn diagnostics( 85pub(crate) fn diagnostics(
85 db: &RootDatabase, 86 db: &RootDatabase,
86 config: &DiagnosticsConfig, 87 config: &DiagnosticsConfig,
87 resolve: bool, 88 resolve: &AssistResolveStrategy,
88 file_id: FileId, 89 file_id: FileId,
89) -> Vec<Diagnostic> { 90) -> Vec<Diagnostic> {
90 let _p = profile::span("diagnostics"); 91 let _p = profile::span("diagnostics");
@@ -212,7 +213,7 @@ pub(crate) fn diagnostics(
212fn diagnostic_with_fix<D: DiagnosticWithFix>( 213fn diagnostic_with_fix<D: DiagnosticWithFix>(
213 d: &D, 214 d: &D,
214 sema: &Semantics<RootDatabase>, 215 sema: &Semantics<RootDatabase>,
215 resolve: bool, 216 resolve: &AssistResolveStrategy,
216) -> Diagnostic { 217) -> Diagnostic {
217 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 218 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
218 .with_fix(d.fix(&sema, resolve)) 219 .with_fix(d.fix(&sema, resolve))
@@ -222,7 +223,7 @@ fn diagnostic_with_fix<D: DiagnosticWithFix>(
222fn warning_with_fix<D: DiagnosticWithFix>( 223fn warning_with_fix<D: DiagnosticWithFix>(
223 d: &D, 224 d: &D,
224 sema: &Semantics<RootDatabase>, 225 sema: &Semantics<RootDatabase>,
225 resolve: bool, 226 resolve: &AssistResolveStrategy,
226) -> Diagnostic { 227) -> Diagnostic {
227 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 228 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
228 .with_fix(d.fix(&sema, resolve)) 229 .with_fix(d.fix(&sema, resolve))
@@ -299,6 +300,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
299#[cfg(test)] 300#[cfg(test)]
300mod tests { 301mod tests {
301 use expect_test::{expect, Expect}; 302 use expect_test::{expect, Expect};
303 use ide_assists::AssistResolveStrategy;
302 use stdx::trim_indent; 304 use stdx::trim_indent;
303 use test_utils::assert_eq_text; 305 use test_utils::assert_eq_text;
304 306
@@ -314,7 +316,11 @@ mod tests {
314 316
315 let (analysis, file_position) = fixture::position(ra_fixture_before); 317 let (analysis, file_position) = fixture::position(ra_fixture_before);
316 let diagnostic = analysis 318 let diagnostic = analysis
317 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 319 .diagnostics(
320 &DiagnosticsConfig::default(),
321 AssistResolveStrategy::All,
322 file_position.file_id,
323 )
318 .unwrap() 324 .unwrap()
319 .pop() 325 .pop()
320 .unwrap(); 326 .unwrap();
@@ -343,7 +349,11 @@ mod tests {
343 fn check_no_fix(ra_fixture: &str) { 349 fn check_no_fix(ra_fixture: &str) {
344 let (analysis, file_position) = fixture::position(ra_fixture); 350 let (analysis, file_position) = fixture::position(ra_fixture);
345 let diagnostic = analysis 351 let diagnostic = analysis
346 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 352 .diagnostics(
353 &DiagnosticsConfig::default(),
354 AssistResolveStrategy::All,
355 file_position.file_id,
356 )
347 .unwrap() 357 .unwrap()
348 .pop() 358 .pop()
349 .unwrap(); 359 .unwrap();
@@ -357,7 +367,9 @@ mod tests {
357 let diagnostics = files 367 let diagnostics = files
358 .into_iter() 368 .into_iter()
359 .flat_map(|file_id| { 369 .flat_map(|file_id| {
360 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap() 370 analysis
371 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
372 .unwrap()
361 }) 373 })
362 .collect::<Vec<_>>(); 374 .collect::<Vec<_>>();
363 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 375 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
@@ -365,8 +377,9 @@ mod tests {
365 377
366 fn check_expect(ra_fixture: &str, expect: Expect) { 378 fn check_expect(ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = fixture::file(ra_fixture); 379 let (analysis, file_id) = fixture::file(ra_fixture);
368 let diagnostics = 380 let diagnostics = analysis
369 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); 381 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
382 .unwrap();
370 expect.assert_debug_eq(&diagnostics) 383 expect.assert_debug_eq(&diagnostics)
371 } 384 }
372 385
@@ -911,11 +924,13 @@ struct Foo {
911 924
912 let (analysis, file_id) = fixture::file(r#"mod foo;"#); 925 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
913 926
914 let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap(); 927 let diagnostics =
928 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
915 assert!(diagnostics.is_empty()); 929 assert!(diagnostics.is_empty());
916 930
917 let diagnostics = 931 let diagnostics = analysis
918 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap(); 932 .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
933 .unwrap();
919 assert!(!diagnostics.is_empty()); 934 assert!(!diagnostics.is_empty());
920 } 935 }
921 936
@@ -1022,7 +1037,11 @@ impl TestStruct {
1022 1037
1023 let (analysis, file_position) = fixture::position(input); 1038 let (analysis, file_position) = fixture::position(input);
1024 let diagnostics = analysis 1039 let diagnostics = analysis
1025 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id) 1040 .diagnostics(
1041 &DiagnosticsConfig::default(),
1042 AssistResolveStrategy::All,
1043 file_position.file_id,
1044 )
1026 .unwrap(); 1045 .unwrap();
1027 assert_eq!(diagnostics.len(), 1); 1046 assert_eq!(diagnostics.len(), 1);
1028 1047
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 7be8b3459..15821500f 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -8,6 +8,7 @@ use hir::{
8 }, 8 },
9 HasSource, HirDisplay, InFile, Semantics, VariantDef, 9 HasSource, HirDisplay, InFile, Semantics, VariantDef,
10}; 10};
11use ide_assists::AssistResolveStrategy;
11use ide_db::{ 12use ide_db::{
12 base_db::{AnchoredPathBuf, FileId}, 13 base_db::{AnchoredPathBuf, FileId},
13 source_change::{FileSystemEdit, SourceChange}, 14 source_change::{FileSystemEdit, SourceChange},
@@ -35,11 +36,19 @@ pub(crate) trait DiagnosticWithFix: Diagnostic {
35 /// 36 ///
36 /// If `resolve` is false, the edit will be computed later, on demand, and 37 /// If `resolve` is false, the edit will be computed later, on demand, and
37 /// can be omitted. 38 /// can be omitted.
38 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>; 39 fn fix(
40 &self,
41 sema: &Semantics<RootDatabase>,
42 _resolve: &AssistResolveStrategy,
43 ) -> Option<Assist>;
39} 44}
40 45
41impl DiagnosticWithFix for UnresolvedModule { 46impl DiagnosticWithFix for UnresolvedModule {
42 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 47 fn fix(
48 &self,
49 sema: &Semantics<RootDatabase>,
50 _resolve: &AssistResolveStrategy,
51 ) -> Option<Assist> {
43 let root = sema.db.parse_or_expand(self.file)?; 52 let root = sema.db.parse_or_expand(self.file)?;
44 let unresolved_module = self.decl.to_node(&root); 53 let unresolved_module = self.decl.to_node(&root);
45 Some(fix( 54 Some(fix(
@@ -59,7 +68,11 @@ impl DiagnosticWithFix for UnresolvedModule {
59} 68}
60 69
61impl DiagnosticWithFix for NoSuchField { 70impl DiagnosticWithFix for NoSuchField {
62 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 71 fn fix(
72 &self,
73 sema: &Semantics<RootDatabase>,
74 _resolve: &AssistResolveStrategy,
75 ) -> Option<Assist> {
63 let root = sema.db.parse_or_expand(self.file)?; 76 let root = sema.db.parse_or_expand(self.file)?;
64 missing_record_expr_field_fix( 77 missing_record_expr_field_fix(
65 &sema, 78 &sema,
@@ -70,7 +83,11 @@ impl DiagnosticWithFix for NoSuchField {
70} 83}
71 84
72impl DiagnosticWithFix for MissingFields { 85impl DiagnosticWithFix for MissingFields {
73 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 86 fn fix(
87 &self,
88 sema: &Semantics<RootDatabase>,
89 _resolve: &AssistResolveStrategy,
90 ) -> Option<Assist> {
74 // Note that although we could add a diagnostics to 91 // Note that although we could add a diagnostics to
75 // fill the missing tuple field, e.g : 92 // fill the missing tuple field, e.g :
76 // `struct A(usize);` 93 // `struct A(usize);`
@@ -106,7 +123,11 @@ impl DiagnosticWithFix for MissingFields {
106} 123}
107 124
108impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { 125impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
109 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 126 fn fix(
127 &self,
128 sema: &Semantics<RootDatabase>,
129 _resolve: &AssistResolveStrategy,
130 ) -> Option<Assist> {
110 let root = sema.db.parse_or_expand(self.file)?; 131 let root = sema.db.parse_or_expand(self.file)?;
111 let tail_expr = self.expr.to_node(&root); 132 let tail_expr = self.expr.to_node(&root);
112 let tail_expr_range = tail_expr.syntax().text_range(); 133 let tail_expr_range = tail_expr.syntax().text_range();
@@ -119,7 +140,11 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
119} 140}
120 141
121impl DiagnosticWithFix for RemoveThisSemicolon { 142impl DiagnosticWithFix for RemoveThisSemicolon {
122 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 143 fn fix(
144 &self,
145 sema: &Semantics<RootDatabase>,
146 _resolve: &AssistResolveStrategy,
147 ) -> Option<Assist> {
123 let root = sema.db.parse_or_expand(self.file)?; 148 let root = sema.db.parse_or_expand(self.file)?;
124 149
125 let semicolon = self 150 let semicolon = self
@@ -139,7 +164,11 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
139} 164}
140 165
141impl DiagnosticWithFix for IncorrectCase { 166impl DiagnosticWithFix for IncorrectCase {
142 fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> { 167 fn fix(
168 &self,
169 sema: &Semantics<RootDatabase>,
170 resolve: &AssistResolveStrategy,
171 ) -> Option<Assist> {
143 let root = sema.db.parse_or_expand(self.file)?; 172 let root = sema.db.parse_or_expand(self.file)?;
144 let name_node = self.ident.to_node(&root); 173 let name_node = self.ident.to_node(&root);
145 174
@@ -149,7 +178,7 @@ impl DiagnosticWithFix for IncorrectCase {
149 178
150 let label = format!("Rename to {}", self.suggested_text); 179 let label = format!("Rename to {}", self.suggested_text);
151 let mut res = unresolved_fix("change_case", &label, frange.range); 180 let mut res = unresolved_fix("change_case", &label, frange.range);
152 if resolve { 181 if resolve.should_resolve(&res.id) {
153 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text); 182 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
154 res.source_change = Some(source_change.ok().unwrap_or_default()); 183 res.source_change = Some(source_change.ok().unwrap_or_default());
155 } 184 }
@@ -159,7 +188,11 @@ impl DiagnosticWithFix for IncorrectCase {
159} 188}
160 189
161impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { 190impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
162 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 191 fn fix(
192 &self,
193 sema: &Semantics<RootDatabase>,
194 _resolve: &AssistResolveStrategy,
195 ) -> Option<Assist> {
163 let root = sema.db.parse_or_expand(self.file)?; 196 let root = sema.db.parse_or_expand(self.file)?;
164 let next_expr = self.next_expr.to_node(&root); 197 let next_expr = self.next_expr.to_node(&root);
165 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; 198 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 7d39f4fbe..93fd25dea 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -5,6 +5,7 @@ use hir::{
5 diagnostics::{Diagnostic, DiagnosticCode}, 5 diagnostics::{Diagnostic, DiagnosticCode},
6 InFile, 6 InFile,
7}; 7};
8use ide_assists::AssistResolveStrategy;
8use ide_db::{ 9use ide_db::{
9 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, 10 base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
10 source_change::SourceChange, 11 source_change::SourceChange,
@@ -50,7 +51,11 @@ impl Diagnostic for UnlinkedFile {
50} 51}
51 52
52impl DiagnosticWithFix for UnlinkedFile { 53impl DiagnosticWithFix for UnlinkedFile {
53 fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> { 54 fn fix(
55 &self,
56 sema: &hir::Semantics<RootDatabase>,
57 _resolve: &AssistResolveStrategy,
58 ) -> Option<Assist> {
54 // If there's an existing module that could add a `mod` item to include the unlinked file, 59 // If there's an existing module that could add a `mod` item to include the unlinked file,
55 // suggest that as a fix. 60 // suggest that as a fix.
56 61
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index d5ef054d8..e0bf660c4 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -218,9 +218,7 @@ fn hint_iterator(
218 ty: &hir::Type, 218 ty: &hir::Type,
219) -> Option<SmolStr> { 219) -> Option<SmolStr> {
220 let db = sema.db; 220 let db = sema.db;
221 let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) 221 let strukt = ty.strip_references().as_adt()?;
222 .last()
223 .and_then(|strukt| strukt.as_adt())?;
224 let krate = strukt.krate(db); 222 let krate = strukt.krate(db);
225 if krate != famous_defs.core()? { 223 if krate != famous_defs.core()? {
226 return None; 224 return None;
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 99e45633e..8e5b72044 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -87,7 +87,9 @@ pub use crate::{
87 }, 87 },
88}; 88};
89pub use hir::{Documentation, Semantics}; 89pub use hir::{Documentation, Semantics};
90pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; 90pub use ide_assists::{
91 Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
92};
91pub use ide_completion::{ 93pub use ide_completion::{
92 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, 94 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
93 InsertTextFormat, 95 InsertTextFormat,
@@ -518,12 +520,13 @@ impl Analysis {
518 pub fn assists( 520 pub fn assists(
519 &self, 521 &self,
520 config: &AssistConfig, 522 config: &AssistConfig,
521 resolve: bool, 523 resolve: AssistResolveStrategy,
522 frange: FileRange, 524 frange: FileRange,
523 ) -> Cancelable<Vec<Assist>> { 525 ) -> Cancelable<Vec<Assist>> {
524 self.with_db(|db| { 526 self.with_db(|db| {
527 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
525 let mut acc = Assist::get(db, config, resolve, frange); 528 let mut acc = Assist::get(db, config, resolve, frange);
526 ssr::add_ssr_assist(db, &mut acc, resolve, frange); 529 acc.extend(ssr_assists.into_iter());
527 acc 530 acc
528 }) 531 })
529 } 532 }
@@ -532,10 +535,10 @@ impl Analysis {
532 pub fn diagnostics( 535 pub fn diagnostics(
533 &self, 536 &self,
534 config: &DiagnosticsConfig, 537 config: &DiagnosticsConfig,
535 resolve: bool, 538 resolve: AssistResolveStrategy,
536 file_id: FileId, 539 file_id: FileId,
537 ) -> Cancelable<Vec<Diagnostic>> { 540 ) -> Cancelable<Vec<Diagnostic>> {
538 self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id)) 541 self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id))
539 } 542 }
540 543
541 /// Convenience function to return assists + quick fixes for diagnostics 544 /// Convenience function to return assists + quick fixes for diagnostics
@@ -543,7 +546,7 @@ impl Analysis {
543 &self, 546 &self,
544 assist_config: &AssistConfig, 547 assist_config: &AssistConfig,
545 diagnostics_config: &DiagnosticsConfig, 548 diagnostics_config: &DiagnosticsConfig,
546 resolve: bool, 549 resolve: AssistResolveStrategy,
547 frange: FileRange, 550 frange: FileRange,
548 ) -> Cancelable<Vec<Assist>> { 551 ) -> Cancelable<Vec<Assist>> {
549 let include_fixes = match &assist_config.allowed { 552 let include_fixes = match &assist_config.allowed {
@@ -552,17 +555,21 @@ impl Analysis {
552 }; 555 };
553 556
554 self.with_db(|db| { 557 self.with_db(|db| {
558 let ssr_assists = ssr::ssr_assists(db, &resolve, frange);
559 let diagnostic_assists = if include_fixes {
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 .collect()
565 } else {
566 Vec::new()
567 };
568
555 let mut res = Assist::get(db, assist_config, resolve, frange); 569 let mut res = Assist::get(db, assist_config, resolve, frange);
556 ssr::add_ssr_assist(db, &mut res, resolve, frange); 570 res.extend(ssr_assists.into_iter());
557 571 res.extend(diagnostic_assists.into_iter());
558 if include_fixes { 572
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 573 res
567 }) 574 })
568 } 575 }
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index 03597f507..d912a01b8 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -27,6 +27,7 @@ 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.
30 let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished)); 31 let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
31 32
32 // 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.
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/ssr.rs b/crates/ide/src/ssr.rs
index f3638d928..57ec80261 100644
--- a/crates/ide/src/ssr.rs
+++ b/crates/ide/src/ssr.rs
@@ -2,18 +2,23 @@
2//! assist in ide_assists because that would require the ide_assists crate 2//! assist in ide_assists because that would require the ide_assists crate
3//! depend on the ide_ssr crate. 3//! depend on the ide_ssr crate.
4 4
5use ide_assists::{Assist, AssistId, AssistKind, GroupLabel}; 5use ide_assists::{Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel};
6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; 6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
7 7
8pub(crate) fn add_ssr_assist( 8pub(crate) fn ssr_assists(
9 db: &RootDatabase, 9 db: &RootDatabase,
10 base: &mut Vec<Assist>, 10 resolve: &AssistResolveStrategy,
11 resolve: bool,
12 frange: FileRange, 11 frange: FileRange,
13) -> Option<()> { 12) -> Vec<Assist> {
14 let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?; 13 let mut ssr_assists = Vec::with_capacity(2);
15 14
16 let (source_change_for_file, source_change_for_workspace) = if resolve { 15 let (match_finder, comment_range) = match ide_ssr::ssr_from_comment(db, frange) {
16 Some(ssr_data) => ssr_data,
17 None => return ssr_assists,
18 };
19 let id = AssistId("ssr", AssistKind::RefactorRewrite);
20
21 let (source_change_for_file, source_change_for_workspace) = if resolve.should_resolve(&id) {
17 let edits = match_finder.edits(); 22 let edits = match_finder.edits();
18 23
19 let source_change_for_file = { 24 let source_change_for_file = {
@@ -35,16 +40,17 @@ pub(crate) fn add_ssr_assist(
35 40
36 for (label, source_change) in assists.into_iter() { 41 for (label, source_change) in assists.into_iter() {
37 let assist = Assist { 42 let assist = Assist {
38 id: AssistId("ssr", AssistKind::RefactorRewrite), 43 id,
39 label: Label::new(label), 44 label: Label::new(label),
40 group: Some(GroupLabel("Apply SSR".into())), 45 group: Some(GroupLabel("Apply SSR".into())),
41 target: comment_range, 46 target: comment_range,
42 source_change, 47 source_change,
43 }; 48 };
44 49
45 base.push(assist); 50 ssr_assists.push(assist);
46 } 51 }
47 Some(()) 52
53 ssr_assists
48} 54}
49 55
50#[cfg(test)] 56#[cfg(test)]
@@ -52,7 +58,7 @@ mod tests {
52 use std::sync::Arc; 58 use std::sync::Arc;
53 59
54 use expect_test::expect; 60 use expect_test::expect;
55 use ide_assists::Assist; 61 use ide_assists::{Assist, AssistResolveStrategy};
56 use ide_db::{ 62 use ide_db::{
57 base_db::{fixture::WithFixture, salsa::Durability, FileRange}, 63 base_db::{fixture::WithFixture, salsa::Durability, FileRange},
58 symbol_index::SymbolsDatabase, 64 symbol_index::SymbolsDatabase,
@@ -60,24 +66,14 @@ mod tests {
60 }; 66 };
61 use rustc_hash::FxHashSet; 67 use rustc_hash::FxHashSet;
62 68
63 use super::add_ssr_assist; 69 use super::ssr_assists;
64 70
65 fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> { 71 fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> {
66 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture); 72 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
67 let mut local_roots = FxHashSet::default(); 73 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ide_db::base_db::fixture::WORKSPACE); 74 local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 75 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70 76 ssr_assists(&db, &resolve, FileRange { file_id, range: range_or_offset.into() })
71 let mut assists = vec![];
72
73 add_ssr_assist(
74 &db,
75 &mut assists,
76 resolve,
77 FileRange { file_id, range: range_or_offset.into() },
78 );
79
80 assists
81 } 77 }
82 78
83 #[test] 79 #[test]
@@ -88,16 +84,13 @@ mod tests {
88 // This is foo $0 84 // This is foo $0
89 fn foo() {} 85 fn foo() {}
90 "#; 86 "#;
91 let resolve = true; 87 let assists = get_assists(ra_fixture, AssistResolveStrategy::All);
92
93 let assists = get_assists(ra_fixture, resolve);
94 88
95 assert_eq!(0, assists.len()); 89 assert_eq!(0, assists.len());
96 } 90 }
97 91
98 #[test] 92 #[test]
99 fn resolve_edits_true() { 93 fn resolve_edits_true() {
100 let resolve = true;
101 let assists = get_assists( 94 let assists = get_assists(
102 r#" 95 r#"
103 //- /lib.rs 96 //- /lib.rs
@@ -109,7 +102,7 @@ mod tests {
109 //- /bar.rs 102 //- /bar.rs
110 fn bar() { 2 } 103 fn bar() { 2 }
111 "#, 104 "#,
112 resolve, 105 AssistResolveStrategy::All,
113 ); 106 );
114 107
115 assert_eq!(2, assists.len()); 108 assert_eq!(2, assists.len());
@@ -200,7 +193,6 @@ mod tests {
200 193
201 #[test] 194 #[test]
202 fn resolve_edits_false() { 195 fn resolve_edits_false() {
203 let resolve = false;
204 let assists = get_assists( 196 let assists = get_assists(
205 r#" 197 r#"
206 //- /lib.rs 198 //- /lib.rs
@@ -212,7 +204,7 @@ mod tests {
212 //- /bar.rs 204 //- /bar.rs
213 fn bar() { 2 } 205 fn bar() { 2 }
214 "#, 206 "#,
215 resolve, 207 AssistResolveStrategy::None,
216 ); 208 );
217 209
218 assert_eq!(2, assists.len()); 210 assert_eq!(2, assists.len());
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 638f42c2f..8d83ba206 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -142,6 +142,7 @@ It is beyond me why you'd use these when you got ///
142```rust 142```rust
143</span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> 143</span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
144``` 144```
145</span><span class="function documentation injected intra_doc_link">[`block_comments2`]</span><span class="comment documentation"> tests these with indentation
145 */</span> 146 */</span>
146<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 147<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
147 148
@@ -150,5 +151,6 @@ It is beyond me why you'd use these when you got ///
150 ```rust 151 ```rust
151</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation"> 152</span><span class="comment documentation"> </span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="comment documentation">
152 ``` 153 ```
154 </span><span class="function documentation injected intra_doc_link">[`block_comments`]</span><span class="comment documentation"> tests these without indentation
153*/</span> 155*/</span>
154<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file 156<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">block_comments2</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 17cc6334b..b6e952b08 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -618,6 +618,7 @@ It is beyond me why you'd use these when you got ///
618```rust 618```rust
619let _ = example(&[1, 2, 3]); 619let _ = example(&[1, 2, 3]);
620``` 620```
621[`block_comments2`] tests these with indentation
621 */ 622 */
622pub fn block_comments() {} 623pub fn block_comments() {}
623 624
@@ -626,6 +627,7 @@ pub fn block_comments() {}
626 ```rust 627 ```rust
627 let _ = example(&[1, 2, 3]); 628 let _ = example(&[1, 2, 3]);
628 ``` 629 ```
630 [`block_comments`] tests these without indentation
629*/ 631*/
630pub fn block_comments2() {} 632pub fn block_comments2() {}
631"# 633"#