Fixes chaining after inline implicit objects

This commit is contained in:
xixixao
2014-01-26 22:06:48 +00:00
parent cc1b74f11b
commit 04b0b94a8c
3 changed files with 94 additions and 82 deletions

View File

@@ -152,7 +152,7 @@
var stack;
stack = [];
return this.scanTokens(function(token, i, tokens) {
var endAllImplicitCalls, endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, nextTag, offset, prevTag, prevToken, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
var endImplicitCall, endImplicitObject, forward, inImplicit, inImplicitCall, inImplicitControl, inImplicitObject, newLine, nextTag, offset, prevTag, prevToken, s, sameLine, stackIdx, stackTag, stackTop, startIdx, startImplicitCall, startImplicitObject, startsLine, tag, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
tag = token[0];
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
@@ -197,11 +197,6 @@
tokens.splice(i, 0, generate('CALL_END', ')'));
return i += 1;
};
endAllImplicitCalls = function() {
while (inImplicitCall()) {
endImplicitCall();
}
};
startImplicitObject = function(j, startsLine) {
var idx;
if (startsLine == null) {
@@ -283,7 +278,7 @@
while (this.tag(s - 2) === 'HERECOMMENT') {
s -= 2;
}
this.objectValueIsFor = nextTag === 'FOR';
this.insideForDeclaration = nextTag === 'FOR';
startsLine = s === 0 || (_ref2 = this.tag(s - 1), __indexOf.call(LINEBREAKS, _ref2) >= 0) || tokens[s - 1].newLine;
if (stackTop()) {
_ref3 = stackTop(), stackTag = _ref3[0], stackIdx = _ref3[1];
@@ -294,19 +289,16 @@
startImplicitObject(s, !!startsLine);
return forward(2);
}
if (inImplicitCall() && __indexOf.call(CALL_CLOSERS, tag) >= 0 && (prevTag === 'OUTDENT' || prevToken.newLine)) {
endAllImplicitCalls();
return forward(1);
}
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
stackTop()[2].sameLine = false;
}
if (__indexOf.call(IMPLICIT_END, tag) >= 0) {
newLine = prevTag === 'OUTDENT' || prevToken.newLine;
if (__indexOf.call(IMPLICIT_END, tag) >= 0 || __indexOf.call(CALL_CLOSERS, tag) >= 0 && newLine) {
while (inImplicit()) {
_ref4 = stackTop(), stackTag = _ref4[0], stackIdx = _ref4[1], (_ref5 = _ref4[2], sameLine = _ref5.sameLine, startsLine = _ref5.startsLine);
if (inImplicitCall() && prevTag !== ',') {
endImplicitCall();
} else if (inImplicitObject() && !this.objectValueIsFor && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && endImplicitObject()) {
} else if (inImplicitObject() && !this.insideForDeclaration && sameLine && tag !== 'TERMINATOR' && prevTag !== ':' && endImplicitObject()) {
} else if (inImplicitObject() && tag === 'TERMINATOR' && prevTag !== ',' && !(startsLine && this.looksObjectish(i + 1))) {
endImplicitObject();
@@ -315,7 +307,7 @@
}
}
}
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !this.objectValueIsFor && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
if (tag === ',' && !this.looksObjectish(i + 1) && inImplicitObject() && !this.insideForDeclaration && (nextTag !== 'TERMINATOR' || !this.looksObjectish(i + 2))) {
offset = nextTag === 'OUTDENT' ? 1 : 0;
while (inImplicitObject()) {
endImplicitObject(i + offset);

View File

@@ -160,11 +160,6 @@ class exports.Rewriter
tokens.splice i, 0, generate 'CALL_END', ')'
i += 1
endAllImplicitCalls = ->
while inImplicitCall()
endImplicitCall()
return
startImplicitObject = (j, startsLine = yes) ->
idx = j ? i
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
@@ -289,15 +284,11 @@ class exports.Rewriter
# f a
# .g b
# .h a
#
if inImplicitCall() and tag in CALL_CLOSERS and
(prevTag is 'OUTDENT' or prevToken.newLine)
endAllImplicitCalls()
return forward(1)
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
if tag in IMPLICIT_END
newLine = prevTag is 'OUTDENT' or prevToken.newLine
if tag in IMPLICIT_END or tag in CALL_CLOSERS and newLine
while inImplicit()
[stackTag, stackIdx, {sameLine, startsLine}] = stackTop()
# Close implicit calls when reached end of argument list
@@ -495,4 +486,4 @@ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADIN
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
# Tokens that close open calls when they follow a newline.
CALL_CLOSERS = ['.', '?.', '::', '?::']
CALL_CLOSERS = ['.', '?.', '::', '?::']

View File

@@ -7,13 +7,6 @@
# string literals -> string literals
# function invocations -> function invocations
# * Line Continuation
# * Property Accesss
# * Operators
# * Array Literals
# * Function Invocations
# * String Literals
doesNotThrow -> CoffeeScript.compile "a = then b"
test "multiple semicolon-separated statements in parentheticals", ->
@@ -21,7 +14,12 @@ test "multiple semicolon-separated statements in parentheticals", ->
eq nonce, (1; 2; nonce)
eq nonce, (-> return (1; 2; nonce))()
# Line Continuation
# * Line Continuation
# * Property Accesss
# * Operators
# * Array Literals
# * Function Invocations
# * String Literals
# Property Access
@@ -57,54 +55,6 @@ test "chained accesses split on period/newline, backwards and forwards", ->
reverse()
.reverse()
test "#1495, method call chaining", ->
str = 'abc'
result = str.split ''
.join ', '
eq 'a, b, c', result
result = str
.split ''
.join ', '
eq 'a, b, c', result
eq 'a, b, c', (str
.split ''
.join ', '
)
eq 'abc',
'aaabbbccc'.replace /(\w)\1\1/g, '$1$1'
.replace /([abc])\1/g, '$1'
# Nested calls
result = [1..3]
.slice Math.max 0, 1
.concat [3]
arrayEq result, [2, 3, 3]
# Single line function arguments.
result = [1..6]
.map (x) -> x * x
.filter (x) -> x % 2 is 0
.reverse()
arrayEq result, [36, 16, 4]
# The parens are forced
result = str.split(''.
split ''
.join ''
).join ', '
eq 'a, b, c', result
test "chaining after outdent", ->
str = 'abc'
zero = parseInt str.replace /\w/, (letter) ->
0
.toString()
eq '0', zero
# Operators
test "newline suppression for operators", ->
@@ -169,6 +119,85 @@ test "indented heredoc", ->
""")
eq "abc", result
# Chaining - all open calls are closed by property access starting a new line
# * chaining after
# * indented argument
# * function block
# * indented object
#
# * single line arguments
# * inline function literal
# * inline object literal
test "chaining after outdent", ->
id = (x) -> x
# indented argument
ff = id parseInt "ff",
16
.toString()
eq '255', ff
# function block
str = 'abc'
zero = parseInt str.replace /\w/, (letter) ->
0
.toString()
eq '0', zero
# indented object
a = id id
a: 1
.a
eq 1, a
test "#1495, method call chaining", ->
str = 'abc'
result = str.split ''
.join ', '
eq 'a, b, c', result
result = str
.split ''
.join ', '
eq 'a, b, c', result
eq 'a, b, c', (str
.split ''
.join ', '
)
eq 'abc',
'aaabbbccc'.replace /(\w)\1\1/g, '$1$1'
.replace /([abc])\1/g, '$1'
# Nested calls
result = [1..3]
.slice Math.max 0, 1
.concat [3]
arrayEq [2, 3, 3], result
# Single line function arguments
result = [1..6]
.map (x) -> x * x
.filter (x) -> x % 2 is 0
.reverse()
arrayEq [36, 16, 4], result
# Single line implicit objects
id = (x) -> x
result = id a: 1
.a
eq 1, result
# The parens are forced
result = str.split(''.
split ''
.join ''
).join ', '
eq 'a, b, c', result
# Nested blocks caused by paren unwrapping
test "#1492: Nested blocks don't cause double semicolons", ->
js = CoffeeScript.compile '(0;0)'