diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index 007bb870dd..0049bf538a 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -26,6 +26,7 @@ this.tagPostfixConditionals(); this.addImplicitBracesAndParens(); this.addLocationDataToGeneratedTokens(); + this.fixOutdentLocationData(); return this.tokens; }; @@ -372,6 +373,23 @@ }); }; + Rewriter.prototype.fixOutdentLocationData = function() { + return this.scanTokens(function(token, i, tokens) { + var prevLocationData; + if (token[0] !== 'OUTDENT') { + return 1; + } + prevLocationData = tokens[i - 1][2]; + token[2] = { + first_line: prevLocationData.last_line, + first_column: prevLocationData.last_column, + last_line: prevLocationData.last_line, + last_column: prevLocationData.last_column + }; + return 1; + }); + }; + Rewriter.prototype.normalizeLines = function() { var action, condition, indent, outdent, starter; starter = indent = outdent = null; diff --git a/src/rewriter.coffee b/src/rewriter.coffee index fddd5df762..941b5e3682 100644 --- a/src/rewriter.coffee +++ b/src/rewriter.coffee @@ -33,6 +33,7 @@ class exports.Rewriter @tagPostfixConditionals() @addImplicitBracesAndParens() @addLocationDataToGeneratedTokens() + @fixOutdentLocationData() @tokens # Rewrite the token stream, looking one token ahead and behind. @@ -368,6 +369,20 @@ class exports.Rewriter last_column: column return 1 + # OUTDENT tokens should always be positioned at the last character of the + # previous token, so that AST nodes ending in an OUTDENT token end up with a + # location corresponding to the last "real" token under the node. + fixOutdentLocationData: -> + @scanTokens (token, i, tokens) -> + return 1 unless token[0] is 'OUTDENT' + prevLocationData = tokens[i - 1][2] + token[2] = + first_line: prevLocationData.last_line + first_column: prevLocationData.last_column + last_line: prevLocationData.last_line + last_column: prevLocationData.last_column + return 1 + # Because our grammar is LALR(1), it can't handle some single-line # expressions that lack ending delimiters. The **Rewriter** adds the implicit # blocks, so it doesn't need to. To keep the grammar clean and tidy, trailing diff --git a/test/location.coffee b/test/location.coffee index 35b9584c51..1782946d82 100644 --- a/test/location.coffee +++ b/test/location.coffee @@ -469,6 +469,24 @@ test "Verify tokens have locations that are in order", -> ok token[2].first_column >= lastToken[2].last_column lastToken = token +test "Verify OUTDENT tokens are located at the end of the previous token", -> + source = ''' + SomeArr = [ -> + if something + lol = + count: 500 + ] + ''' + tokens = CoffeeScript.tokens source + [..., number, curly, outdent1, outdent2, outdent3, bracket, terminator] = tokens + eq number[0], 'NUMBER' + for outdent in [outdent1, outdent2, outdent3] + eq outdent[0], 'OUTDENT' + eq outdent[2].first_line, number[2].last_line + eq outdent[2].first_column, number[2].last_column + eq outdent[2].last_line, number[2].last_line + eq outdent[2].last_column, number[2].last_column + test "Verify all tokens get a location", -> doesNotThrow -> tokens = CoffeeScript.tokens testScript