Merge pull request #81 from asottile/fix_begin_end_hang

fix highlighting hang with empty begin end rules
This commit is contained in:
Anthony Sottile
2020-07-24 15:26:38 -07:00
committed by GitHub
2 changed files with 40 additions and 5 deletions

View File

@@ -273,6 +273,7 @@ class CompiledRegsetRule(CompiledRule, Protocol):
class Entry(NamedTuple): class Entry(NamedTuple):
scope: Tuple[str, ...] scope: Tuple[str, ...]
rule: CompiledRule rule: CompiledRule
start: Tuple[str, int]
reg: _Reg = ERR_REG reg: _Reg = ERR_REG
boundary: bool = False boundary: bool = False
@@ -284,7 +285,7 @@ def _inner_capture_parse(
scope: Scope, scope: Scope,
rule: CompiledRule, rule: CompiledRule,
) -> Regions: ) -> Regions:
state = State.root(Entry(scope + rule.name, rule)) state = State.root(Entry(scope + rule.name, rule, (s, 0)))
_, regions = highlight_line(compiler, state, s, first_line=False) _, regions = highlight_line(compiler, state, s, first_line=False)
return tuple( return tuple(
r._replace(start=r.start + start, end=r.end + start) for r in regions r._replace(start=r.start + start, end=r.end + start) for r in regions
@@ -440,7 +441,8 @@ class EndRule(NamedTuple):
boundary = match.end() == len(match.string) boundary = match.end() == len(match.string)
reg = make_reg(expand_escaped(match, self.end)) reg = make_reg(expand_escaped(match, self.end))
state = state.push(Entry(next_scope, self, reg, boundary)) start = (match.string, match.start())
state = state.push(Entry(next_scope, self, start, reg, boundary))
regions = _captures(compiler, scope, match, self.begin_captures) regions = _captures(compiler, scope, match, self.begin_captures)
return state, True, regions return state, True, regions
@@ -455,7 +457,16 @@ class EndRule(NamedTuple):
if m.start() > pos: if m.start() > pos:
ret.append(Region(pos, m.start(), state.cur.scope)) ret.append(Region(pos, m.start(), state.cur.scope))
ret.extend(_captures(compiler, state.cur.scope, m, self.end_captures)) ret.extend(_captures(compiler, state.cur.scope, m, self.end_captures))
return state.pop(), m.end(), False, tuple(ret) # this is probably a bug in the grammar, but it pushed and popped at
# the same position.
# we'll advance the highlighter by one position to get past the loop
# this appears to be what vs code does as well
if state.entries[-1].start == (m.string, m.end()):
ret.append(Region(m.end(), m.end() + 1, state.cur.scope))
end = m.end() + 1
else:
end = m.end()
return state.pop(), end, False, tuple(ret)
def search( def search(
self, self,
@@ -501,7 +512,9 @@ class WhileRule(NamedTuple):
boundary = match.end() == len(match.string) boundary = match.end() == len(match.string)
reg = make_reg(expand_escaped(match, self.while_)) reg = make_reg(expand_escaped(match, self.while_))
state = state.push_while(self, Entry(next_scope, self, reg, boundary)) start = (match.string, match.start())
entry = Entry(next_scope, self, start, reg, boundary)
state = state.push_while(self, entry)
regions = _captures(compiler, scope, match, self.begin_captures) regions = _captures(compiler, scope, match, self.begin_captures)
return state, True, regions return state, True, regions
@@ -541,7 +554,7 @@ class Compiler:
self._rule_to_grammar: Dict[_Rule, Grammar] = {} self._rule_to_grammar: Dict[_Rule, Grammar] = {}
self._c_rules: Dict[_Rule, CompiledRule] = {} self._c_rules: Dict[_Rule, CompiledRule] = {}
root = self._compile_root(grammar) root = self._compile_root(grammar)
self.root_state = State.root(Entry(root.name, root)) self.root_state = State.root(Entry(root.name, root, ('', 0)))
def _visit_rule(self, grammar: Grammar, rule: _Rule) -> _Rule: def _visit_rule(self, grammar: Grammar, rule: _Rule) -> _Rule:
self._rule_to_grammar[rule] = grammar self._rule_to_grammar[rule] = grammar

View File

@@ -637,3 +637,25 @@ def test_backslash_z(compiler_state):
assert regions2 == ( assert regions2 == (
Region(0, 6, ('test', 'comment')), Region(0, 6, ('test', 'comment')),
) )
def test_buggy_begin_end_grammar(compiler_state):
# before this would result in an infinite loop of start / end
compiler, state = compiler_state({
'scopeName': 'test',
'patterns': [
{
'begin': '(?=</style)',
'end': '(?=</style)',
'name': 'css',
},
],
})
state, regions = highlight_line(compiler, state, 'test </style', True)
assert regions == (
Region(0, 5, ('test',)),
Region(5, 6, ('test', 'css')),
Region(6, 12, ('test',)),
)