aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-02-27 15:48:06 +0000
committerGitHub <[email protected]>2020-02-27 15:48:06 +0000
commitde492d439f0cda73fe2d9b25e4d9f028f70eb095 (patch)
tree6c7053d80c08ba65266a86771e965ce572521c1f
parent062c12e3cdad887d751defa6f448edb5426ebf01 (diff)
parentc6247f74c72857de3619a080698237d58ff9e960 (diff)
Merge pull request #3349 from matklad/yo-dawg
Put syntax highlighting into syntax highlighting
-rw-r--r--crates/ra_hir_ty/src/tests.rs4
-rw-r--r--crates/ra_ide/src/call_info.rs34
-rw-r--r--crates/ra_ide/src/mock_analysis.rs16
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs59
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs30
5 files changed, 120 insertions, 23 deletions
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 240cc03a2..087edcc92 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -51,8 +51,8 @@ fn type_at(content: &str) -> String {
51 type_at_pos(&db, file_pos) 51 type_at_pos(&db, file_pos)
52} 52}
53 53
54fn infer(content: &str) -> String { 54fn infer(ra_fixture: &str) -> String {
55 infer_with_mismatches(content, false) 55 infer_with_mismatches(ra_fixture, false)
56} 56}
57 57
58fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { 58fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index 9a1fc0d35..2b35a3803 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -3,7 +3,7 @@ use hir::Semantics;
3use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
6 match_ast, AstNode, SyntaxNode, 6 match_ast, AstNode, SyntaxNode, SyntaxToken,
7}; 7};
8use test_utils::tested_by; 8use test_utils::tested_by;
9 9
@@ -16,7 +16,13 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
16 let file = file.syntax(); 16 let file = file.syntax();
17 let token = file.token_at_offset(position.offset).next()?; 17 let token = file.token_at_offset(position.offset).next()?;
18 let token = sema.descend_into_macros(token); 18 let token = sema.descend_into_macros(token);
19 call_info_for_token(&sema, token)
20}
19 21
22pub(crate) fn call_info_for_token(
23 sema: &Semantics<RootDatabase>,
24 token: SyntaxToken,
25) -> Option<CallInfo> {
20 // Find the calling expression and it's NameRef 26 // Find the calling expression and it's NameRef
21 let calling_node = FnCallNode::with_node(&token.parent())?; 27 let calling_node = FnCallNode::with_node(&token.parent())?;
22 28
@@ -27,21 +33,23 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
27 match callable_def { 33 match callable_def {
28 hir::CallableDef::FunctionId(it) => { 34 hir::CallableDef::FunctionId(it) => {
29 let fn_def = it.into(); 35 let fn_def = it.into();
30 (CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db)) 36 (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
37 }
38 hir::CallableDef::StructId(it) => {
39 (CallInfo::with_struct(sema.db, it.into())?, false)
31 } 40 }
32 hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false),
33 hir::CallableDef::EnumVariantId(it) => { 41 hir::CallableDef::EnumVariantId(it) => {
34 (CallInfo::with_enum_variant(db, it.into())?, false) 42 (CallInfo::with_enum_variant(sema.db, it.into())?, false)
35 } 43 }
36 } 44 }
37 } 45 }
38 FnCallNode::MethodCallExpr(method_call) => { 46 FnCallNode::MethodCallExpr(method_call) => {
39 let function = sema.resolve_method_call(&method_call)?; 47 let function = sema.resolve_method_call(&method_call)?;
40 (CallInfo::with_fn(db, function), function.has_self_param(db)) 48 (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
41 } 49 }
42 FnCallNode::MacroCallExpr(macro_call) => { 50 FnCallNode::MacroCallExpr(macro_call) => {
43 let macro_def = sema.resolve_macro_call(&macro_call)?; 51 let macro_def = sema.resolve_macro_call(&macro_call)?;
44 (CallInfo::with_macro(db, macro_def)?, false) 52 (CallInfo::with_macro(sema.db, macro_def)?, false)
45 } 53 }
46 }; 54 };
47 55
@@ -61,7 +69,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
61 let num_args_at_callsite = arg_list.args().count(); 69 let num_args_at_callsite = arg_list.args().count();
62 70
63 let arg_list_range = arg_list.syntax().text_range(); 71 let arg_list_range = arg_list.syntax().text_range();
64 if !arg_list_range.contains_inclusive(position.offset) { 72 if !arg_list_range.contains_inclusive(token.text_range().start()) {
65 tested_by!(call_info_bad_offset); 73 tested_by!(call_info_bad_offset);
66 return None; 74 return None;
67 } 75 }
@@ -70,7 +78,9 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
70 num_args_at_callsite, 78 num_args_at_callsite,
71 arg_list 79 arg_list
72 .args() 80 .args()
73 .take_while(|arg| arg.syntax().text_range().end() < position.offset) 81 .take_while(|arg| {
82 arg.syntax().text_range().end() < token.text_range().start()
83 })
74 .count(), 84 .count(),
75 ); 85 );
76 86
@@ -100,7 +110,13 @@ impl FnCallNode {
100 match_ast! { 110 match_ast! {
101 match node { 111 match node {
102 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) }, 112 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
103 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) }, 113 ast::MethodCallExpr(it) => {
114 let arg_list = it.arg_list()?;
115 if !syntax.text_range().is_subrange(&arg_list.syntax().text_range()) {
116 return None;
117 }
118 Some(FnCallNode::MethodCallExpr(it))
119 },
104 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) }, 120 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
105 _ => { None }, 121 _ => { None },
106 } 122 }
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 081aaee8c..f4cd6deb7 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -124,28 +124,28 @@ impl MockAnalysis {
124} 124}
125 125
126/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 126/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
127pub fn analysis_and_position(fixture: &str) -> (Analysis, FilePosition) { 127pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
128 let (mock, position) = MockAnalysis::with_files_and_position(fixture); 128 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
129 (mock.analysis(), position) 129 (mock.analysis(), position)
130} 130}
131 131
132/// Creates analysis for a single file. 132/// Creates analysis for a single file.
133pub fn single_file(code: &str) -> (Analysis, FileId) { 133pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
134 let mut mock = MockAnalysis::new(); 134 let mut mock = MockAnalysis::new();
135 let file_id = mock.add_file("/main.rs", code); 135 let file_id = mock.add_file("/main.rs", ra_fixture);
136 (mock.analysis(), file_id) 136 (mock.analysis(), file_id)
137} 137}
138 138
139/// Creates analysis for a single file, returns position marked with <|>. 139/// Creates analysis for a single file, returns position marked with <|>.
140pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { 140pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) {
141 let mut mock = MockAnalysis::new(); 141 let mut mock = MockAnalysis::new();
142 let pos = mock.add_file_with_position("/main.rs", code); 142 let pos = mock.add_file_with_position("/main.rs", ra_fixture);
143 (mock.analysis(), pos) 143 (mock.analysis(), pos)
144} 144}
145 145
146/// Creates analysis for a single file, returns range marked with a pair of <|>. 146/// Creates analysis for a single file, returns range marked with a pair of <|>.
147pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) { 147pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) {
148 let mut mock = MockAnalysis::new(); 148 let mut mock = MockAnalysis::new();
149 let pos = mock.add_file_with_range("/main.rs", code); 149 let pos = mock.add_file_with_range("/main.rs", ra_fixture);
150 (mock.analysis(), pos) 150 (mock.analysis(), pos)
151} 151}
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 796f0e545..3a5cbee9b 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -12,11 +12,12 @@ use ra_ide_db::{
12}; 12};
13use ra_prof::profile; 13use ra_prof::profile;
14use ra_syntax::{ 14use ra_syntax::{
15 ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, TextRange, WalkEvent, T, 15 ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, SyntaxToken,
16 TextRange, WalkEvent, T,
16}; 17};
17use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
18 19
19use crate::{references::classify_name_ref, FileId}; 20use crate::{call_info::call_info_for_token, references::classify_name_ref, Analysis, FileId};
20 21
21pub(crate) use html::highlight_as_html; 22pub(crate) use html::highlight_as_html;
22pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; 23pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
@@ -94,11 +95,12 @@ pub(crate) fn highlight(
94 WalkEvent::Enter(it) => it, 95 WalkEvent::Enter(it) => it,
95 WalkEvent::Leave(_) => continue, 96 WalkEvent::Leave(_) => continue,
96 }; 97 };
98
97 let range = element.text_range(); 99 let range = element.text_range();
98 100
99 let element_to_highlight = if current_macro_call.is_some() { 101 let element_to_highlight = if current_macro_call.is_some() {
100 // Inside a macro -- expand it first 102 // Inside a macro -- expand it first
101 let token = match element.into_token() { 103 let token = match element.clone().into_token() {
102 Some(it) if it.parent().kind() == TOKEN_TREE => it, 104 Some(it) if it.parent().kind() == TOKEN_TREE => it,
103 _ => continue, 105 _ => continue,
104 }; 106 };
@@ -110,9 +112,17 @@ pub(crate) fn highlight(
110 _ => token.into(), 112 _ => token.into(),
111 } 113 }
112 } else { 114 } else {
113 element 115 element.clone()
114 }; 116 };
115 117
118 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
119 let expanded = element_to_highlight.as_token().unwrap().clone();
120 if highlight_injection(&mut res, &sema, token, expanded).is_some() {
121 eprintln!("res = {:?}", res);
122 continue;
123 }
124 }
125
116 if let Some((highlight, binding_hash)) = 126 if let Some((highlight, binding_hash)) =
117 highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight) 127 highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight)
118 { 128 {
@@ -281,3 +291,44 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
281 _ => default, 291 _ => default,
282 } 292 }
283} 293}
294
295fn highlight_injection(
296 acc: &mut Vec<HighlightedRange>,
297 sema: &Semantics<RootDatabase>,
298 literal: ast::RawString,
299 expanded: SyntaxToken,
300) -> Option<()> {
301 let call_info = call_info_for_token(&sema, expanded)?;
302 let idx = call_info.active_parameter?;
303 let name = call_info.signature.parameter_names.get(idx)?;
304 if name != "ra_fixture" {
305 return None;
306 }
307 let value = literal.value()?;
308 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
309
310 if let Some(range) = literal.open_quote_text_range() {
311 acc.push(HighlightedRange {
312 range,
313 highlight: HighlightTag::LiteralString.into(),
314 binding_hash: None,
315 })
316 }
317
318 for mut h in analysis.highlight(tmp_file_id).unwrap() {
319 if let Some(r) = literal.map_range_up(h.range) {
320 h.range = r;
321 acc.push(h)
322 }
323 }
324
325 if let Some(range) = literal.close_quote_text_range() {
326 acc.push(HighlightedRange {
327 range,
328 highlight: HighlightTag::LiteralString.into(),
329 binding_hash: None,
330 })
331 }
332
333 Some(())
334}
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index ed8661faf..693b35feb 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -171,6 +171,36 @@ impl RawString {
171 let inside_str = &text[start_of_inside..end_of_inside]; 171 let inside_str = &text[start_of_inside..end_of_inside];
172 Some(inside_str.to_string()) 172 Some(inside_str.to_string())
173 } 173 }
174
175 pub fn open_quote_text_range(&self) -> Option<TextRange> {
176 let text = self.text().as_str();
177 let usual_string_range = find_usual_string_range(text)?;
178
179 let start = self.syntax().text_range().start();
180 let len = usual_string_range.start() + TextUnit::of_char('"');
181 Some(TextRange::offset_len(start, len))
182 }
183
184 pub fn close_quote_text_range(&self) -> Option<TextRange> {
185 let text = self.text().as_str();
186 let usual_string_range = find_usual_string_range(text)?;
187
188 let end = self.syntax().text_range().end();
189 let len = TextUnit::of_str(text) - usual_string_range.end();
190 Some(TextRange::from_to(end - len, end))
191 }
192
193 pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
194 // FIXME: handle escapes here properly
195 let text = self.text().as_str();
196 let usual_string_range = find_usual_string_range(text)?;
197 Some(
198 range
199 + self.syntax().text_range().start()
200 + TextUnit::of_char('"')
201 + usual_string_range.start(),
202 )
203 }
174} 204}
175 205
176fn find_usual_string_range(s: &str) -> Option<TextRange> { 206fn find_usual_string_range(s: &str) -> Option<TextRange> {