Skip macro matcher fragment name semantic highlighting

This commit is contained in:
Lukas Wirth 2020-10-10 23:13:16 +02:00
parent cde189c5d5
commit 1416413d69
4 changed files with 124 additions and 4 deletions

View File

@ -68,7 +68,7 @@ pub(crate) fn highlight(
// When we leave a node, the we use it to flatten the highlighted ranges.
let mut stack = HighlightedRangeStack::new();
let mut current_macro_call: Option<ast::MacroCall> = None;
let mut current_macro_call: Option<(ast::MacroCall, Option<MacroMatcherParseState>)> = None;
let mut format_string: Option<SyntaxElement> = None;
// Walk all nodes, keeping track of whether we are inside a macro or not.
@ -92,7 +92,6 @@ pub(crate) fn highlight(
// Track "inside macro" state
match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
WalkEvent::Enter(Some(mc)) => {
current_macro_call = Some(mc.clone());
if let Some(range) = macro_call_range(&mc) {
stack.add(HighlightedRange {
range,
@ -100,7 +99,9 @@ pub(crate) fn highlight(
binding_hash: None,
});
}
let mut is_macro_rules = None;
if let Some(name) = mc.is_macro_rules() {
is_macro_rules = Some(MacroMatcherParseState::new());
if let Some((highlight, binding_hash)) = highlight_element(
&sema,
&mut bindings_shadow_count,
@ -114,10 +115,11 @@ pub(crate) fn highlight(
});
}
}
current_macro_call = Some((mc.clone(), is_macro_rules));
continue;
}
WalkEvent::Leave(Some(mc)) => {
assert!(current_macro_call == Some(mc));
assert!(current_macro_call.map(|it| it.0) == Some(mc));
current_macro_call = None;
format_string = None;
}
@ -146,6 +148,20 @@ pub(crate) fn highlight(
WalkEvent::Leave(_) => continue,
};
// check if in matcher part of a macro_rules rule
if let Some((_, Some(ref mut state))) = current_macro_call {
if let Some(tok) = element.as_token() {
if matches!(
update_macro_rules_state(tok, state),
RuleState::Matcher | RuleState::Expander
) {
if skip_metavariables(element.clone()) {
continue;
}
}
}
}
let range = element.text_range();
let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
@ -918,3 +934,99 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
_ => default.into(),
}
}
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 MacroMatcherParseState {
fn new() -> Self {
MacroMatcherParseState {
paren_ty: None,
paren_level: 0,
in_invoc_body: false,
rule_state: RuleState::None,
}
}
}
#[derive(Copy, Clone, 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_rules_state(tok: &SyntaxToken, state: &mut MacroMatcherParseState) -> RuleState {
if !state.in_invoc_body {
if tok.kind() == T!['{'] {
state.in_invoc_body = true;
}
return state.rule_state;
}
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 {
let res = state.rule_state;
state.rule_state.transition();
state.paren_ty = None;
return res;
}
}
}
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();
}
}
}
state.rule_state
}
fn skip_metavariables(element: SyntaxElement) -> bool {
let tok = match element.as_token() {
Some(tok) => tok,
None => return false,
};
let is_fragment = || tok.prev_token().map(|tok| tok.kind()) == Some(T![$]);
match tok.kind() {
IDENT if is_fragment() => true,
kind if kind.is_keyword() && is_fragment() => true,
_ => false,
}
}

View File

@ -37,7 +37,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
</style>
<pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span>
<span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">{</span>
<span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span><span class="keyword">crate</span><span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
<span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span>
<span class="punctuation">}</span><span class="punctuation">)</span>
<span class="punctuation">}</span>
#[rustc_builtin_macro]

View File

@ -115,6 +115,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="punctuation">}</span>
<span class="punctuation">}</span>
<span class="macro">macro_rules!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span>
<span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span>
<span class="punctuation">}</span>
<span class="comment">// comment</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
<span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span>

View File

@ -89,6 +89,10 @@ macro_rules! noop {
}
}
macro_rules! keyword_frag {
($type:ty) => ($type)
}
// comment
fn main() {
println!("Hello, {}!", 92);