Stop tokenizing line when the same rule is pushed more than once

Previously Rails classes would infinitly loop if the Ruby on Rails
grammar was loaded but the Ruby grammar had not been. This occurred
because a rule was continually pushing itself on the stack but never
advancing.

Now if the position does not advance and the last two rules in the
stack have the same scope the last rule is popped and the entire line
is tokenized with the current scopes.

Closes #524 #486
This commit is contained in:
Corey Johnson & Kevin Sawicki
2013-04-29 12:10:16 -07:00
parent fe219ed159
commit 0d8a6782b3
2 changed files with 36 additions and 3 deletions

View File

@@ -488,6 +488,29 @@ describe "TextMateGrammar", ->
expect(tokens[2].value).toBe "SELECT"
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
describe "when the position doesn't advance and rule includes $self and matches itself", ->
it "token the entire line using the rule", ->
grammar = new TextMateGrammar
name: "test"
scopeName: "source"
repository: {}
patterns: [
{
name: "text"
begin: "(?=forever)"
end: "whatevs"
patterns: [
include: "$self"
]
}
]
{tokens} = grammar.tokenizeLine("forever and ever")
expect(tokens.length).toBe 1
expect(tokens[0].value).toBe "forever and ever"
expect(tokens[0].scopes).toEqual ["source", "text"]
describe "language-specific integration tests", ->
lines = null

View File

@@ -155,9 +155,19 @@ class TextMateGrammar
))
break
if position == previousPosition and ruleStack.length == previousRuleStackLength
console.error("Popping rule because it loops at column #{position} of line '#{line}'", _.clone(ruleStack))
ruleStack.pop()
if position == previousPosition
if ruleStack.length == previousRuleStackLength
console.error("Popping rule because it loops at column #{position} of line '#{line}'", _.clone(ruleStack))
ruleStack.pop()
[penultimateRule, lastRule] = ruleStack[-2..]
if lastRule? and penultimateRule.scopeName == lastRule.scopeName
ruleStack.pop()
tokens.push(new Token(
value: line[position...line.length]
scopes: scopes
))
break
ruleStack.forEach (rule) -> rule.clearAnchorPosition()
{ tokens, ruleStack }