aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/syntax_highlighting.rs
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2020-10-14 18:23:59 +0100
committerLukas Wirth <[email protected]>2020-10-14 18:23:59 +0100
commit8c6dc5f28a5550acffbbb063335833304dac266d (patch)
tree6b5966bf5bff6436a47741ce25ace87812794eed /crates/ide/src/syntax_highlighting.rs
parentdf87be88d8500c8955f882d71467e01a7d4db9ab (diff)
Factor macro_rules! highlighting out
Diffstat (limited to 'crates/ide/src/syntax_highlighting.rs')
-rw-r--r--crates/ide/src/syntax_highlighting.rs135
1 files changed, 18 insertions, 117 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index f430006d7..8ecaff204 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -1,6 +1,7 @@
1mod format; 1mod format;
2mod html; 2mod html;
3mod injection; 3mod injection;
4mod macro_rules;
4mod tags; 5mod tags;
5#[cfg(test)] 6#[cfg(test)]
6mod tests; 7mod tests;
@@ -18,7 +19,10 @@ use syntax::{
18 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, 19 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
19}; 20};
20 21
21use crate::{syntax_highlighting::format::FormatStringHighlighter, FileId}; 22use crate::{
23 syntax_highlighting::{format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter},
24 FileId,
25};
22 26
23pub(crate) use html::highlight_as_html; 27pub(crate) use html::highlight_as_html;
24pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; 28pub use tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag};
@@ -68,8 +72,9 @@ pub(crate) fn highlight(
68 // When we leave a node, the we use it to flatten the highlighted ranges. 72 // When we leave a node, the we use it to flatten the highlighted ranges.
69 let mut stack = HighlightedRangeStack::new(); 73 let mut stack = HighlightedRangeStack::new();
70 74
71 let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None; 75 let mut current_macro_call: Option<ast::MacroCall> = None;
72 let mut format_string_highlighter = FormatStringHighlighter::default(); 76 let mut format_string_highlighter = FormatStringHighlighter::default();
77 let mut macro_rules_highlighter = MacroRulesHighlighter::new();
73 78
74 // Walk all nodes, keeping track of whether we are inside a macro or not. 79 // Walk all nodes, keeping track of whether we are inside a macro or not.
75 // If in macro, expand it first and highlight the expanded code. 80 // If in macro, expand it first and highlight the expanded code.
@@ -99,9 +104,8 @@ pub(crate) fn highlight(
99 binding_hash: None, 104 binding_hash: None,
100 }); 105 });
101 } 106 }
102 let mut is_macro_rules = None;
103 if let Some(name) = mc.is_macro_rules() { 107 if let Some(name) = mc.is_macro_rules() {
104 is_macro_rules = Some(MacroMatcherParseState::new()); 108 macro_rules_highlighter.init();
105 if let Some((highlight, binding_hash)) = highlight_element( 109 if let Some((highlight, binding_hash)) = highlight_element(
106 &sema, 110 &sema,
107 &mut bindings_shadow_count, 111 &mut bindings_shadow_count,
@@ -115,13 +119,14 @@ pub(crate) fn highlight(
115 }); 119 });
116 } 120 }
117 } 121 }
118 current_macro_call = Some((mc.clone(), is_macro_rules)); 122 current_macro_call = Some(mc.clone());
119 continue; 123 continue;
120 } 124 }
121 WalkEvent::Leave(Some(mc)) => { 125 WalkEvent::Leave(Some(mc)) => {
122 assert!(current_macro_call.map(|it| it.0) == Some(mc)); 126 assert!(current_macro_call == Some(mc));
123 current_macro_call = None; 127 current_macro_call = None;
124 format_string_highlighter.reset(); 128 format_string_highlighter.reset();
129 macro_rules_highlighter.reset();
125 } 130 }
126 _ => (), 131 _ => (),
127 } 132 }
@@ -148,20 +153,6 @@ pub(crate) fn highlight(
148 WalkEvent::Leave(_) => continue, 153 WalkEvent::Leave(_) => continue,
149 }; 154 };
150 155
151 // check if in matcher part of a macro_rules rule
152 if let Some((_, Some(ref mut state))) = current_macro_call {
153 if let Some(tok) = element.as_token() {
154 if matches!(
155 update_macro_rules_state(tok, state),
156 RuleState::Matcher | RuleState::Expander
157 ) {
158 if skip_metavariables(element.clone()) {
159 continue;
160 }
161 }
162 }
163 }
164
165 let range = element.text_range(); 156 let range = element.text_range();
166 157
167 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { 158 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
@@ -174,6 +165,9 @@ pub(crate) fn highlight(
174 let parent = token.parent(); 165 let parent = token.parent();
175 166
176 format_string_highlighter.check_for_format_string(&parent); 167 format_string_highlighter.check_for_format_string(&parent);
168 if let Some(tok) = element.as_token() {
169 macro_rules_highlighter.advance(tok);
170 }
177 171
178 // We only care Name and Name_ref 172 // We only care Name and Name_ref
179 match (token.kind(), parent.kind()) { 173 match (token.kind(), parent.kind()) {
@@ -197,7 +191,10 @@ pub(crate) fn highlight(
197 syntactic_name_ref_highlighting, 191 syntactic_name_ref_highlighting,
198 element_to_highlight.clone(), 192 element_to_highlight.clone(),
199 ) { 193 ) {
200 stack.add(HighlightedRange { range, highlight, binding_hash }); 194 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
195 stack.add(HighlightedRange { range, highlight, binding_hash });
196 }
197
201 if let Some(string) = 198 if let Some(string) =
202 element_to_highlight.as_token().cloned().and_then(ast::String::cast) 199 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
203 { 200 {
@@ -867,99 +864,3 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
867 _ => default.into(), 864 _ => default.into(),
868 } 865 }
869} 866}
870
871struct MacroMatcherParseState {
872 /// Opening and corresponding closing bracket of the matcher or expander of the current rule
873 paren_ty: Option<(SyntaxKind, SyntaxKind)>,
874 paren_level: usize,
875 rule_state: RuleState,
876 /// Whether we are inside the outer `{` `}` macro block that holds the rules
877 in_invoc_body: bool,
878}
879
880impl MacroMatcherParseState {
881 fn new() -> Self {
882 MacroMatcherParseState {
883 paren_ty: None,
884 paren_level: 0,
885 in_invoc_body: false,
886 rule_state: RuleState::None,
887 }
888 }
889}
890
891#[derive(Copy, Clone, PartialEq)]
892enum RuleState {
893 Matcher,
894 Expander,
895 Between,
896 None,
897}
898
899impl RuleState {
900 fn transition(&mut self) {
901 *self = match self {
902 RuleState::Matcher => RuleState::Between,
903 RuleState::Expander => RuleState::None,
904 RuleState::Between => RuleState::Expander,
905 RuleState::None => RuleState::Matcher,
906 };
907 }
908}
909
910fn update_macro_rules_state(tok: &SyntaxToken, state: &mut MacroMatcherParseState) -> RuleState {
911 if !state.in_invoc_body {
912 if tok.kind() == T!['{'] {
913 state.in_invoc_body = true;
914 }
915 return state.rule_state;
916 }
917
918 match state.paren_ty {
919 Some((open, close)) => {
920 if tok.kind() == open {
921 state.paren_level += 1;
922 } else if tok.kind() == close {
923 state.paren_level -= 1;
924 if state.paren_level == 0 {
925 let res = state.rule_state;
926 state.rule_state.transition();
927 state.paren_ty = None;
928 return res;
929 }
930 }
931 }
932 None => {
933 match tok.kind() {
934 T!['('] => {
935 state.paren_ty = Some((T!['('], T![')']));
936 }
937 T!['{'] => {
938 state.paren_ty = Some((T!['{'], T!['}']));
939 }
940 T!['['] => {
941 state.paren_ty = Some((T!['['], T![']']));
942 }
943 _ => (),
944 }
945 if state.paren_ty.is_some() {
946 state.paren_level = 1;
947 state.rule_state.transition();
948 }
949 }
950 }
951 state.rule_state
952}
953
954fn skip_metavariables(element: SyntaxElement) -> bool {
955 let tok = match element.as_token() {
956 Some(tok) => tok,
957 None => return false,
958 };
959 let is_fragment = || tok.prev_token().map(|tok| tok.kind()) == Some(T![$]);
960 match tok.kind() {
961 IDENT if is_fragment() => true,
962 kind if kind.is_keyword() && is_fragment() => true,
963 _ => false,
964 }
965}