From 0d8a6782b399b438e98c6ba8def3e08e4d5bd9a5 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Kevin Sawicki Date: Mon, 29 Apr 2013 12:10:16 -0700 Subject: [PATCH] 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 --- spec/app/text-mate-grammar-spec.coffee | 23 +++++++++++++++++++++++ src/app/text-mate-grammar.coffee | 16 +++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index eb4239e80..d98d6ca06 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -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 diff --git a/src/app/text-mate-grammar.coffee b/src/app/text-mate-grammar.coffee index b600147a7..cf48323c0 100644 --- a/src/app/text-mate-grammar.coffee +++ b/src/app/text-mate-grammar.coffee @@ -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 }