diff options
author | Lukas Wirth <[email protected]> | 2021-03-18 12:16:27 +0000 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-03-18 12:16:27 +0000 |
commit | d41a1690d2aa5d3287b00d100897136b6186c39c (patch) | |
tree | 5ad953d6959a1255f09e3ca730ac48669dab9030 | |
parent | 80d497e5415e9826cfe0596b6be88c6733f56cb5 (diff) |
Track source file IDs in source mapping of Attrs
-rw-r--r-- | crates/hir_def/src/attr.rs | 64 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 16 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 22 |
3 files changed, 76 insertions, 26 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index e4c84afbf..739e3f5e3 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -283,8 +283,51 @@ impl Attrs { | |||
283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. | 283 | /// Constructs a map that maps the lowered `Attr`s in this `Attrs` back to its original syntax nodes. |
284 | /// | 284 | /// |
285 | /// `owner` must be the original owner of the attributes. | 285 | /// `owner` must be the original owner of the attributes. |
286 | pub fn source_map(&self, owner: &dyn ast::AttrsOwner) -> AttrSourceMap { | 286 | // FIXME: figure out a better api that doesnt require the for_module hack |
287 | AttrSourceMap { attrs: collect_attrs(owner).collect() } | 287 | pub fn source_map(&self, owner: InFile<&dyn ast::AttrsOwner>) -> AttrSourceMap { |
288 | // FIXME: This doesn't work correctly for modules, as the attributes there can have up to | ||
289 | // two different owners | ||
290 | AttrSourceMap { | ||
291 | attrs: collect_attrs(owner.value) | ||
292 | .map(|attr| InFile::new(owner.file_id, attr)) | ||
293 | .collect(), | ||
294 | } | ||
295 | } | ||
296 | |||
297 | pub fn source_map_for_module( | ||
298 | &self, | ||
299 | db: &dyn DefDatabase, | ||
300 | module: crate::ModuleId, | ||
301 | ) -> AttrSourceMap { | ||
302 | let def_map = module.def_map(db); | ||
303 | let mod_data = &def_map[module.local_id]; | ||
304 | let attrs = match mod_data.declaration_source(db) { | ||
305 | Some(it) => { | ||
306 | let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner) | ||
307 | .map(|attr| InFile::new(it.file_id, attr)) | ||
308 | .collect(); | ||
309 | if let InFile { file_id, value: ModuleSource::SourceFile(file) } = | ||
310 | mod_data.definition_source(db) | ||
311 | { | ||
312 | attrs.extend( | ||
313 | collect_attrs(&file as &dyn ast::AttrsOwner) | ||
314 | .map(|attr| InFile::new(file_id, attr)), | ||
315 | ) | ||
316 | } | ||
317 | attrs | ||
318 | } | ||
319 | None => { | ||
320 | let InFile { file_id, value } = mod_data.definition_source(db); | ||
321 | match &value { | ||
322 | ModuleSource::SourceFile(file) => collect_attrs(file as &dyn ast::AttrsOwner), | ||
323 | ModuleSource::Module(module) => collect_attrs(module as &dyn ast::AttrsOwner), | ||
324 | ModuleSource::BlockExpr(block) => collect_attrs(block as &dyn ast::AttrsOwner), | ||
325 | } | ||
326 | .map(|attr| InFile::new(file_id, attr)) | ||
327 | .collect() | ||
328 | } | ||
329 | }; | ||
330 | AttrSourceMap { attrs } | ||
288 | } | 331 | } |
289 | 332 | ||
290 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 333 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
@@ -379,7 +422,7 @@ fn inner_attributes( | |||
379 | } | 422 | } |
380 | 423 | ||
381 | pub struct AttrSourceMap { | 424 | pub struct AttrSourceMap { |
382 | attrs: Vec<Either<ast::Attr, ast::Comment>>, | 425 | attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>, |
383 | } | 426 | } |
384 | 427 | ||
385 | impl AttrSourceMap { | 428 | impl AttrSourceMap { |
@@ -389,10 +432,11 @@ impl AttrSourceMap { | |||
389 | /// | 432 | /// |
390 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | 433 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of |
391 | /// the attribute represented by `Attr`. | 434 | /// the attribute represented by `Attr`. |
392 | pub fn source_of(&self, attr: &Attr) -> &Either<ast::Attr, ast::Comment> { | 435 | pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { |
393 | self.attrs | 436 | self.attrs |
394 | .get(attr.index as usize) | 437 | .get(attr.index as usize) |
395 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) | 438 | .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) |
439 | .as_ref() | ||
396 | } | 440 | } |
397 | } | 441 | } |
398 | 442 | ||
@@ -428,18 +472,6 @@ impl Attr { | |||
428 | Some(Attr { index, path, input }) | 472 | Some(Attr { index, path, input }) |
429 | } | 473 | } |
430 | 474 | ||
431 | /// Maps this lowered `Attr` back to its original syntax node. | ||
432 | /// | ||
433 | /// `owner` must be the original owner of the attribute. | ||
434 | /// | ||
435 | /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of | ||
436 | /// the attribute represented by `Attr`. | ||
437 | pub fn to_src(&self, owner: &dyn ast::AttrsOwner) -> Either<ast::Attr, ast::Comment> { | ||
438 | collect_attrs(owner).nth(self.index as usize).unwrap_or_else(|| { | ||
439 | panic!("cannot find `Attr` at index {} in {}", self.index, owner.syntax()) | ||
440 | }) | ||
441 | } | ||
442 | |||
443 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths | 475 | /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths |
444 | /// to derive macros. | 476 | /// to derive macros. |
445 | /// | 477 | /// |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ba3447b3a..e25b698e0 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -12,7 +12,7 @@ mod html; | |||
12 | #[cfg(test)] | 12 | #[cfg(test)] |
13 | mod tests; | 13 | mod tests; |
14 | 14 | ||
15 | use hir::{Name, Semantics}; | 15 | use hir::{InFile, Name, Semantics}; |
16 | use ide_db::{RootDatabase, SymbolKind}; | 16 | use ide_db::{RootDatabase, SymbolKind}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | use syntax::{ | 18 | use syntax::{ |
@@ -73,14 +73,20 @@ pub(crate) fn highlight( | |||
73 | }; | 73 | }; |
74 | 74 | ||
75 | let mut hl = highlights::Highlights::new(root.text_range()); | 75 | let mut hl = highlights::Highlights::new(root.text_range()); |
76 | traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting); | 76 | traverse( |
77 | &mut hl, | ||
78 | &sema, | ||
79 | InFile::new(file_id.into(), &root), | ||
80 | range_to_highlight, | ||
81 | syntactic_name_ref_highlighting, | ||
82 | ); | ||
77 | hl.to_vec() | 83 | hl.to_vec() |
78 | } | 84 | } |
79 | 85 | ||
80 | fn traverse( | 86 | fn traverse( |
81 | hl: &mut Highlights, | 87 | hl: &mut Highlights, |
82 | sema: &Semantics<RootDatabase>, | 88 | sema: &Semantics<RootDatabase>, |
83 | root: &SyntaxNode, | 89 | root: InFile<&SyntaxNode>, |
84 | range_to_highlight: TextRange, | 90 | range_to_highlight: TextRange, |
85 | syntactic_name_ref_highlighting: bool, | 91 | syntactic_name_ref_highlighting: bool, |
86 | ) { | 92 | ) { |
@@ -93,7 +99,7 @@ fn traverse( | |||
93 | 99 | ||
94 | // Walk all nodes, keeping track of whether we are inside a macro or not. | 100 | // Walk all nodes, keeping track of whether we are inside a macro or not. |
95 | // If in macro, expand it first and highlight the expanded code. | 101 | // If in macro, expand it first and highlight the expanded code. |
96 | for event in root.preorder_with_tokens() { | 102 | for event in root.value.preorder_with_tokens() { |
97 | let event_range = match &event { | 103 | let event_range = match &event { |
98 | WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(), | 104 | WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(), |
99 | }; | 105 | }; |
@@ -150,7 +156,7 @@ fn traverse( | |||
150 | WalkEvent::Enter(it) => it, | 156 | WalkEvent::Enter(it) => it, |
151 | WalkEvent::Leave(it) => { | 157 | WalkEvent::Leave(it) => { |
152 | if let Some(node) = it.as_node() { | 158 | if let Some(node) = it.as_node() { |
153 | inject::doc_comment(hl, sema, node); | 159 | inject::doc_comment(hl, sema, root.with_value(node)); |
154 | } | 160 | } |
155 | continue; | 161 | continue; |
156 | } | 162 | } |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 947cc974c..e6dbd307e 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::{mem, ops::Range}; | 3 | use std::{mem, ops::Range}; |
4 | 4 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{HasAttrs, Semantics}; | 6 | use hir::{HasAttrs, InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, defs::Definition}; | 7 | use ide_db::{call_info::ActiveParameter, defs::Definition}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, | 9 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, |
@@ -148,8 +148,12 @@ fn doc_attributes<'node>( | |||
148 | } | 148 | } |
149 | 149 | ||
150 | /// Injection of syntax highlighting of doctests. | 150 | /// Injection of syntax highlighting of doctests. |
151 | pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) { | 151 | pub(super) fn doc_comment( |
152 | let (owner, attributes, def) = match doc_attributes(sema, node) { | 152 | hl: &mut Highlights, |
153 | sema: &Semantics<RootDatabase>, | ||
154 | node: InFile<&SyntaxNode>, | ||
155 | ) { | ||
156 | let (owner, attributes, def) = match doc_attributes(sema, node.value) { | ||
153 | Some(it) => it, | 157 | Some(it) => it, |
154 | None => return, | 158 | None => return, |
155 | }; | 159 | }; |
@@ -157,7 +161,12 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n | |||
157 | let mut inj = Injector::default(); | 161 | let mut inj = Injector::default(); |
158 | inj.add_unmapped("fn doctest() {\n"); | 162 | inj.add_unmapped("fn doctest() {\n"); |
159 | 163 | ||
160 | let attrs_source_map = attributes.source_map(&owner); | 164 | let attrs_source_map = match def { |
165 | Definition::ModuleDef(hir::ModuleDef::Module(module)) => { | ||
166 | attributes.source_map_for_module(sema.db, module.into()) | ||
167 | } | ||
168 | _ => attributes.source_map(node.with_value(&owner)), | ||
169 | }; | ||
161 | 170 | ||
162 | let mut is_codeblock = false; | 171 | let mut is_codeblock = false; |
163 | let mut is_doctest = false; | 172 | let mut is_doctest = false; |
@@ -168,7 +177,10 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n | |||
168 | let mut intra_doc_links = Vec::new(); | 177 | let mut intra_doc_links = Vec::new(); |
169 | let mut string; | 178 | let mut string; |
170 | for attr in attributes.by_key("doc").attrs() { | 179 | for attr in attributes.by_key("doc").attrs() { |
171 | let src = attrs_source_map.source_of(&attr); | 180 | let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); |
181 | if file_id != node.file_id { | ||
182 | continue; | ||
183 | } | ||
172 | let (line, range, prefix) = match &src { | 184 | let (line, range, prefix) = match &src { |
173 | Either::Left(it) => { | 185 | Either::Left(it) => { |
174 | string = match find_doc_string_in_attr(attr, it) { | 186 | string = match find_doc_string_in_attr(attr, it) { |