aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/display/navigation_target.rs4
-rw-r--r--crates/ide/src/display/short_label.rs10
-rw-r--r--crates/ide/src/doc_links.rs23
-rw-r--r--crates/ide/src/extend_selection.rs4
-rw-r--r--crates/ide/src/goto_definition.rs90
-rw-r--r--crates/ide/src/hover.rs30
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/runnables.rs68
-rw-r--r--crates/ide/src/status.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
12 files changed, 144 insertions, 95 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 8d08e4763..16fa828ad 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -130,8 +130,7 @@ impl NavigationTarget {
130 node: InFile<&dyn ast::NameOwner>, 130 node: InFile<&dyn ast::NameOwner>,
131 kind: SymbolKind, 131 kind: SymbolKind,
132 ) -> NavigationTarget { 132 ) -> NavigationTarget {
133 let name = 133 let name = node.value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
134 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
135 let focus_range = 134 let focus_range =
136 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range); 135 node.value.name().map(|it| node.with_value(it.syntax()).original_file_range(db).range);
137 let frange = node.map(|it| it.syntax()).original_file_range(db); 136 let frange = node.map(|it| it.syntax()).original_file_range(db);
@@ -272,6 +271,7 @@ impl ToNav for hir::Module {
272 ModuleSource::Module(node) => { 271 ModuleSource::Module(node) => {
273 (node.syntax(), node.name().map(|it| it.syntax().text_range())) 272 (node.syntax(), node.name().map(|it| it.syntax().text_range()))
274 } 273 }
274 ModuleSource::BlockExpr(node) => (node.syntax(), None),
275 }; 275 };
276 let frange = src.with_value(syntax).original_file_range(db); 276 let frange = src.with_value(syntax).original_file_range(db);
277 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module) 277 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, SymbolKind::Module)
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 990f740b8..7ac050473 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -53,6 +53,12 @@ impl ShortLabel for ast::SourceFile {
53 } 53 }
54} 54}
55 55
56impl ShortLabel for ast::BlockExpr {
57 fn short_label(&self) -> Option<String> {
58 None
59 }
60}
61
56impl ShortLabel for ast::TypeAlias { 62impl ShortLabel for ast::TypeAlias {
57 fn short_label(&self) -> Option<String> { 63 fn short_label(&self) -> Option<String> {
58 short_label_from_node(self, "type ") 64 short_label_from_node(self, "type ")
@@ -90,7 +96,7 @@ impl ShortLabel for ast::Variant {
90impl ShortLabel for ast::ConstParam { 96impl ShortLabel for ast::ConstParam {
91 fn short_label(&self) -> Option<String> { 97 fn short_label(&self) -> Option<String> {
92 let mut buf = "const ".to_owned(); 98 let mut buf = "const ".to_owned();
93 buf.push_str(self.name()?.text().as_str()); 99 buf.push_str(self.name()?.text());
94 if let Some(type_ref) = self.ty() { 100 if let Some(type_ref) = self.ty() {
95 format_to!(buf, ": {}", type_ref.syntax()); 101 format_to!(buf, ": {}", type_ref.syntax());
96 } 102 }
@@ -117,6 +123,6 @@ where
117{ 123{
118 let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default(); 124 let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
119 buf.push_str(label); 125 buf.push_str(label);
120 buf.push_str(node.name()?.text().as_str()); 126 buf.push_str(node.name()?.text());
121 Some(buf) 127 Some(buf)
122} 128}
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 1f08d7810..730e0dd0a 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -221,14 +221,31 @@ fn rewrite_intra_doc_link(
221 }?; 221 }?;
222 let krate = resolved.module(db)?.krate(); 222 let krate = resolved.module(db)?.krate();
223 let canonical_path = resolved.canonical_path(db)?; 223 let canonical_path = resolved.canonical_path(db)?;
224 let new_target = get_doc_url(db, &krate)? 224 let mut new_url = get_doc_url(db, &krate)?
225 .join(&format!("{}/", krate.display_name(db)?)) 225 .join(&format!("{}/", krate.display_name(db)?))
226 .ok()? 226 .ok()?
227 .join(&canonical_path.replace("::", "/")) 227 .join(&canonical_path.replace("::", "/"))
228 .ok()? 228 .ok()?
229 .join(&get_symbol_filename(db, &resolved)?) 229 .join(&get_symbol_filename(db, &resolved)?)
230 .ok()? 230 .ok()?;
231 .into_string(); 231
232 if let ModuleDef::Trait(t) = resolved {
233 let items = t.items(db);
234 if let Some(field_or_assoc_item) = items.iter().find_map(|assoc_item| {
235 if let Some(name) = assoc_item.name(db) {
236 if link.to_string() == format!("{}::{}", canonical_path, name) {
237 return Some(FieldOrAssocItem::AssocItem(*assoc_item));
238 }
239 }
240 None
241 }) {
242 if let Some(fragment) = get_symbol_fragment(db, &field_or_assoc_item) {
243 new_url = new_url.join(&fragment).ok()?;
244 }
245 };
246 }
247
248 let new_target = new_url.into_string();
232 let new_title = strip_prefixes_suffixes(title); 249 let new_title = strip_prefixes_suffixes(title);
233 Some((new_target, new_title.to_string())) 250 Some((new_target, new_title.to_string()))
234} 251}
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 17a540972..2d722dee0 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -213,8 +213,8 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextSize) -> TextRange
213 let ws_text = ws.text(); 213 let ws_text = ws.text();
214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start(); 214 let suffix = TextRange::new(offset, ws.text_range().end()) - ws.text_range().start();
215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start(); 215 let prefix = TextRange::new(ws.text_range().start(), offset) - ws.text_range().start();
216 let ws_suffix = &ws_text.as_str()[suffix]; 216 let ws_suffix = &ws_text[suffix];
217 let ws_prefix = &ws_text.as_str()[prefix]; 217 let ws_prefix = &ws_text[prefix];
218 if ws_text.contains('\n') && !ws_suffix.contains('\n') { 218 if ws_text.contains('\n') && !ws_suffix.contains('\n') {
219 if let Some(node) = ws.next_sibling_or_token() { 219 if let Some(node) = ws.next_sibling_or_token() {
220 let start = match ws_prefix.rfind('\n') { 220 let start = match ws_prefix.rfind('\n') {
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index a1d2bce1d..1a997fa40 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -2,16 +2,14 @@ use either::Either;
2use hir::{HasAttrs, ModuleDef, Semantics}; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 defs::{Definition, NameClass, NameRefClass}, 4 defs::{Definition, NameClass, NameRefClass},
5 symbol_index, RootDatabase, 5 RootDatabase,
6}; 6};
7use syntax::{ 7use syntax::{
8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, 8 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
9}; 9};
10 10
11use crate::{ 11use crate::{
12 display::{ToNav, TryToNav}, 12 display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def,
13 doc_links::extract_definitions_from_markdown,
14 runnables::doc_owner_to_def,
15 FilePosition, NavigationTarget, RangeInfo, 13 FilePosition, NavigationTarget, RangeInfo,
16}; 14};
17 15
@@ -38,28 +36,26 @@ pub(crate) fn goto_definition(
38 return Some(RangeInfo::new(original_token.text_range(), vec![nav])); 36 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
39 } 37 }
40 38
41 let nav_targets = match_ast! { 39 let nav = match_ast! {
42 match parent { 40 match parent {
43 ast::NameRef(name_ref) => { 41 ast::NameRef(name_ref) => {
44 reference_definition(&sema, Either::Right(&name_ref)).to_vec() 42 reference_definition(&sema, Either::Right(&name_ref))
45 }, 43 },
46 ast::Name(name) => { 44 ast::Name(name) => {
47 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 45 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
48 let nav = def.try_to_nav(sema.db)?; 46 def.try_to_nav(sema.db)
49 vec![nav]
50 }, 47 },
51 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 48 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
52 let def = name_class.referenced_or_defined(sema.db); 49 let def = name_class.referenced_or_defined(sema.db);
53 let nav = def.try_to_nav(sema.db)?; 50 def.try_to_nav(sema.db)
54 vec![nav]
55 } else { 51 } else {
56 reference_definition(&sema, Either::Left(&lt)).to_vec() 52 reference_definition(&sema, Either::Left(&lt))
57 }, 53 },
58 _ => return None, 54 _ => return None,
59 } 55 }
60 }; 56 };
61 57
62 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 58 Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect()))
63} 59}
64 60
65fn def_for_doc_comment( 61fn def_for_doc_comment(
@@ -120,42 +116,16 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
120 } 116 }
121} 117}
122 118
123#[derive(Debug)]
124pub(crate) enum ReferenceResult {
125 Exact(NavigationTarget),
126 Approximate(Vec<NavigationTarget>),
127}
128
129impl ReferenceResult {
130 fn to_vec(self) -> Vec<NavigationTarget> {
131 match self {
132 ReferenceResult::Exact(target) => vec![target],
133 ReferenceResult::Approximate(vec) => vec,
134 }
135 }
136}
137
138pub(crate) fn reference_definition( 119pub(crate) fn reference_definition(
139 sema: &Semantics<RootDatabase>, 120 sema: &Semantics<RootDatabase>,
140 name_ref: Either<&ast::Lifetime, &ast::NameRef>, 121 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
141) -> ReferenceResult { 122) -> Option<NavigationTarget> {
142 let name_kind = name_ref.either( 123 let name_kind = name_ref.either(
143 |lifetime| NameRefClass::classify_lifetime(sema, lifetime), 124 |lifetime| NameRefClass::classify_lifetime(sema, lifetime),
144 |name_ref| NameRefClass::classify(sema, name_ref), 125 |name_ref| NameRefClass::classify(sema, name_ref),
145 ); 126 )?;
146 if let Some(def) = name_kind { 127 let def = name_kind.referenced(sema.db);
147 let def = def.referenced(sema.db); 128 def.try_to_nav(sema.db)
148 return match def.try_to_nav(sema.db) {
149 Some(nav) => ReferenceResult::Exact(nav),
150 None => ReferenceResult::Approximate(Vec::new()),
151 };
152 }
153
154 // Fallback index based approach:
155 let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
156 let navs =
157 symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
158 ReferenceResult::Approximate(navs)
159} 129}
160 130
161#[cfg(test)] 131#[cfg(test)]
@@ -192,12 +162,12 @@ mod tests {
192 fn goto_def_for_extern_crate() { 162 fn goto_def_for_extern_crate() {
193 check( 163 check(
194 r#" 164 r#"
195 //- /main.rs crate:main deps:std 165//- /main.rs crate:main deps:std
196 extern crate std$0; 166extern crate std$0;
197 //- /std/lib.rs crate:std 167//- /std/lib.rs crate:std
198 // empty 168// empty
199 //^ file 169//^ file
200 "#, 170"#,
201 ) 171 )
202 } 172 }
203 173
@@ -205,12 +175,12 @@ mod tests {
205 fn goto_def_for_renamed_extern_crate() { 175 fn goto_def_for_renamed_extern_crate() {
206 check( 176 check(
207 r#" 177 r#"
208 //- /main.rs crate:main deps:std 178//- /main.rs crate:main deps:std
209 extern crate std as abc$0; 179extern crate std as abc$0;
210 //- /std/lib.rs crate:std 180//- /std/lib.rs crate:std
211 // empty 181// empty
212 //^ file 182//^ file
213 "#, 183"#,
214 ) 184 )
215 } 185 }
216 186
@@ -297,13 +267,13 @@ fn bar() {
297 fn goto_def_for_macros_from_other_crates() { 267 fn goto_def_for_macros_from_other_crates() {
298 check( 268 check(
299 r#" 269 r#"
300//- /lib.rs 270//- /lib.rs crate:main deps:foo
301use foo::foo; 271use foo::foo;
302fn bar() { 272fn bar() {
303 $0foo!(); 273 $0foo!();
304} 274}
305 275
306//- /foo/lib.rs 276//- /foo/lib.rs crate:foo
307#[macro_export] 277#[macro_export]
308macro_rules! foo { () => { () } } 278macro_rules! foo { () => { () } }
309 //^^^ 279 //^^^
@@ -315,10 +285,10 @@ macro_rules! foo { () => { () } }
315 fn goto_def_for_macros_in_use_tree() { 285 fn goto_def_for_macros_in_use_tree() {
316 check( 286 check(
317 r#" 287 r#"
318//- /lib.rs 288//- /lib.rs crate:main deps:foo
319use foo::foo$0; 289use foo::foo$0;
320 290
321//- /foo/lib.rs 291//- /foo/lib.rs crate:foo
322#[macro_export] 292#[macro_export]
323macro_rules! foo { () => { () } } 293macro_rules! foo { () => { () } }
324 //^^^ 294 //^^^
@@ -976,10 +946,10 @@ type Alias<T> = T$0;
976 fn goto_def_for_macro_container() { 946 fn goto_def_for_macro_container() {
977 check( 947 check(
978 r#" 948 r#"
979//- /lib.rs 949//- /lib.rs crate:main deps:foo
980foo::module$0::mac!(); 950foo::module$0::mac!();
981 951
982//- /foo/lib.rs 952//- /foo/lib.rs crate:foo
983pub mod module { 953pub mod module {
984 //^^^^^^ 954 //^^^^^^
985 #[macro_export] 955 #[macro_export]
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 44ebdbd35..d47a4cb0f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -321,6 +321,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
321 match it.definition_source(db).value { 321 match it.definition_source(db).value {
322 ModuleSource::Module(it) => it.short_label(), 322 ModuleSource::Module(it) => it.short_label(),
323 ModuleSource::SourceFile(it) => it.short_label(), 323 ModuleSource::SourceFile(it) => it.short_label(),
324 ModuleSource::BlockExpr(it) => it.short_label(),
324 }, 325 },
325 mod_path, 326 mod_path,
326 ), 327 ),
@@ -1825,6 +1826,35 @@ pub struct B$0ar
1825 "#]], 1826 "#]],
1826 ); 1827 );
1827 } 1828 }
1829 #[test]
1830 fn test_hover_intra_link_reference_to_trait_method() {
1831 check(
1832 r#"
1833pub trait Foo {
1834 fn buzz() -> usize;
1835}
1836/// [Foo][buzz]
1837///
1838/// [buzz]: Foo::buzz
1839pub struct B$0ar
1840"#,
1841 expect![[r#"
1842 *Bar*
1843
1844 ```rust
1845 test
1846 ```
1847
1848 ```rust
1849 pub struct Bar
1850 ```
1851
1852 ---
1853
1854 [Foo](https://docs.rs/test/*/test/trait.Foo.html#tymethod.buzz)
1855 "#]],
1856 );
1857 }
1828 1858
1829 #[test] 1859 #[test]
1830 fn test_hover_external_url() { 1860 fn test_hover_external_url() {
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index a2039fcc7..54485fd30 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -411,7 +411,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
411 match expr { 411 match expr {
412 ast::Expr::MethodCallExpr(method_call_expr) => { 412 ast::Expr::MethodCallExpr(method_call_expr) => {
413 let name_ref = method_call_expr.name_ref()?; 413 let name_ref = method_call_expr.name_ref()?;
414 match name_ref.text().as_str() { 414 match name_ref.text() {
415 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()), 415 "clone" => method_call_expr.receiver().map(|rec| rec.to_string()),
416 name_ref => Some(name_ref.to_owned()), 416 name_ref => Some(name_ref.to_owned()),
417 } 417 }
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 981467c8d..631bde0f1 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -59,7 +59,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
59 // The node is either the first or the last in the file 59 // The node is either the first or the last in the file
60 let suff = &token.text()[TextRange::new( 60 let suff = &token.text()[TextRange::new(
61 offset - token.text_range().start() + TextSize::of('\n'), 61 offset - token.text_range().start() + TextSize::of('\n'),
62 TextSize::of(token.text().as_str()), 62 TextSize::of(token.text()),
63 )]; 63 )];
64 let spaces = suff.bytes().take_while(|&b| b == b' ').count(); 64 let spaces = suff.bytes().take_while(|&b| b == b' ').count();
65 65
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index e282b31af..33170906d 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,6 +9,7 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
12 13
13use crate::{ 14use crate::{
14 display::{ToNav, TryToNav}, 15 display::{ToNav, TryToNav},
@@ -96,28 +97,26 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 97pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 98 let sema = Semantics::new(db);
98 let module = match sema.to_module_def(file_id) { 99 let module = match sema.to_module_def(file_id) {
99 None => return vec![], 100 None => return Vec::new(),
100 Some(it) => it, 101 Some(it) => it,
101 }; 102 };
102 103
103 runnables_mod(&sema, module) 104 let mut res = Vec::new();
105 runnables_mod(&sema, &mut res, module);
106 res
104} 107}
105 108
106fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> { 109fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
107 let mut res: Vec<Runnable> = module 110 acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
108 .declarations(sema.db) 111 let runnable = match def {
109 .into_iter() 112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
110 .filter_map(|def| { 113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
111 let runnable = match def { 114 _ => None,
112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it), 115 };
113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it), 116 runnable.or_else(|| module_def_doctest(&sema, def))
114 _ => None, 117 }));
115 };
116 runnable.or_else(|| module_def_doctest(&sema, def))
117 })
118 .collect();
119 118
120 res.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map( 119 acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
121 |def| match def { 120 |def| match def {
122 hir::AssocItem::Function(it) => { 121 hir::AssocItem::Function(it) => {
123 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) 122 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
@@ -127,12 +126,15 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Run
127 }, 126 },
128 )); 127 ));
129 128
130 res.extend(module.declarations(sema.db).into_iter().flat_map(|def| match def { 129 for def in module.declarations(sema.db) {
131 hir::ModuleDef::Module(it) => runnables_mod(sema, it), 130 if let hir::ModuleDef::Module(submodule) = def {
132 _ => vec![], 131 match submodule.definition_source(sema.db).value {
133 })); 132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
134 133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules),
135 res 134 hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
135 }
136 }
137 }
136} 138}
137 139
138pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 140pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -326,6 +328,7 @@ fn has_test_function_or_multiple_test_submodules(
326#[cfg(test)] 328#[cfg(test)]
327mod tests { 329mod tests {
328 use expect_test::{expect, Expect}; 330 use expect_test::{expect, Expect};
331 use test_utils::mark;
329 332
330 use crate::fixture; 333 use crate::fixture;
331 334
@@ -1050,4 +1053,25 @@ mod tests {
1050 "#]], 1053 "#]],
1051 ); 1054 );
1052 } 1055 }
1056
1057 #[test]
1058 fn dont_recurse_in_outline_submodules() {
1059 mark::check!(dont_recurse_in_outline_submodules);
1060 check(
1061 r#"
1062//- /lib.rs
1063$0
1064mod m;
1065//- /m.rs
1066mod tests {
1067 #[test]
1068 fn t() {}
1069}
1070"#,
1071 &[],
1072 expect![[r#"
1073 []
1074 "#]],
1075 );
1076 }
1053} 1077}
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs
index e10d7c3a4..137c38c0d 100644
--- a/crates/ide/src/status.rs
+++ b/crates/ide/src/status.rs
@@ -38,6 +38,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
38 format_to!(buf, "{}\n", syntax_tree_stats(db)); 38 format_to!(buf, "{}\n", syntax_tree_stats(db));
39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db)); 39 format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db));
40 format_to!(buf, "{} total\n", memory_usage()); 40 format_to!(buf, "{} total\n", memory_usage());
41 format_to!(buf, "\ncounts:\n{}", profile::countme::get_all());
41 42
42 if let Some(file_id) = file_id { 43 if let Some(file_id) = file_id {
43 format_to!(buf, "\nfile info:\n"); 44 format_to!(buf, "\nfile info:\n");
@@ -60,6 +61,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
60 None => format_to!(buf, "does not belong to any crate"), 61 None => format_to!(buf, "does not belong to any crate"),
61 } 62 }
62 } 63 }
64
63 buf 65 buf
64} 66}
65 67
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 63bff6376..8c67a0863 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -31,7 +31,7 @@ fn is_format_string(string: &ast::String) -> Option<()> {
31 let parent = string.syntax().parent(); 31 let parent = string.syntax().parent();
32 32
33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; 33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
34 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") { 34 if !matches!(name.text(), "format_args" | "format_args_nl") {
35 return None; 35 return None;
36 } 36 }
37 37
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 281461493..8cdc3688f 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -116,7 +116,7 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
116 None => (), 116 None => (),
117 } 117 }
118 118
119 let line: &str = comment.text().as_str(); 119 let line: &str = comment.text();
120 let range = comment.syntax().text_range(); 120 let range = comment.syntax().text_range();
121 121
122 let mut pos = TextSize::of(comment.prefix()); 122 let mut pos = TextSize::of(comment.prefix());