diff --git a/CHANGELOG.md b/CHANGELOG.md index e5616a92..38b74670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a - [@rindek], [@kddeisz] - Do not remove parentheses when using the special `call` syntax with no arguments. - [@ykpythemind] - Do not change regexp bounds if the body has certain content. - [@karanmandal], [@kddeisz] - Correctly print for loops. +- [@rafbm], [@kddeisz] - If there are method chains with arguments only at the end, we should group the method chain and the method args. ## [1.0.1] - 2020-12-12 @@ -1071,6 +1072,7 @@ would previously result in `array[]`, but now prints properly. [@overload119]: https://github.com/Overload119 [@petevk]: https://github.com/petevk [@pje]: https://github.com/pje +[@rafbm]: https://github.com/rafbm [@rindek]: https://github.com/rindek [@rosskinsella]: https://github.com/RossKinsella [@rsullivan00]: https://github.com/Rsullivan00 diff --git a/src/nodes/calls.js b/src/nodes/calls.js index 6e55b43b..0033f8a2 100644 --- a/src/nodes/calls.js +++ b/src/nodes/calls.js @@ -39,6 +39,7 @@ function printCall(path, opts, print) { // right side of the expression, as we want to have a nice multi-line layout. if (chained.includes(parentNode.type)) { parentNode.chain = (node.chain || 0) + 1; + parentNode.callChain = (node.callChain || 0) + 1; parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc); } @@ -109,6 +110,21 @@ function printMethodAddArg(path, opts, print) { // If we're at the top of a chain, then we're going to print out a nice // multi-line layout if this doesn't break into multiple lines. if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) { + // This is pretty specialized behavior. Basically if we're at the top of a + // chain but we've only had method calls without arguments and now we have + // arguments, then we're effectively trying to call a method with arguments + // that is nested under a bunch of stuff. So we group together to first part + // to make it so just the arguments break. This looks like, for example: + // + // config.action_dispatch.rescue_responses.merge!( + // 'ActiveRecord::ConnectionTimeoutError' => :service_unavailable, + // 'ActiveRecord::QueryCanceled' => :service_unavailable + // ) + // + if (node.callChain === node.chain) { + return concat([group(indent(concat(node.breakDoc))), group(argsDoc)]); + } + return ifBreak( group(indent(concat(node.breakDoc.concat(argsDoc)))), concat([methodDoc, argsDoc]) diff --git a/test/js/nodes/calls.test.js b/test/js/nodes/calls.test.js index bb821ed8..113d66fe 100644 --- a/test/js/nodes/calls.test.js +++ b/test/js/nodes/calls.test.js @@ -1,4 +1,4 @@ -const { ruby } = require("../utils"); +const { long, ruby } = require("../utils"); describe("calls", () => { test("simple calls", () => { @@ -26,6 +26,17 @@ describe("calls", () => { return expect(before).toChangeFormat(after); }); + test("chains of methods with one with arguments right at the top", () => { + const content = ruby(` + aaa.bbb.ccc.ddd.eee.merge( + ${long.slice(0, 30)}: 'aaa', + ${long.slice(0, 31)}: 'bbb' + ) + `); + + return expect(content).toMatchFormat(); + }); + test("tons of calls that fit on one line", () => { const content = "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z";