1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
//! Syntax highlighting for macro_rules!.
use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
use crate::{HlRange, HlTag};
#[derive(Default)]
pub(super) struct MacroHighlighter {
state: Option<MacroMatcherParseState>,
}
impl MacroHighlighter {
pub(super) fn init(&mut self) {
self.state = Some(MacroMatcherParseState::default());
}
pub(super) fn advance(&mut self, token: &SyntaxToken) {
if let Some(state) = self.state.as_mut() {
update_macro_state(state, token);
}
}
pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HlRange> {
if let Some(state) = self.state.as_ref() {
if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) {
if let Some(range) = is_metavariable(element) {
return Some(HlRange {
range,
highlight: HlTag::UnresolvedReference.into(),
binding_hash: None,
});
}
}
}
None
}
}
struct MacroMatcherParseState {
/// Opening and corresponding closing bracket of the matcher or expander of the current rule
paren_ty: Option<(SyntaxKind, SyntaxKind)>,
paren_level: usize,
rule_state: RuleState,
/// Whether we are inside the outer `{` `}` macro block that holds the rules
in_invoc_body: bool,
}
impl Default for MacroMatcherParseState {
fn default() -> Self {
MacroMatcherParseState {
paren_ty: None,
paren_level: 0,
in_invoc_body: false,
rule_state: RuleState::None,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum RuleState {
Matcher,
Expander,
Between,
None,
}
impl RuleState {
fn transition(&mut self) {
*self = match self {
RuleState::Matcher => RuleState::Between,
RuleState::Expander => RuleState::None,
RuleState::Between => RuleState::Expander,
RuleState::None => RuleState::Matcher,
};
}
}
fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
if !state.in_invoc_body {
if tok.kind() == T!['{'] || tok.kind() == T!['('] {
state.in_invoc_body = true;
}
return;
}
match state.paren_ty {
Some((open, close)) => {
if tok.kind() == open {
state.paren_level += 1;
} else if tok.kind() == close {
state.paren_level -= 1;
if state.paren_level == 0 {
state.rule_state.transition();
state.paren_ty = None;
}
}
}
None => {
match tok.kind() {
T!['('] => {
state.paren_ty = Some((T!['('], T![')']));
}
T!['{'] => {
state.paren_ty = Some((T!['{'], T!['}']));
}
T!['['] => {
state.paren_ty = Some((T!['['], T![']']));
}
_ => (),
}
if state.paren_ty.is_some() {
state.paren_level = 1;
state.rule_state.transition();
}
}
}
}
fn is_metavariable(element: SyntaxElement) -> Option<TextRange> {
let tok = element.as_token()?;
match tok.kind() {
kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) {
return Some(tok.text_range());
}
}
_ => (),
};
None
}
|