diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/diagnostics.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 63 | ||||
-rw-r--r-- | crates/ide/src/folding_ranges.rs | 39 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 15 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/move_item.rs | 268 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 32 |
7 files changed, 345 insertions, 74 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 22697a537..dd42116a7 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -165,7 +165,6 @@ pub(crate) fn diagnostics( | |||
165 | sema.diagnostics_display_range(d.display_source()).range, | 165 | sema.diagnostics_display_range(d.display_source()).range, |
166 | d.message(), | 166 | d.message(), |
167 | ) | 167 | ) |
168 | .with_unused(true) | ||
169 | .with_fix(d.fix(&sema)) | 168 | .with_fix(d.fix(&sema)) |
170 | .with_code(Some(d.code())), | 169 | .with_code(Some(d.code())), |
171 | ); | 170 | ); |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 4a9e8b21c..67e2e5a1c 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -98,6 +98,29 @@ pub(crate) fn remove_links(markdown: &str) -> String { | |||
98 | out | 98 | out |
99 | } | 99 | } |
100 | 100 | ||
101 | /// Retrieve a link to documentation for the given symbol. | ||
102 | pub(crate) fn external_docs( | ||
103 | db: &RootDatabase, | ||
104 | position: &FilePosition, | ||
105 | ) -> Option<DocumentationLink> { | ||
106 | let sema = Semantics::new(db); | ||
107 | let file = sema.parse(position.file_id).syntax().clone(); | ||
108 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
109 | let token = sema.descend_into_macros(token); | ||
110 | |||
111 | let node = token.parent()?; | ||
112 | let definition = match_ast! { | ||
113 | match node { | ||
114 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | ||
115 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | ||
116 | _ => None, | ||
117 | } | ||
118 | }; | ||
119 | |||
120 | get_doc_link(db, definition?) | ||
121 | } | ||
122 | |||
123 | /// Extracts all links from a given markdown text. | ||
101 | pub(crate) fn extract_definitions_from_markdown( | 124 | pub(crate) fn extract_definitions_from_markdown( |
102 | markdown: &str, | 125 | markdown: &str, |
103 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { | 126 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { |
@@ -178,15 +201,15 @@ pub(crate) fn resolve_doc_path_for_def( | |||
178 | ) -> Option<hir::ModuleDef> { | 201 | ) -> Option<hir::ModuleDef> { |
179 | match def { | 202 | match def { |
180 | Definition::ModuleDef(def) => match def { | 203 | Definition::ModuleDef(def) => match def { |
181 | ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | 204 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), |
182 | ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | 205 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), |
183 | ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | 206 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), |
184 | ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | 207 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), |
185 | ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | 208 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), |
186 | ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | 209 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), |
187 | ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | 210 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), |
188 | ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | 211 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), |
189 | ModuleDef::BuiltinType(_) => None, | 212 | hir::ModuleDef::BuiltinType(_) => None, |
190 | }, | 213 | }, |
191 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | 214 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), |
192 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | 215 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), |
@@ -328,28 +351,6 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S | |||
328 | .map(|url| url.into_string()) | 351 | .map(|url| url.into_string()) |
329 | } | 352 | } |
330 | 353 | ||
331 | /// Retrieve a link to documentation for the given symbol. | ||
332 | pub(crate) fn external_docs( | ||
333 | db: &RootDatabase, | ||
334 | position: &FilePosition, | ||
335 | ) -> Option<DocumentationLink> { | ||
336 | let sema = Semantics::new(db); | ||
337 | let file = sema.parse(position.file_id).syntax().clone(); | ||
338 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
339 | let token = sema.descend_into_macros(token); | ||
340 | |||
341 | let node = token.parent()?; | ||
342 | let definition = match_ast! { | ||
343 | match node { | ||
344 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | ||
345 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | ||
346 | _ => None, | ||
347 | } | ||
348 | }; | ||
349 | |||
350 | get_doc_link(db, definition?) | ||
351 | } | ||
352 | |||
353 | /// Rewrites a markdown document, applying 'callback' to each link. | 354 | /// Rewrites a markdown document, applying 'callback' to each link. |
354 | fn map_links<'e>( | 355 | fn map_links<'e>( |
355 | events: impl Iterator<Item = Event<'e>>, | 356 | events: impl Iterator<Item = Event<'e>>, |
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs index 4b1b24562..153726ce8 100644 --- a/crates/ide/src/folding_ranges.rs +++ b/crates/ide/src/folding_ranges.rs | |||
@@ -17,6 +17,8 @@ pub enum FoldKind { | |||
17 | Block, | 17 | Block, |
18 | ArgList, | 18 | ArgList, |
19 | Region, | 19 | Region, |
20 | Consts, | ||
21 | Statics, | ||
20 | } | 22 | } |
21 | 23 | ||
22 | #[derive(Debug)] | 24 | #[derive(Debug)] |
@@ -30,6 +32,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { | |||
30 | let mut visited_comments = FxHashSet::default(); | 32 | let mut visited_comments = FxHashSet::default(); |
31 | let mut visited_imports = FxHashSet::default(); | 33 | let mut visited_imports = FxHashSet::default(); |
32 | let mut visited_mods = FxHashSet::default(); | 34 | let mut visited_mods = FxHashSet::default(); |
35 | let mut visited_consts = FxHashSet::default(); | ||
36 | let mut visited_statics = FxHashSet::default(); | ||
33 | // regions can be nested, here is a LIFO buffer | 37 | // regions can be nested, here is a LIFO buffer |
34 | let mut regions_starts: Vec<TextSize> = vec![]; | 38 | let mut regions_starts: Vec<TextSize> = vec![]; |
35 | 39 | ||
@@ -91,6 +95,19 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> { | |||
91 | res.push(Fold { range, kind: FoldKind::Mods }) | 95 | res.push(Fold { range, kind: FoldKind::Mods }) |
92 | } | 96 | } |
93 | } | 97 | } |
98 | |||
99 | // Fold groups of consts | ||
100 | if node.kind() == CONST && !visited_consts.contains(&node) { | ||
101 | if let Some(range) = contiguous_range_for_group(&node, &mut visited_consts) { | ||
102 | res.push(Fold { range, kind: FoldKind::Consts }) | ||
103 | } | ||
104 | } | ||
105 | // Fold groups of consts | ||
106 | if node.kind() == STATIC && !visited_statics.contains(&node) { | ||
107 | if let Some(range) = contiguous_range_for_group(&node, &mut visited_statics) { | ||
108 | res.push(Fold { range, kind: FoldKind::Statics }) | ||
109 | } | ||
110 | } | ||
94 | } | 111 | } |
95 | } | 112 | } |
96 | } | 113 | } |
@@ -250,6 +267,8 @@ mod tests { | |||
250 | FoldKind::Block => "block", | 267 | FoldKind::Block => "block", |
251 | FoldKind::ArgList => "arglist", | 268 | FoldKind::ArgList => "arglist", |
252 | FoldKind::Region => "region", | 269 | FoldKind::Region => "region", |
270 | FoldKind::Consts => "consts", | ||
271 | FoldKind::Statics => "statics", | ||
253 | }; | 272 | }; |
254 | assert_eq!(kind, &attr.unwrap()); | 273 | assert_eq!(kind, &attr.unwrap()); |
255 | } | 274 | } |
@@ -457,4 +476,24 @@ calling_function(x,y); | |||
457 | "#, | 476 | "#, |
458 | ) | 477 | ) |
459 | } | 478 | } |
479 | |||
480 | #[test] | ||
481 | fn fold_consecutive_const() { | ||
482 | check( | ||
483 | r#" | ||
484 | <fold consts>const FIRST_CONST: &str = "first"; | ||
485 | const SECOND_CONST: &str = "second";</fold> | ||
486 | "#, | ||
487 | ) | ||
488 | } | ||
489 | |||
490 | #[test] | ||
491 | fn fold_consecutive_static() { | ||
492 | check( | ||
493 | r#" | ||
494 | <fold statics>static FIRST_STATIC: &str = "first"; | ||
495 | static SECOND_STATIC: &str = "second";</fold> | ||
496 | "#, | ||
497 | ) | ||
498 | } | ||
460 | } | 499 | } |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a2c97061f..c6556c487 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -918,6 +918,21 @@ fn f() -> impl Iterator<Item$0 = u8> {} | |||
918 | } | 918 | } |
919 | 919 | ||
920 | #[test] | 920 | #[test] |
921 | #[should_panic = "unresolved reference"] | ||
922 | fn unknown_assoc_ty() { | ||
923 | check( | ||
924 | r#" | ||
925 | trait Iterator { | ||
926 | type Item; | ||
927 | //^^^^ | ||
928 | } | ||
929 | |||
930 | fn f() -> impl Iterator<Invalid$0 = u8> {} | ||
931 | "#, | ||
932 | ) | ||
933 | } | ||
934 | |||
935 | #[test] | ||
921 | fn goto_def_for_assoc_ty_in_path_multiple() { | 936 | fn goto_def_for_assoc_ty_in_path_multiple() { |
922 | check( | 937 | check( |
923 | r#" | 938 | r#" |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index dfd32c8c1..7e35a1450 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -470,6 +470,7 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module> | |||
470 | 470 | ||
471 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 471 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
472 | return tokens.max_by_key(priority); | 472 | return tokens.max_by_key(priority); |
473 | |||
473 | fn priority(n: &SyntaxToken) -> usize { | 474 | fn priority(n: &SyntaxToken) -> usize { |
474 | match n.kind() { | 475 | match n.kind() { |
475 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3, | 476 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3, |
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 05fa8fc13..d36dcd4e4 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs | |||
@@ -4,10 +4,12 @@ use hir::Semantics; | |||
4 | use ide_db::{base_db::FileRange, RootDatabase}; | 4 | use ide_db::{base_db::FileRange, RootDatabase}; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | algo, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, | 7 | algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, |
8 | TokenAtOffset, | ||
8 | }; | 9 | }; |
9 | use text_edit::{TextEdit, TextEditBuilder}; | 10 | use text_edit::{TextEdit, TextEditBuilder}; |
10 | 11 | ||
12 | #[derive(Copy, Clone, Debug)] | ||
11 | pub enum Direction { | 13 | pub enum Direction { |
12 | Up, | 14 | Up, |
13 | Down, | 15 | Down, |
@@ -31,14 +33,19 @@ pub(crate) fn move_item( | |||
31 | let sema = Semantics::new(db); | 33 | let sema = Semantics::new(db); |
32 | let file = sema.parse(range.file_id); | 34 | let file = sema.parse(range.file_id); |
33 | 35 | ||
34 | let item = file.syntax().covering_element(range.range); | 36 | let item = if range.range.is_empty() { |
37 | SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?) | ||
38 | } else { | ||
39 | file.syntax().covering_element(range.range) | ||
40 | }; | ||
41 | |||
35 | find_ancestors(item, direction, range.range) | 42 | find_ancestors(item, direction, range.range) |
36 | } | 43 | } |
37 | 44 | ||
38 | fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> { | 45 | fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -> Option<TextEdit> { |
39 | let root = match item { | 46 | let root = match item { |
40 | NodeOrToken::Node(node) => node, | 47 | SyntaxElement::Node(node) => node, |
41 | NodeOrToken::Token(token) => token.parent()?, | 48 | SyntaxElement::Token(token) => token.parent()?, |
42 | }; | 49 | }; |
43 | 50 | ||
44 | let movable = [ | 51 | let movable = [ |
@@ -51,6 +58,11 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - | |||
51 | SyntaxKind::PARAM, | 58 | SyntaxKind::PARAM, |
52 | SyntaxKind::LET_STMT, | 59 | SyntaxKind::LET_STMT, |
53 | SyntaxKind::EXPR_STMT, | 60 | SyntaxKind::EXPR_STMT, |
61 | SyntaxKind::IF_EXPR, | ||
62 | SyntaxKind::FOR_EXPR, | ||
63 | SyntaxKind::LOOP_EXPR, | ||
64 | SyntaxKind::WHILE_EXPR, | ||
65 | SyntaxKind::RETURN_EXPR, | ||
54 | SyntaxKind::MATCH_EXPR, | 66 | SyntaxKind::MATCH_EXPR, |
55 | SyntaxKind::MACRO_CALL, | 67 | SyntaxKind::MACRO_CALL, |
56 | SyntaxKind::TYPE_ALIAS, | 68 | SyntaxKind::TYPE_ALIAS, |
@@ -83,11 +95,11 @@ fn move_in_direction( | |||
83 | ) -> Option<TextEdit> { | 95 | ) -> Option<TextEdit> { |
84 | match_ast! { | 96 | match_ast! { |
85 | match node { | 97 | match node { |
86 | ast::ArgList(it) => swap_sibling_in_list(it.args(), range, direction), | 98 | ast::ArgList(it) => swap_sibling_in_list(node, it.args(), range, direction), |
87 | ast::GenericParamList(it) => swap_sibling_in_list(it.generic_params(), range, direction), | 99 | ast::GenericParamList(it) => swap_sibling_in_list(node, it.generic_params(), range, direction), |
88 | ast::GenericArgList(it) => swap_sibling_in_list(it.generic_args(), range, direction), | 100 | ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), |
89 | ast::VariantList(it) => swap_sibling_in_list(it.variants(), range, direction), | 101 | ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), |
90 | ast::TypeBoundList(it) => swap_sibling_in_list(it.bounds(), range, direction), | 102 | ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), |
91 | _ => Some(replace_nodes(node, &match direction { | 103 | _ => Some(replace_nodes(node, &match direction { |
92 | Direction::Up => node.prev_sibling(), | 104 | Direction::Up => node.prev_sibling(), |
93 | Direction::Down => node.next_sibling(), | 105 | Direction::Down => node.next_sibling(), |
@@ -97,19 +109,27 @@ fn move_in_direction( | |||
97 | } | 109 | } |
98 | 110 | ||
99 | fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( | 111 | fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>( |
112 | node: &SyntaxNode, | ||
100 | list: I, | 113 | list: I, |
101 | range: TextRange, | 114 | range: TextRange, |
102 | direction: Direction, | 115 | direction: Direction, |
103 | ) -> Option<TextEdit> { | 116 | ) -> Option<TextEdit> { |
104 | let (l, r) = list | 117 | let list_lookup = list |
105 | .tuple_windows() | 118 | .tuple_windows() |
106 | .filter(|(l, r)| match direction { | 119 | .filter(|(l, r)| match direction { |
107 | Direction::Up => r.syntax().text_range().contains_range(range), | 120 | Direction::Up => r.syntax().text_range().contains_range(range), |
108 | Direction::Down => l.syntax().text_range().contains_range(range), | 121 | Direction::Down => l.syntax().text_range().contains_range(range), |
109 | }) | 122 | }) |
110 | .next()?; | 123 | .next(); |
111 | 124 | ||
112 | Some(replace_nodes(l.syntax(), r.syntax())) | 125 | if let Some((l, r)) = list_lookup { |
126 | Some(replace_nodes(l.syntax(), r.syntax())) | ||
127 | } else { | ||
128 | // Cursor is beyond any movable list item (for example, on curly brace in enum). | ||
129 | // It's not necessary, that parent of list is movable (arg list's parent is not, for example), | ||
130 | // and we have to continue tree traversal to find suitable node. | ||
131 | find_ancestors(SyntaxElement::Node(node.parent()?), direction, range) | ||
132 | } | ||
113 | } | 133 | } |
114 | 134 | ||
115 | fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { | 135 | fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { |
@@ -121,6 +141,18 @@ fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { | |||
121 | edit.finish() | 141 | edit.finish() |
122 | } | 142 | } |
123 | 143 | ||
144 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | ||
145 | return tokens.max_by_key(priority); | ||
146 | |||
147 | fn priority(n: &SyntaxToken) -> usize { | ||
148 | match n.kind() { | ||
149 | SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2, | ||
150 | kind if kind.is_trivia() => 0, | ||
151 | _ => 1, | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
124 | #[cfg(test)] | 156 | #[cfg(test)] |
125 | mod tests { | 157 | mod tests { |
126 | use crate::fixture; | 158 | use crate::fixture; |
@@ -265,6 +297,107 @@ fn main() { | |||
265 | "#]], | 297 | "#]], |
266 | Direction::Up, | 298 | Direction::Up, |
267 | ); | 299 | ); |
300 | check( | ||
301 | r#" | ||
302 | fn main() { | ||
303 | println!("Hello, world"); | ||
304 | |||
305 | if true { | ||
306 | println!("Test"); | ||
307 | }$0$0 | ||
308 | } | ||
309 | "#, | ||
310 | expect![[r#" | ||
311 | fn main() { | ||
312 | if true { | ||
313 | println!("Test"); | ||
314 | } | ||
315 | |||
316 | println!("Hello, world"); | ||
317 | } | ||
318 | "#]], | ||
319 | Direction::Up, | ||
320 | ); | ||
321 | check( | ||
322 | r#" | ||
323 | fn main() { | ||
324 | println!("Hello, world"); | ||
325 | |||
326 | for i in 0..10 { | ||
327 | println!("Test"); | ||
328 | }$0$0 | ||
329 | } | ||
330 | "#, | ||
331 | expect![[r#" | ||
332 | fn main() { | ||
333 | for i in 0..10 { | ||
334 | println!("Test"); | ||
335 | } | ||
336 | |||
337 | println!("Hello, world"); | ||
338 | } | ||
339 | "#]], | ||
340 | Direction::Up, | ||
341 | ); | ||
342 | check( | ||
343 | r#" | ||
344 | fn main() { | ||
345 | println!("Hello, world"); | ||
346 | |||
347 | loop { | ||
348 | println!("Test"); | ||
349 | }$0$0 | ||
350 | } | ||
351 | "#, | ||
352 | expect![[r#" | ||
353 | fn main() { | ||
354 | loop { | ||
355 | println!("Test"); | ||
356 | } | ||
357 | |||
358 | println!("Hello, world"); | ||
359 | } | ||
360 | "#]], | ||
361 | Direction::Up, | ||
362 | ); | ||
363 | check( | ||
364 | r#" | ||
365 | fn main() { | ||
366 | println!("Hello, world"); | ||
367 | |||
368 | while true { | ||
369 | println!("Test"); | ||
370 | }$0$0 | ||
371 | } | ||
372 | "#, | ||
373 | expect![[r#" | ||
374 | fn main() { | ||
375 | while true { | ||
376 | println!("Test"); | ||
377 | } | ||
378 | |||
379 | println!("Hello, world"); | ||
380 | } | ||
381 | "#]], | ||
382 | Direction::Up, | ||
383 | ); | ||
384 | check( | ||
385 | r#" | ||
386 | fn main() { | ||
387 | println!("Hello, world"); | ||
388 | |||
389 | return 123;$0$0 | ||
390 | } | ||
391 | "#, | ||
392 | expect![[r#" | ||
393 | fn main() { | ||
394 | return 123; | ||
395 | |||
396 | println!("Hello, world"); | ||
397 | } | ||
398 | "#]], | ||
399 | Direction::Up, | ||
400 | ); | ||
268 | } | 401 | } |
269 | 402 | ||
270 | #[test] | 403 | #[test] |
@@ -615,6 +748,115 @@ fn test() { | |||
615 | } | 748 | } |
616 | 749 | ||
617 | #[test] | 750 | #[test] |
751 | fn test_cursor_at_item_start() { | ||
752 | check( | ||
753 | r#" | ||
754 | $0$0#[derive(Debug)] | ||
755 | enum FooBar { | ||
756 | Foo, | ||
757 | Bar, | ||
758 | } | ||
759 | |||
760 | fn main() {} | ||
761 | "#, | ||
762 | expect![[r#" | ||
763 | fn main() {} | ||
764 | |||
765 | #[derive(Debug)] | ||
766 | enum FooBar { | ||
767 | Foo, | ||
768 | Bar, | ||
769 | } | ||
770 | "#]], | ||
771 | Direction::Down, | ||
772 | ); | ||
773 | check( | ||
774 | r#" | ||
775 | $0$0enum FooBar { | ||
776 | Foo, | ||
777 | Bar, | ||
778 | } | ||
779 | |||
780 | fn main() {} | ||
781 | "#, | ||
782 | expect![[r#" | ||
783 | fn main() {} | ||
784 | |||
785 | enum FooBar { | ||
786 | Foo, | ||
787 | Bar, | ||
788 | } | ||
789 | "#]], | ||
790 | Direction::Down, | ||
791 | ); | ||
792 | check( | ||
793 | r#" | ||
794 | struct Test; | ||
795 | |||
796 | trait SomeTrait {} | ||
797 | |||
798 | $0$0impl SomeTrait for Test {} | ||
799 | |||
800 | fn main() {} | ||
801 | "#, | ||
802 | expect![[r#" | ||
803 | struct Test; | ||
804 | |||
805 | impl SomeTrait for Test {} | ||
806 | |||
807 | trait SomeTrait {} | ||
808 | |||
809 | fn main() {} | ||
810 | "#]], | ||
811 | Direction::Up, | ||
812 | ); | ||
813 | } | ||
814 | |||
815 | #[test] | ||
816 | fn test_cursor_at_item_end() { | ||
817 | check( | ||
818 | r#" | ||
819 | enum FooBar { | ||
820 | Foo, | ||
821 | Bar, | ||
822 | }$0$0 | ||
823 | |||
824 | fn main() {} | ||
825 | "#, | ||
826 | expect![[r#" | ||
827 | fn main() {} | ||
828 | |||
829 | enum FooBar { | ||
830 | Foo, | ||
831 | Bar, | ||
832 | } | ||
833 | "#]], | ||
834 | Direction::Down, | ||
835 | ); | ||
836 | check( | ||
837 | r#" | ||
838 | struct Test; | ||
839 | |||
840 | trait SomeTrait {} | ||
841 | |||
842 | impl SomeTrait for Test {}$0$0 | ||
843 | |||
844 | fn main() {} | ||
845 | "#, | ||
846 | expect![[r#" | ||
847 | struct Test; | ||
848 | |||
849 | impl SomeTrait for Test {} | ||
850 | |||
851 | trait SomeTrait {} | ||
852 | |||
853 | fn main() {} | ||
854 | "#]], | ||
855 | Direction::Up, | ||
856 | ); | ||
857 | } | ||
858 | |||
859 | #[test] | ||
618 | fn handles_empty_file() { | 860 | fn handles_empty_file() { |
619 | check(r#"$0$0"#, expect![[r#""#]], Direction::Up); | 861 | check(r#"$0$0"#, expect![[r#""#]], Direction::Up); |
620 | } | 862 | } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 963c3fb59..b62d43256 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -11,7 +11,8 @@ use syntax::{ | |||
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase, | 14 | doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | use super::{highlights::Highlights, injector::Injector}; | 18 | use super::{highlights::Highlights, injector::Injector}; |
@@ -190,7 +191,7 @@ pub(super) fn doc_comment( | |||
190 | extract_definitions_from_markdown(line) | 191 | extract_definitions_from_markdown(line) |
191 | .into_iter() | 192 | .into_iter() |
192 | .filter_map(|(range, link, ns)| { | 193 | .filter_map(|(range, link, ns)| { |
193 | Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns)) | 194 | Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns)) |
194 | }) | 195 | }) |
195 | .map(|(Range { start, end }, def)| { | 196 | .map(|(Range { start, end }, def)| { |
196 | ( | 197 | ( |
@@ -283,33 +284,6 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri | |||
283 | } | 284 | } |
284 | } | 285 | } |
285 | 286 | ||
286 | fn validate_intra_doc_link( | ||
287 | db: &RootDatabase, | ||
288 | def: &Definition, | ||
289 | link: &str, | ||
290 | ns: Option<hir::Namespace>, | ||
291 | ) -> Option<hir::ModuleDef> { | ||
292 | match def { | ||
293 | Definition::ModuleDef(def) => match def { | ||
294 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | ||
295 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | ||
296 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | ||
297 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | ||
298 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | ||
299 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | ||
300 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | ||
301 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | ||
302 | hir::ModuleDef::BuiltinType(_) => None, | ||
303 | }, | ||
304 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | ||
305 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | ||
306 | Definition::SelfType(_) | ||
307 | | Definition::Local(_) | ||
308 | | Definition::GenericParam(_) | ||
309 | | Definition::Label(_) => None, | ||
310 | } | ||
311 | } | ||
312 | |||
313 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { | 287 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { |
314 | let symbol = match def { | 288 | let symbol = match def { |
315 | hir::ModuleDef::Module(_) => SymbolKind::Module, | 289 | hir::ModuleDef::Module(_) => SymbolKind::Module, |