diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index 1a99d8c9..f607a47b 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript 1.6.3 (function() { - var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1, + var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HEREDOC, HEREDOC_ILLEGAL, HEREDOC_INDENT, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVERSES, JSTOKEN, JS_FORBIDDEN, JS_KEYWORDS, LINE_BREAK, LINE_CONTINUER, LOGIC, Lexer, MATH, MULTILINER, MULTI_DENT, NOT_REGEX, NOT_SPACED_REGEX, NUMBER, OPERATOR, REGEX, RELATION, RESERVED, Rewriter, SHIFT, SIMPLESTR, STRICT_PROSCRIBED, TRAILING_SPACES, UNARY, WHITESPACE, compact, count, invertLiterate, key, last, locationDataToString, repeat, starts, throwSyntaxError, _ref, _ref1, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; _ref = require('./rewriter'), Rewriter = _ref.Rewriter, INVERSES = _ref.INVERSES; @@ -351,36 +351,42 @@ this.indents.push(diff); this.ends.push('OUTDENT'); this.outdebt = this.indebt = 0; + this.indent = size; } else if (size < this.baseIndent) { this.error('missing indentation', indent.length); } else { this.indebt = 0; this.outdentToken(this.indent - size, noNewlines, indent.length); } - this.indent = size; return indent.length; }; Lexer.prototype.outdentToken = function(moveOut, noNewlines, outdentLength) { - var dent, len; + var decreasedIndent, dent, lastIndent, _ref2; + decreasedIndent = this.indent - moveOut; while (moveOut > 0) { - len = this.indents.length - 1; - if (this.indents[len] === void 0) { + lastIndent = this.indents[this.indents.length - 1]; + if (!lastIndent) { moveOut = 0; - } else if (this.indents[len] === this.outdebt) { + } else if (lastIndent === this.outdebt) { moveOut -= this.outdebt; this.outdebt = 0; - } else if (this.indents[len] < this.outdebt) { - this.outdebt -= this.indents[len]; - moveOut -= this.indents[len]; + } else if (lastIndent < this.outdebt) { + this.outdebt -= lastIndent; + moveOut -= lastIndent; } else { dent = this.indents.pop() + this.outdebt; - moveOut -= dent; + if (outdentLength && (_ref2 = this.chunk[outdentLength], __indexOf.call(INDENTABLE_CLOSERS, _ref2) >= 0)) { + decreasedIndent -= dent - moveOut; + moveOut = dent; + } this.outdebt = 0; this.pair('OUTDENT'); - this.token('OUTDENT', dent, 0, outdentLength); + this.token('OUTDENT', moveOut, 0, outdentLength); + moveOut -= dent; } } + this.indent = decreasedIndent; if (dent) { this.outdebt -= moveOut; } @@ -685,13 +691,12 @@ }; Lexer.prototype.pair = function(tag) { - var size, wanted; + var wanted; if (tag !== (wanted = last(this.ends))) { if ('OUTDENT' !== wanted) { this.error("unmatched " + tag); } - this.indent -= size = last(this.indents); - this.outdentToken(size, true); + this.outdentToken(last(this.indents), true); return this.pair(tag); } return this.ends.pop(); @@ -910,4 +915,6 @@ LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR']; + INDENTABLE_CLOSERS = [')', '}', ']']; + }).call(this); diff --git a/src/lexer.coffee b/src/lexer.coffee index 3356d875..1abf6003 100644 --- a/src/lexer.coffee +++ b/src/lexer.coffee @@ -327,37 +327,43 @@ exports.Lexer = class Lexer @indents.push diff @ends.push 'OUTDENT' @outdebt = @indebt = 0 + @indent = size else if size < @baseIndent @error 'missing indentation', indent.length else @indebt = 0 @outdentToken @indent - size, noNewlines, indent.length - @indent = size indent.length # Record an outdent token or multiple tokens, if we happen to be moving back - # inwards past several recorded indents. + # inwards past several recorded indents. Sets new @indent value. outdentToken: (moveOut, noNewlines, outdentLength) -> + decreasedIndent = @indent - moveOut while moveOut > 0 - len = @indents.length - 1 - if @indents[len] is undefined + lastIndent = @indents[@indents.length - 1] + if not lastIndent moveOut = 0 - else if @indents[len] is @outdebt + else if lastIndent is @outdebt moveOut -= @outdebt @outdebt = 0 - else if @indents[len] < @outdebt - @outdebt -= @indents[len] - moveOut -= @indents[len] + else if lastIndent < @outdebt + @outdebt -= lastIndent + moveOut -= lastIndent else dent = @indents.pop() + @outdebt - moveOut -= dent + if outdentLength and @chunk[outdentLength] in INDENTABLE_CLOSERS + decreasedIndent -= dent - moveOut + moveOut = dent @outdebt = 0 + # pair might call outdentToken, so preserve decreasedIndent @pair 'OUTDENT' - @token 'OUTDENT', dent, 0, outdentLength + @token 'OUTDENT', moveOut, 0, outdentLength + moveOut -= dent @outdebt -= moveOut if dent @tokens.pop() while @value() is ';' @token 'TERMINATOR', '\n', outdentLength, 0 unless @tag() is 'TERMINATOR' or noNewlines + @indent = decreasedIndent this # Matches and consumes non-meaningful whitespace. Tag the previous token @@ -606,8 +612,7 @@ exports.Lexer = class Lexer # el.click((event) -> # el.hide()) # - @indent -= size = last @indents - @outdentToken size, true + @outdentToken last(@indents), true return @pair tag @ends.pop() @@ -881,3 +886,6 @@ INDEXABLE = CALLABLE.concat 'NUMBER', 'BOOL', 'NULL', 'UNDEFINED' # occurs at the start of a line. We disambiguate these from trailing whens to # avoid an ambiguity in the grammar. LINE_BREAK = ['INDENT', 'OUTDENT', 'TERMINATOR'] + +# Additional indent in front of these is ignored. +INDENTABLE_CLOSERS = [')', '}', ']'] diff --git a/test/formatting.coffee b/test/formatting.coffee index 8ca2f19e..ce398f8e 100644 --- a/test/formatting.coffee +++ b/test/formatting.coffee @@ -199,7 +199,7 @@ test "#1299: Disallow token misnesting", -> test "#2981: Enforce initial indentation", -> try - CoffeeScript.compile ' a\nb' + CoffeeScript.compile ' a\nb-' ok no catch e eq 'missing indentation', e.message @@ -212,3 +212,16 @@ test "'single-line' expression containing multiple lines", -> then -b else null """ + +test "#1275: allow indentation before closing brackets", -> + array = [ + 1 + 2 + 3 + ] + eq array, array + do -> + ( + a = 1 + ) + eq 1, a