diff options
author | Lukas Wirth <[email protected]> | 2021-03-30 17:27:16 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-03-30 17:27:16 +0100 |
commit | bb56b7a75cfae8297535d55fbddbee9875cbc756 (patch) | |
tree | d367c18dddc65d4442679680ea728f5b06280ea5 /crates/ide/src | |
parent | 9a327311e4a9b9102528751e052c63266c00c6bd (diff) |
Use new new docs string source mapping in goto_def and hover
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/doc_links.rs | 117 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 19 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 21 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 35 |
4 files changed, 73 insertions, 119 deletions
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 69442278b..9a9a41113 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -15,10 +15,7 @@ use ide_db::{ | |||
15 | defs::{Definition, NameClass, NameRefClass}, | 15 | defs::{Definition, NameClass, NameRefClass}, |
16 | RootDatabase, | 16 | RootDatabase, |
17 | }; | 17 | }; |
18 | use syntax::{ | 18 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TokenAtOffset, T}; |
19 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, | ||
20 | TokenAtOffset, T, | ||
21 | }; | ||
22 | 19 | ||
23 | use crate::{FilePosition, Semantics}; | 20 | use crate::{FilePosition, Semantics}; |
24 | 21 | ||
@@ -119,77 +116,22 @@ pub(crate) fn external_docs( | |||
119 | pub(crate) fn extract_definitions_from_markdown( | 116 | pub(crate) fn extract_definitions_from_markdown( |
120 | markdown: &str, | 117 | markdown: &str, |
121 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { | 118 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { |
122 | extract_definitions_from_markdown_(markdown, &mut broken_link_clone_cb).collect() | 119 | Parser::new_with_broken_link_callback( |
123 | } | 120 | markdown, |
124 | 121 | Options::empty(), | |
125 | fn extract_definitions_from_markdown_<'a>( | 122 | Some(&mut broken_link_clone_cb), |
126 | markdown: &'a str, | 123 | ) |
127 | cb: &'a mut dyn FnMut(BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)>, | 124 | .into_offset_iter() |
128 | ) -> impl Iterator<Item = (Range<usize>, String, Option<hir::Namespace>)> + 'a { | 125 | .filter_map(|(event, range)| { |
129 | Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(cb)) | 126 | if let Event::Start(Tag::Link(_, target, title)) = event { |
130 | .into_offset_iter() | 127 | let link = if target.is_empty() { title } else { target }; |
131 | .filter_map(|(event, range)| { | 128 | let (link, ns) = parse_intra_doc_link(&link); |
132 | if let Event::Start(Tag::Link(_, target, title)) = event { | 129 | Some((range, link.to_string(), ns)) |
133 | let link = if target.is_empty() { title } else { target }; | 130 | } else { |
134 | let (link, ns) = parse_intra_doc_link(&link); | 131 | None |
135 | Some((range, link.to_string(), ns)) | ||
136 | } else { | ||
137 | None | ||
138 | } | ||
139 | }) | ||
140 | } | ||
141 | |||
142 | /// Extracts a link from a comment at the given position returning the spanning range, link and | ||
143 | /// optionally it's namespace. | ||
144 | pub(crate) fn extract_positioned_link_from_comment( | ||
145 | position: TextSize, | ||
146 | comment: &ast::Comment, | ||
147 | docs: hir::Documentation, | ||
148 | ) -> Option<(TextRange, String, Option<hir::Namespace>)> { | ||
149 | let doc_comment = comment.doc_comment()?.to_string() + "\n" + docs.as_str(); | ||
150 | let comment_start = | ||
151 | comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32); | ||
152 | let len = comment.syntax().text_range().len().into(); | ||
153 | let mut cb = broken_link_clone_cb; | ||
154 | // because pulldown_cmarks lifetimes are wrong we gotta dance around a few temporaries here | ||
155 | let res = extract_definitions_from_markdown_(&doc_comment, &mut cb) | ||
156 | .take_while(|&(Range { end, .. }, ..)| end < len) | ||
157 | .find_map(|(Range { start, end }, def_link, ns)| { | ||
158 | let range = TextRange::at( | ||
159 | comment_start + TextSize::from(start as u32), | ||
160 | TextSize::from((end - start) as u32), | ||
161 | ); | ||
162 | range.contains(position).then(|| (range, def_link, ns)) | ||
163 | }); | ||
164 | res | ||
165 | } | ||
166 | |||
167 | /// Turns a syntax node into it's [`Definition`] if it can hold docs. | ||
168 | pub(crate) fn doc_owner_to_def( | ||
169 | sema: &Semantics<RootDatabase>, | ||
170 | item: &SyntaxNode, | ||
171 | ) -> Option<Definition> { | ||
172 | let res: hir::ModuleDef = match_ast! { | ||
173 | match item { | ||
174 | ast::SourceFile(_it) => sema.scope(item).module()?.into(), | ||
175 | ast::Fn(it) => sema.to_def(&it)?.into(), | ||
176 | ast::Struct(it) => sema.to_def(&it)?.into(), | ||
177 | ast::Enum(it) => sema.to_def(&it)?.into(), | ||
178 | ast::Union(it) => sema.to_def(&it)?.into(), | ||
179 | ast::Trait(it) => sema.to_def(&it)?.into(), | ||
180 | ast::Const(it) => sema.to_def(&it)?.into(), | ||
181 | ast::Static(it) => sema.to_def(&it)?.into(), | ||
182 | ast::TypeAlias(it) => sema.to_def(&it)?.into(), | ||
183 | ast::Variant(it) => sema.to_def(&it)?.into(), | ||
184 | ast::Trait(it) => sema.to_def(&it)?.into(), | ||
185 | ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType), | ||
186 | ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro), | ||
187 | ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field), | ||
188 | ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field), | ||
189 | _ => return None, | ||
190 | } | 132 | } |
191 | }; | 133 | }) |
192 | Some(Definition::ModuleDef(res)) | 134 | .collect() |
193 | } | 135 | } |
194 | 136 | ||
195 | pub(crate) fn resolve_doc_path_for_def( | 137 | pub(crate) fn resolve_doc_path_for_def( |
@@ -219,6 +161,33 @@ pub(crate) fn resolve_doc_path_for_def( | |||
219 | } | 161 | } |
220 | } | 162 | } |
221 | 163 | ||
164 | pub(crate) fn doc_attributes( | ||
165 | sema: &Semantics<RootDatabase>, | ||
166 | node: &SyntaxNode, | ||
167 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
168 | match_ast! { | ||
169 | match node { | ||
170 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
171 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
172 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
173 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
174 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
175 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
176 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
177 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
178 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
179 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
180 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
181 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
182 | ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
183 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
184 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | ||
185 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
186 | _ => return None | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
222 | fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, CowStr<'b>)> { | 191 | fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, CowStr<'b>)> { |
223 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong | 192 | // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong |
224 | // this is fixed in the repo but not on the crates.io release yet | 193 | // this is fixed in the repo but not on the crates.io release yet |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 1951c599f..780bdd40d 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::Semantics; | 2 | use hir::{InFile, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | defs::{NameClass, NameRefClass}, | 4 | defs::{NameClass, NameRefClass}, |
5 | RootDatabase, | 5 | RootDatabase, |
@@ -8,7 +8,7 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | display::TryToNav, | 10 | display::TryToNav, |
11 | doc_links::{doc_owner_to_def, extract_positioned_link_from_comment, resolve_doc_path_for_def}, | 11 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
12 | FilePosition, NavigationTarget, RangeInfo, | 12 | FilePosition, NavigationTarget, RangeInfo, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -30,11 +30,16 @@ pub(crate) fn goto_definition( | |||
30 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 30 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
31 | let token = sema.descend_into_macros(original_token.clone()); | 31 | let token = sema.descend_into_macros(original_token.clone()); |
32 | let parent = token.parent()?; | 32 | let parent = token.parent()?; |
33 | if let Some(comment) = ast::Comment::cast(token) { | 33 | if let Some(_) = ast::Comment::cast(token) { |
34 | let docs = doc_owner_to_def(&sema, &parent)?.docs(db)?; | 34 | let (attributes, def) = doc_attributes(&sema, &parent)?; |
35 | 35 | ||
36 | let (_, link, ns) = extract_positioned_link_from_comment(position.offset, &comment, docs)?; | 36 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
37 | let def = doc_owner_to_def(&sema, &parent)?; | 37 | let (_, link, ns) = |
38 | extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| { | ||
39 | doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| { | ||
40 | file_id == position.file_id.into() && range.contains(position.offset) | ||
41 | }) | ||
42 | })?; | ||
38 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; | 43 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; |
39 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 44 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
40 | } | 45 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 5a497e92d..1e66219e4 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::{ | 2 | use hir::{ |
3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, Module, | 3 | AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module, |
4 | ModuleDef, Semantics, | 4 | ModuleDef, Semantics, |
5 | }; | 5 | }; |
6 | use ide_db::{ | 6 | use ide_db::{ |
@@ -16,8 +16,8 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke | |||
16 | use crate::{ | 16 | use crate::{ |
17 | display::{macro_label, TryToNav}, | 17 | display::{macro_label, TryToNav}, |
18 | doc_links::{ | 18 | doc_links::{ |
19 | doc_owner_to_def, extract_positioned_link_from_comment, remove_links, | 19 | doc_attributes, extract_definitions_from_markdown, remove_links, resolve_doc_path_for_def, |
20 | resolve_doc_path_for_def, rewrite_links, | 20 | rewrite_links, |
21 | }, | 21 | }, |
22 | markdown_remove::remove_markdown, | 22 | markdown_remove::remove_markdown, |
23 | markup::Markup, | 23 | markup::Markup, |
@@ -114,11 +114,18 @@ pub(crate) fn hover( | |||
114 | ), | 114 | ), |
115 | 115 | ||
116 | _ => ast::Comment::cast(token.clone()) | 116 | _ => ast::Comment::cast(token.clone()) |
117 | .and_then(|comment| { | 117 | .and_then(|_| { |
118 | let def = doc_owner_to_def(&sema, &node)?; | 118 | let (attributes, def) = doc_attributes(&sema, &node)?; |
119 | let docs = def.docs(db)?; | 119 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
120 | let (idl_range, link, ns) = | 120 | let (idl_range, link, ns) = |
121 | extract_positioned_link_from_comment(position.offset, &comment, docs)?; | 121 | extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| { |
122 | let InFile { file_id, value: range } = doc_mapping.map(range.clone())?; | ||
123 | if file_id == position.file_id.into() && range.contains(position.offset) { | ||
124 | Some((range, link, ns)) | ||
125 | } else { | ||
126 | None | ||
127 | } | ||
128 | })?; | ||
122 | range = Some(idl_range); | 129 | range = Some(idl_range); |
123 | resolve_doc_path_for_def(db, def, &link, ns) | 130 | resolve_doc_path_for_def(db, def, &link, ns) |
124 | }) | 131 | }) |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 504783f31..04fafd244 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -3,15 +3,15 @@ | |||
3 | use std::mem; | 3 | use std::mem; |
4 | 4 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{HasAttrs, InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, defs::Definition, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
10 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, | 14 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | 15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -90,33 +90,6 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
90 | "edition2021", | 90 | "edition2021", |
91 | ]; | 91 | ]; |
92 | 92 | ||
93 | fn doc_attributes<'node>( | ||
94 | sema: &Semantics<RootDatabase>, | ||
95 | node: &'node SyntaxNode, | ||
96 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
97 | match_ast! { | ||
98 | match node { | ||
99 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
100 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
101 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
102 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
103 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
104 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
105 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
106 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
107 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
108 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
109 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
110 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
111 | ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
112 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
113 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | ||
114 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
115 | _ => return None | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /// Injection of syntax highlighting of doctests. | 93 | /// Injection of syntax highlighting of doctests. |
121 | pub(super) fn doc_comment( | 94 | pub(super) fn doc_comment( |
122 | hl: &mut Highlights, | 95 | hl: &mut Highlights, |