aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-03-16 17:57:47 +0000
committerLukas Wirth <[email protected]>2021-03-16 17:57:47 +0000
commit11e9bc60a2d9c22dbf51b7e1aa3d6e30a7006a35 (patch)
treec45f80a838b17f45ec87282fa4377b35da284096 /crates
parenta69f7ce312ba04acbca970a61d9576d520dacb2e (diff)
Move doc-comment highlight injection from AST to HIR
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/attrs.rs5
-rw-r--r--crates/hir/src/semantics.rs1
-rw-r--r--crates/hir/src/semantics/source_to_def.rs6
-rw-r--r--crates/hir_def/src/attr.rs2
-rw-r--r--crates/ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs65
6 files changed, 63 insertions, 18 deletions
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 9e6a3e155..505fc05e7 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -11,8 +11,8 @@ use hir_ty::db::HirDatabase;
11use syntax::ast; 11use syntax::ast;
12 12
13use crate::{ 13use crate::{
14 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module, 14 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, Impl, LifetimeParam, MacroDef,
15 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, 15 Module, ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
16}; 16};
17 17
18pub trait HasAttrs { 18pub trait HasAttrs {
@@ -64,6 +64,7 @@ impl_has_attrs![
64 (Adt, AdtId), 64 (Adt, AdtId),
65 (Module, ModuleId), 65 (Module, ModuleId),
66 (GenericParam, GenericParamId), 66 (GenericParam, GenericParamId),
67 (Impl, ImplId),
67]; 68];
68 69
69macro_rules! impl_has_attrs_enum { 70macro_rules! impl_has_attrs_enum {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 03c9371b5..c7e0d0be3 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -752,6 +752,7 @@ macro_rules! to_def_impls {
752 752
753to_def_impls![ 753to_def_impls![
754 (crate::Module, ast::Module, module_to_def), 754 (crate::Module, ast::Module, module_to_def),
755 (crate::Module, ast::SourceFile, source_file_to_def),
755 (crate::Struct, ast::Struct, struct_to_def), 756 (crate::Struct, ast::Struct, struct_to_def),
756 (crate::Enum, ast::Enum, enum_to_def), 757 (crate::Enum, ast::Enum, enum_to_def),
757 (crate::Union, ast::Union, union_to_def), 758 (crate::Union, ast::Union, union_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index e9d820140..c6ad5ecb5 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -71,6 +71,12 @@ impl SourceToDefCtx<'_, '_> {
71 Some(def_map.module_id(child_id)) 71 Some(def_map.module_id(child_id))
72 } 72 }
73 73
74 pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
75 let _p = profile::span("source_file_to_def");
76 let file_id = src.file_id.original_file(self.db.upcast());
77 self.file_to_def(file_id).get(0).copied()
78 }
79
74 pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> { 80 pub(super) fn trait_to_def(&mut self, src: InFile<ast::Trait>) -> Option<TraitId> {
75 self.to_def(src, keys::TRAIT) 81 self.to_def(src, keys::TRAIT)
76 } 82 }
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 7b41b148c..505c4cd17 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -475,7 +475,7 @@ impl<'a> AttrQuery<'a> {
475 self.attrs().next().is_some() 475 self.attrs().next().is_some()
476 } 476 }
477 477
478 pub(crate) fn attrs(self) -> impl Iterator<Item = &'a Attr> { 478 pub fn attrs(self) -> impl Iterator<Item = &'a Attr> {
479 let key = self.key; 479 let key = self.key;
480 self.attrs 480 self.attrs
481 .iter() 481 .iter()
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 870146d24..ba3447b3a 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -150,7 +150,7 @@ fn traverse(
150 WalkEvent::Enter(it) => it, 150 WalkEvent::Enter(it) => it,
151 WalkEvent::Leave(it) => { 151 WalkEvent::Leave(it) => {
152 if let Some(node) = it.as_node() { 152 if let Some(node) = it.as_node() {
153 inject::doc_comment(hl, node); 153 inject::doc_comment(hl, sema, node);
154 } 154 }
155 continue; 155 continue;
156 } 156 }
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 8cdc3688f..5b065c09f 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -1,8 +1,12 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures. 1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2 2
3use hir::Semantics; 3use either::Either;
4use hir::{HasAttrs, Semantics};
4use ide_db::call_info::ActiveParameter; 5use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; 6use syntax::{
7 ast::{self, AstNode, AttrsOwner},
8 match_ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
9};
6 10
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; 11use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase};
8 12
@@ -81,16 +85,46 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
81 "edition2021", 85 "edition2021",
82]; 86];
83 87
88fn doc_attributes<'node>(
89 sema: &Semantics<RootDatabase>,
90 node: &'node SyntaxNode,
91) -> Option<(Box<dyn AttrsOwner>, hir::Attrs)> {
92 match_ast! {
93 match node {
94 ast::SourceFile(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
95 ast::Fn(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
96 ast::Struct(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
97 ast::Union(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
98 ast::RecordField(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
99 ast::TupleField(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
100 ast::Enum(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
101 ast::Variant(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
102 ast::Trait(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
103 ast::Module(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
104 ast::Static(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
105 ast::Const(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
106 ast::TypeAlias(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
107 ast::Impl(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
108 ast::MacroRules(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
109 ast::MacroRules(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
110 // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
111 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
112 _ => return None
113 }
114 }
115}
116
84/// Injection of syntax highlighting of doctests. 117/// Injection of syntax highlighting of doctests.
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { 118pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) {
86 let doc_comments = node 119 let (owner, attributes) = match doc_attributes(sema, node) {
87 .children_with_tokens() 120 Some(it) => it,
88 .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) 121 None => return,
89 .filter(|it| it.kind().doc.is_some()); 122 };
90 123
91 if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { 124 if attributes.docs().map_or(true, |docs| !String::from(docs).contains(RUSTDOC_FENCE)) {
92 return; 125 return;
93 } 126 }
127 let doc_comments = attributes.by_key("doc").attrs().map(|attr| attr.to_src(&*owner));
94 128
95 let mut inj = Injector::default(); 129 let mut inj = Injector::default();
96 inj.add_unmapped("fn doctest() {\n"); 130 inj.add_unmapped("fn doctest() {\n");
@@ -102,11 +136,17 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
102 // spanning comment ranges. 136 // spanning comment ranges.
103 let mut new_comments = Vec::new(); 137 let mut new_comments = Vec::new();
104 for comment in doc_comments { 138 for comment in doc_comments {
105 match comment.text().find(RUSTDOC_FENCE) { 139 let (line, range, prefix) = match &comment {
140 Either::Left(_) => continue, // FIXME
141 Either::Right(comment) => {
142 (comment.text(), comment.syntax().text_range(), comment.prefix())
143 }
144 };
145 match line.find(RUSTDOC_FENCE) {
106 Some(idx) => { 146 Some(idx) => {
107 is_codeblock = !is_codeblock; 147 is_codeblock = !is_codeblock;
108 // Check whether code is rust by inspecting fence guards 148 // Check whether code is rust by inspecting fence guards
109 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; 149 let guards = &line[idx + RUSTDOC_FENCE.len()..];
110 let is_rust = 150 let is_rust =
111 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); 151 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
112 is_doctest = is_codeblock && is_rust; 152 is_doctest = is_codeblock && is_rust;
@@ -116,10 +156,7 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
116 None => (), 156 None => (),
117 } 157 }
118 158
119 let line: &str = comment.text(); 159 let mut pos = TextSize::of(prefix);
120 let range = comment.syntax().text_range();
121
122 let mut pos = TextSize::of(comment.prefix());
123 // whitespace after comment is ignored 160 // whitespace after comment is ignored
124 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { 161 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
125 pos += TextSize::of(ws); 162 pos += TextSize::of(ws);