aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorPascal Hertleif <[email protected]>2019-05-23 18:42:42 +0100
committerPascal Hertleif <[email protected]>2019-05-27 10:26:33 +0100
commit5bf3e949e8470a138a61c806769e1a329761cab6 (patch)
tree9885346944b4aa82804514580944673a53605ee2 /crates/ra_ide_api
parent4b48cff022a1606bde596f01fbf44361640b10d8 (diff)
Semantic highlighting spike
Very simple approach: For each identifier, set the hash of the range where it's defined as its 'id' and use it in the VSCode extension to generate unique colors. Thus, the generated colors are per-file. They are also quite fragile, and I'm not entirely sure why. Looks like we need to make sure the same ranges aren't overwritten by a later request?
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlighting.snap192
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap87
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs101
3 files changed, 345 insertions, 35 deletions
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
new file mode 100644
index 000000000..208681f10
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
@@ -0,0 +1,192 @@
1---
2created: "2019-05-25T10:53:54.439877Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result
6---
7Ok(
8 [
9 HighlightedRange {
10 range: [1; 24),
11 tag: "attribute",
12 id: None,
13 },
14 HighlightedRange {
15 range: [25; 31),
16 tag: "keyword",
17 id: None,
18 },
19 HighlightedRange {
20 range: [32; 35),
21 tag: "variable",
22 id: Some(
23 461893210254723387,
24 ),
25 },
26 HighlightedRange {
27 range: [42; 45),
28 tag: "keyword",
29 id: None,
30 },
31 HighlightedRange {
32 range: [46; 47),
33 tag: "variable",
34 id: Some(
35 8312289520117458465,
36 ),
37 },
38 HighlightedRange {
39 range: [49; 52),
40 tag: "text",
41 id: None,
42 },
43 HighlightedRange {
44 range: [58; 61),
45 tag: "keyword",
46 id: None,
47 },
48 HighlightedRange {
49 range: [62; 63),
50 tag: "variable",
51 id: Some(
52 4497542318236667727,
53 ),
54 },
55 HighlightedRange {
56 range: [65; 68),
57 tag: "text",
58 id: None,
59 },
60 HighlightedRange {
61 range: [73; 75),
62 tag: "keyword",
63 id: None,
64 },
65 HighlightedRange {
66 range: [76; 79),
67 tag: "variable",
68 id: Some(
69 4506850079084802999,
70 ),
71 },
72 HighlightedRange {
73 range: [80; 81),
74 tag: "type",
75 id: None,
76 },
77 HighlightedRange {
78 range: [80; 81),
79 tag: "variable",
80 id: Some(
81 16968185728268100018,
82 ),
83 },
84 HighlightedRange {
85 range: [88; 89),
86 tag: "type",
87 id: None,
88 },
89 HighlightedRange {
90 range: [96; 110),
91 tag: "macro",
92 id: None,
93 },
94 HighlightedRange {
95 range: [117; 127),
96 tag: "comment",
97 id: None,
98 },
99 HighlightedRange {
100 range: [128; 130),
101 tag: "keyword",
102 id: None,
103 },
104 HighlightedRange {
105 range: [131; 135),
106 tag: "variable",
107 id: Some(
108 14467718814232352107,
109 ),
110 },
111 HighlightedRange {
112 range: [145; 153),
113 tag: "macro",
114 id: None,
115 },
116 HighlightedRange {
117 range: [154; 166),
118 tag: "string",
119 id: None,
120 },
121 HighlightedRange {
122 range: [168; 170),
123 tag: "literal",
124 id: None,
125 },
126 HighlightedRange {
127 range: [178; 181),
128 tag: "keyword",
129 id: None,
130 },
131 HighlightedRange {
132 range: [182; 185),
133 tag: "keyword",
134 id: None,
135 },
136 HighlightedRange {
137 range: [186; 189),
138 tag: "macro",
139 id: None,
140 },
141 HighlightedRange {
142 range: [197; 200),
143 tag: "macro",
144 id: None,
145 },
146 HighlightedRange {
147 range: [192; 195),
148 tag: "text",
149 id: None,
150 },
151 HighlightedRange {
152 range: [208; 211),
153 tag: "macro",
154 id: None,
155 },
156 HighlightedRange {
157 range: [212; 216),
158 tag: "macro",
159 id: None,
160 },
161 HighlightedRange {
162 range: [226; 227),
163 tag: "literal",
164 id: None,
165 },
166 HighlightedRange {
167 range: [232; 233),
168 tag: "literal",
169 id: None,
170 },
171 HighlightedRange {
172 range: [242; 248),
173 tag: "keyword.unsafe",
174 id: None,
175 },
176 HighlightedRange {
177 range: [251; 254),
178 tag: "text",
179 id: None,
180 },
181 HighlightedRange {
182 range: [255; 262),
183 tag: "text",
184 id: None,
185 },
186 HighlightedRange {
187 range: [263; 264),
188 tag: "literal",
189 id: None,
190 },
191 ],
192)
diff --git a/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap
new file mode 100644
index 000000000..3b3fe32e9
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/tests__sematic_highlighting.snap
@@ -0,0 +1,87 @@
1---
2created: "2019-05-25T10:25:13.898113Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result
6---
7Ok(
8 [
9 HighlightedRange {
10 range: [1; 3),
11 tag: "keyword",
12 id: None,
13 },
14 HighlightedRange {
15 range: [4; 8),
16 tag: "variable",
17 id: Some(
18 17119830160611610240,
19 ),
20 },
21 HighlightedRange {
22 range: [17; 20),
23 tag: "keyword",
24 id: None,
25 },
26 HighlightedRange {
27 range: [21; 26),
28 tag: "variable",
29 id: Some(
30 2744494144922727377,
31 ),
32 },
33 HighlightedRange {
34 range: [29; 36),
35 tag: "string",
36 id: None,
37 },
38 HighlightedRange {
39 range: [42; 45),
40 tag: "keyword",
41 id: None,
42 },
43 HighlightedRange {
44 range: [46; 47),
45 tag: "variable",
46 id: Some(
47 10375904121795371996,
48 ),
49 },
50 HighlightedRange {
51 range: [50; 55),
52 tag: "variable",
53 id: Some(
54 2744494144922727377,
55 ),
56 },
57 HighlightedRange {
58 range: [56; 65),
59 tag: "text",
60 id: None,
61 },
62 HighlightedRange {
63 range: [73; 76),
64 tag: "keyword",
65 id: None,
66 },
67 HighlightedRange {
68 range: [77; 78),
69 tag: "variable",
70 id: Some(
71 8228548264153724449,
72 ),
73 },
74 HighlightedRange {
75 range: [81; 86),
76 tag: "variable",
77 id: Some(
78 2744494144922727377,
79 ),
80 },
81 HighlightedRange {
82 range: [87; 96),
83 tag: "text",
84 id: None,
85 },
86 ],
87)
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 87e053364..da000c0c3 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -10,6 +10,7 @@ use crate::{FileId, db::RootDatabase};
10pub struct HighlightedRange { 10pub struct HighlightedRange {
11 pub range: TextRange, 11 pub range: TextRange,
12 pub tag: &'static str, 12 pub tag: &'static str,
13 pub id: Option<u64>,
13} 14}
14 15
15fn is_control_keyword(kind: SyntaxKind) -> bool { 16fn is_control_keyword(kind: SyntaxKind) -> bool {
@@ -32,6 +33,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
32 33
33 let source_file = db.parse(file_id); 34 let source_file = db.parse(file_id);
34 35
36 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
37 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
38
39 let mut hasher = DefaultHasher::new();
40 x.hash(&mut hasher);
41 hasher.finish()
42 }
43
35 // Visited nodes to handle highlighting priorities 44 // Visited nodes to handle highlighting priorities
36 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default(); 45 let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
37 let mut res = Vec::new(); 46 let mut res = Vec::new();
@@ -39,52 +48,59 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
39 if highlighted.contains(&node) { 48 if highlighted.contains(&node) {
40 continue; 49 continue;
41 } 50 }
42 let tag = match node.kind() { 51 let (tag, id) = match node.kind() {
43 COMMENT => "comment", 52 COMMENT => ("comment", None),
44 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", 53 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => ("string", None),
45 ATTR => "attribute", 54 ATTR => ("attribute", None),
46 NAME_REF => { 55 NAME_REF => {
47 if let Some(name_ref) = node.as_node().and_then(|n| ast::NameRef::cast(n)) { 56 if let Some(name_ref) = node.as_ast_node::<ast::NameRef>() {
48 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*}; 57 use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
49 use hir::{ModuleDef, ImplItem}; 58 use hir::{ModuleDef, ImplItem};
50 59
51 // FIXME: try to reuse the SourceAnalyzers 60 // FIXME: try to reuse the SourceAnalyzers
52 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); 61 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
53 match classify_name_ref(db, &analyzer, name_ref) { 62 match classify_name_ref(db, &analyzer, name_ref) {
54 Some(Method(_)) => "function", 63 Some(Method(_)) => ("function", None),
55 Some(Macro(_)) => "macro", 64 Some(Macro(_)) => ("macro", None),
56 Some(FieldAccess(_)) => "field", 65 Some(FieldAccess(_)) => ("field", None),
57 Some(AssocItem(ImplItem::Method(_))) => "function", 66 Some(AssocItem(ImplItem::Method(_))) => ("function", None),
58 Some(AssocItem(ImplItem::Const(_))) => "constant", 67 Some(AssocItem(ImplItem::Const(_))) => ("constant", None),
59 Some(AssocItem(ImplItem::TypeAlias(_))) => "type", 68 Some(AssocItem(ImplItem::TypeAlias(_))) => ("type", None),
60 Some(Def(ModuleDef::Module(_))) => "module", 69 Some(Def(ModuleDef::Module(_))) => ("module", None),
61 Some(Def(ModuleDef::Function(_))) => "function", 70 Some(Def(ModuleDef::Function(_))) => ("function", None),
62 Some(Def(ModuleDef::Struct(_))) => "type", 71 Some(Def(ModuleDef::Struct(_))) => ("type", None),
63 Some(Def(ModuleDef::Union(_))) => "type", 72 Some(Def(ModuleDef::Union(_))) => ("type", None),
64 Some(Def(ModuleDef::Enum(_))) => "type", 73 Some(Def(ModuleDef::Enum(_))) => ("type", None),
65 Some(Def(ModuleDef::EnumVariant(_))) => "constant", 74 Some(Def(ModuleDef::EnumVariant(_))) => ("constant", None),
66 Some(Def(ModuleDef::Const(_))) => "constant", 75 Some(Def(ModuleDef::Const(_))) => ("constant", None),
67 Some(Def(ModuleDef::Static(_))) => "constant", 76 Some(Def(ModuleDef::Static(_))) => ("constant", None),
68 Some(Def(ModuleDef::Trait(_))) => "type", 77 Some(Def(ModuleDef::Trait(_))) => ("type", None),
69 Some(Def(ModuleDef::TypeAlias(_))) => "type", 78 Some(Def(ModuleDef::TypeAlias(_))) => ("type", None),
70 Some(SelfType(_)) => "type", 79 Some(SelfType(_)) => ("type", None),
71 Some(Pat(_)) => "text", 80 Some(Pat(ptr)) => ("variable", Some(hash(ptr.syntax_node_ptr().range()))),
72 Some(SelfParam(_)) => "type", 81 Some(SelfParam(_)) => ("type", None),
73 Some(GenericParam(_)) => "type", 82 Some(GenericParam(_)) => ("type", None),
74 None => "text", 83 None => ("text", None),
75 } 84 }
76 } else { 85 } else {
77 "text" 86 ("text", None)
78 } 87 }
79 } 88 }
80 NAME => "function", 89 NAME => {
81 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => "type", 90 if let Some(name) = node.as_ast_node::<ast::Name>() {
82 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 91 ("variable", Some(hash(name.syntax().range())))
83 LIFETIME => "parameter", 92 } else {
84 T![unsafe] => "keyword.unsafe", 93 ("text", None)
85 k if is_control_keyword(k) => "keyword.control", 94 }
86 k if k.is_keyword() => "keyword", 95 }
96 TYPE_ALIAS_DEF | TYPE_ARG | TYPE_PARAM => ("type", None),
97 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => ("literal", None),
98 LIFETIME => ("parameter", None),
99 T![unsafe] => ("keyword.unsafe", None),
100 k if is_control_keyword(k) => ("keyword.control", None),
101 k if k.is_keyword() => ("keyword", None),
87 _ => { 102 _ => {
103 // let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
88 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) { 104 if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) {
89 if let Some(path) = macro_call.path() { 105 if let Some(path) = macro_call.path() {
90 if let Some(segment) = path.segment() { 106 if let Some(segment) = path.segment() {
@@ -101,6 +117,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
101 res.push(HighlightedRange { 117 res.push(HighlightedRange {
102 range: TextRange::from_to(range_start, range_end), 118 range: TextRange::from_to(range_start, range_end),
103 tag: "macro", 119 tag: "macro",
120 id: None,
104 }) 121 })
105 } 122 }
106 } 123 }
@@ -109,7 +126,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
109 continue; 126 continue;
110 } 127 }
111 }; 128 };
112 res.push(HighlightedRange { range: node.range(), tag }) 129 res.push(HighlightedRange { range: node.range(), tag, id })
113 } 130 }
114 res 131 res
115} 132}
@@ -221,4 +238,18 @@ fn main() {
221 // std::fs::write(dst_file, &actual_html).unwrap(); 238 // std::fs::write(dst_file, &actual_html).unwrap();
222 assert_eq_text!(expected_html, actual_html); 239 assert_eq_text!(expected_html, actual_html);
223 } 240 }
241
242 #[test]
243 fn test_sematic_highlighting() {
244 let (analysis, file_id) = single_file(
245 r#"
246fn main() {
247 let hello = "hello";
248 let x = hello.to_string();
249 let y = hello.to_string();
250}"#,
251 );
252 let result = analysis.highlight(file_id);
253 assert_debug_snapshot_matches!("sematic_highlighting", result);
254 }
224} 255}