Handle nested calls and function oneliners when chaining

This commit is contained in:
xixixao
2013-11-27 04:52:52 +00:00
parent 15a70f863c
commit ee9febe399
3 changed files with 56 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.6.3
(function() {
var BALANCED_PAIRS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, left, rite, _i, _len, _ref,
__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; },
__slice = [].slice;
@@ -149,7 +149,7 @@
var stack;
stack = [];
return this.scanTokens(function(token, i, tokens) {
var 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 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;
tag = token[0];
prevTag = (prevToken = i > 0 ? tokens[i - 1] : [])[0];
nextTag = (i < tokens.length - 1 ? tokens[i + 1] : [])[0];
@@ -194,6 +194,14 @@
tokens.splice(i, 0, generate('CALL_END', ')'));
return i += 1;
};
endAllImplicitCalls = function() {
var _results;
_results = [];
while (inImplicitCall()) {
_results.push(endImplicitCall());
}
return _results;
};
startImplicitObject = function(j, startsLine) {
var idx;
if (startsLine == null) {
@@ -285,9 +293,15 @@
startImplicitObject(s, !!startsLine);
return forward(2);
}
if ((prevTag === 'OUTDENT' || prevToken.newLine) && inImplicitCall() && (tag === '.' || tag === '?.' || tag === '::' || tag === '?::')) {
endImplicitCall();
return forward(1);
if (inImplicitCall() && __indexOf.call(CALL_CLOSERS, tag) >= 0) {
if (prevTag === 'OUTDENT') {
endImplicitCall();
return forward(1);
}
if (prevToken.newLine) {
endAllImplicitCalls();
return forward(1);
}
}
if (inImplicitObject() && __indexOf.call(LINEBREAKS, tag) >= 0) {
stackTop()[2].sameLine = false;
@@ -346,8 +360,8 @@
var action, condition, indent, outdent, starter;
starter = indent = outdent = null;
condition = function(token, i) {
var _ref, _ref1, _ref2;
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'TERMINATOR' && (_ref1 = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref2 = token[0]) === 'CATCH' || _ref2 === 'FINALLY') && (starter === '->' || starter === '=>'));
var _ref, _ref1, _ref2, _ref3;
return token[1] !== ';' && (_ref = token[0], __indexOf.call(SINGLE_CLOSERS, _ref) >= 0) && !(token[0] === 'TERMINATOR' && (_ref1 = this.tag(i + 1), __indexOf.call(EXPRESSION_CLOSE, _ref1) >= 0)) && !(token[0] === 'ELSE' && starter !== 'THEN') && !(((_ref2 = token[0]) === 'CATCH' || _ref2 === 'FINALLY') && (starter === '->' || starter === '=>')) || (_ref3 = token[0], __indexOf.call(CALL_CLOSERS, _ref3) >= 0) && this.tokens[i - 1].newLine;
};
action = function(token, i) {
return this.tokens.splice((this.tag(i - 1) === ',' ? i - 1 : i), 0, outdent);
@@ -472,4 +486,6 @@
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT'];
CALL_CLOSERS = ['.', '?.', '::', '?::'];
}).call(this);

View File

@@ -159,6 +159,10 @@ class exports.Rewriter
tokens.splice i, 0, generate 'CALL_END', ')'
i += 1
endAllImplicitCalls = ->
while inImplicitCall()
endImplicitCall()
startImplicitObject = (j, startsLine = yes) ->
idx = j ? i
stack.push ['{', idx, sameLine: yes, startsLine: startsLine, ours: yes]
@@ -281,10 +285,13 @@ class exports.Rewriter
# .g b
# .h a
#
if (prevTag is 'OUTDENT' or prevToken.newLine) and inImplicitCall() and
tag in ['.', '?.', '::', '?::']
endImplicitCall()
return forward(1)
if inImplicitCall() and tag in CALL_CLOSERS
if prevTag is 'OUTDENT'
endImplicitCall()
return forward(1)
if prevToken.newLine
endAllImplicitCalls()
return forward(1)
stackTop()[2].sameLine = no if inImplicitObject() and tag in LINEBREAKS
@@ -363,7 +370,8 @@ class exports.Rewriter
token[1] isnt ';' and token[0] in SINGLE_CLOSERS and
not (token[0] is 'TERMINATOR' and @tag(i + 1) in EXPRESSION_CLOSE) and
not (token[0] is 'ELSE' and starter isnt 'THEN') and
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>'])
not (token[0] in ['CATCH', 'FINALLY'] and starter in ['->', '=>']) or
token[0] in CALL_CLOSERS and @tokens[i - 1].newLine
action = (token, i) ->
@tokens.splice (if @tag(i - 1) is ',' then i - 1 else i), 0, outdent
@@ -478,3 +486,6 @@ SINGLE_CLOSERS = ['TERMINATOR', 'CATCH', 'FINALLY', 'ELSE', 'OUTDENT', 'LEADIN
# Tokens that end a line.
LINEBREAKS = ['TERMINATOR', 'INDENT', 'OUTDENT']
# Tokens that close open calls when they follow a newline.
CALL_CLOSERS = ['.', '?.', '::', '?::']

View File

@@ -78,11 +78,24 @@ test "#1495, method call chaining", ->
'aaabbbccc'.replace /(\w)\1\1/g, '$1$1'
.replace /([abc])\1/g, '$1'
# Unreadable code, not a real-life use case
result = str.split ''.
# 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 ', '
.join ''
).join ', '
eq 'a, b, c', result
# Operators