Skip to content

Commit 563f14b

Browse files
committed
Merge pull request #3263 from xixixao/issue1495
Implement #1495, Method call chaining
2 parents 4eee9c3 + ee9febe commit 563f14b

File tree

3 files changed

+97
-18
lines changed

3 files changed

+97
-18
lines changed

lib/coffee-script/rewriter.js

+24-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/rewriter.coffee

+23-5
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class exports.Rewriter
131131

132132
@scanTokens (token, i, tokens) ->
133133
[tag] = token
134-
[prevTag] = if i > 0 then tokens[i - 1] else []
134+
[prevTag] = prevToken = if i > 0 then tokens[i - 1] else []
135135
[nextTag] = if i < tokens.length - 1 then tokens[i + 1] else []
136136
stackTop = -> stack[stack.length - 1]
137137
startIdx = i
@@ -159,6 +159,10 @@ class exports.Rewriter
159159
tokens.splice i, 0, generate 'CALL_END', ')'
160160
i += 1
161161

162+
endAllImplicitCalls = ->
163+
while inImplicitCall()
164+
endImplicitCall()
165+
162166
startImplicitObject = (j, startsLine = yes) ->
163167
idx = j ? i
164168
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
@@ -275,9 +279,19 @@ class exports.Rewriter
275279
# c
276280
# .h a
277281
#
278-
if prevTag is 'OUTDENT' and inImplicitCall() and tag in ['.', '?.', '::', '?::']
279-
endImplicitCall()
280-
return forward(1)
282+
# and also
283+
#
284+
# f a
285+
# .g b
286+
# .h a
287+
#
288+
if inImplicitCall() and tag in CALL_CLOSERS
289+
if prevTag is 'OUTDENT'
290+
endImplicitCall()
291+
return forward(1)
292+
if prevToken.newLine
293+
endAllImplicitCalls()
294+
return forward(1)
281295

282296
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
283297

@@ -356,7 +370,8 @@ class exports.Rewriter
356370
token[1] isnt ';' and token[0] in SINGLE_CLOSERS and
357371
not (token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE) and
358372
not (token[0] is 'ELSE' and starter isnt 'THEN') and
359-
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>'])
373+
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
374+
token[0] in CALL_CLOSERS and @tokens[i - 1].newLine
360375

361376
action = (token, i) ->
362377
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
@@ -471,3 +486,6 @@ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADIN
471486

472487
# Tokens that end a line.
473488
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
489+
490+
# Tokens that close open calls when they follow a newline.
491+
CALL_CLOSERS = ['.', '?.', '::', '?::']

test/formatting.coffee

+50-5
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,60 @@ test "chained accesses split on period/newline, backwards and forwards", ->
4444
.reverse()
4545
.reverse()
4646
arrayEq ['c','b','a'], result
47-
arrayEq ['c','b','a'], str
47+
arrayEq ['c','b','a'],
48+
str
4849
.split('')
4950
.reverse()
5051
.reverse()
5152
.reverse()
52-
arrayEq ['c','b','a'], str.
53+
arrayEq ['c','b','a'],
54+
str.
5355
split('')
5456
.reverse().
5557
reverse()
5658
.reverse()
5759

60+
test "#1495, method call chaining", ->
61+
str = 'abc'
62+
63+
result = str.split ''
64+
.join ', '
65+
eq 'a, b, c', result
66+
67+
result = str
68+
.split ''
69+
.join ', '
70+
eq 'a, b, c', result
71+
72+
eq 'a, b, c', (str
73+
.split ''
74+
.join ', '
75+
)
76+
77+
eq 'abc',
78+
'aaabbbccc'.replace /(\w)\1\1/g, '$1$1'
79+
.replace /([abc])\1/g, '$1'
80+
81+
# Nested calls
82+
result = [1..3]
83+
.slice Math.max 0, 1
84+
.concat [3]
85+
arrayEq result, [2, 3, 3]
86+
87+
# Single line function arguments.
88+
result = [1..6]
89+
.map (x) -> x * x
90+
.filter (x) -> x % 2 is 0
91+
.reverse()
92+
arrayEq result, [36, 16, 4]
93+
94+
# The parens are forced
95+
result = str.split(''.
96+
split ''
97+
.join ''
98+
).join ', '
99+
eq 'a, b, c', result
100+
58101
# Operators
59102

60103
test "newline suppression for operators", ->
@@ -65,9 +108,11 @@ test "newline suppression for operators", ->
65108
eq 6, six
66109

67110
test "`?.` and `::` should continue lines", ->
68-
ok not Date
69-
::
70-
?.foo
111+
ok not (
112+
Date
113+
::
114+
?.foo
115+
)
71116
#eq Object::toString, Date?.
72117
#prototype
73118
#::

0 commit comments

Comments
 (0)