|
1 | 1 | const { |
2 | 2 | doc: { |
3 | | - builders: { group, ifBreak, indent, softline } |
| 3 | + builders: { group, ifBreak, indent, label, softline } |
4 | 4 | } |
5 | 5 | } = require('prettier'); |
6 | 6 |
|
@@ -47,43 +47,106 @@ const isEndOfChain = (node, path) => { |
47 | 47 | return true; |
48 | 48 | }; |
49 | 49 |
|
50 | | -const shallIndent = (path) => { |
51 | | - let i = 0; |
52 | | - let elderNode = path.getParentNode(i); |
53 | | - while (elderNode) { |
54 | | - if ( |
55 | | - elderNode.type === 'VariableDeclarationStatement' || |
56 | | - elderNode.type === 'BinaryOperation' |
57 | | - ) |
58 | | - return false; |
59 | | - i += 1; |
60 | | - elderNode = path.getParentNode(i); |
61 | | - } |
62 | | - return true; |
| 50 | +/** |
| 51 | + * processChain expects the doc[] of the full chain of MemberAccess. |
| 52 | + * |
| 53 | + * It uses the separator label to split the chain into 2 arrays. |
| 54 | + * The first array is the doc[] corresponding to the first element before the |
| 55 | + * first separator. |
| 56 | + * The second array contains the rest of the chain. |
| 57 | + * |
| 58 | + * The indentation of the whole chain depends on the result of the first |
| 59 | + * element. |
| 60 | + * |
| 61 | + * If the first element breaks into multiple lines, we won't indent the rest of |
| 62 | + * the chain as the last line (most likely a closing parentheses) won't be |
| 63 | + * indented. |
| 64 | + * |
| 65 | + * i.e. |
| 66 | + * ``` |
| 67 | + * functionCall( |
| 68 | + * arg1, |
| 69 | + * arg2 |
| 70 | + * ).rest.of.chain |
| 71 | + * |
| 72 | + * functionCall( |
| 73 | + * arg1, |
| 74 | + * arg2 |
| 75 | + * ) |
| 76 | + * .long |
| 77 | + * .rest |
| 78 | + * .of |
| 79 | + * .chain |
| 80 | + * ``` |
| 81 | + * |
| 82 | + * If the first element doesn't break into multiple lines we treat the rest of |
| 83 | + * the chain as a normal chain and proceed to indent it. |
| 84 | + * |
| 85 | + * |
| 86 | + * i.e. |
| 87 | + * ``` |
| 88 | + * a = functionCall(arg1, arg2).rest.of.chain |
| 89 | + * |
| 90 | + * b = functionCall(arg1, arg2) |
| 91 | + * .long |
| 92 | + * .rest |
| 93 | + * .of |
| 94 | + * .chain |
| 95 | + * ``` |
| 96 | + * |
| 97 | + * NOTE: As described in the examples above, the rest of the chain will be grouped |
| 98 | + * and try to stay in the same line as the end of the first element. |
| 99 | + * |
| 100 | + * @param {doc[]} chain is the full chain of MemberAccess |
| 101 | + * @returns a processed doc[] with the proper grouping and indentation ready to |
| 102 | + * be printed. |
| 103 | + */ |
| 104 | +const processChain = (chain) => { |
| 105 | + const firstSeparatorIndex = chain.findIndex((element) => { |
| 106 | + if (element.label) { |
| 107 | + return JSON.parse(element.label).type === 'separator'; |
| 108 | + } |
| 109 | + return false; |
| 110 | + }); |
| 111 | + // We fetch the groupId from the firstSeparator |
| 112 | + const { groupId } = JSON.parse(chain[firstSeparatorIndex].label); |
| 113 | + // The doc[] before the first separator |
| 114 | + const firstExpression = chain.slice(0, firstSeparatorIndex); |
| 115 | + // The doc[] containing the rest of the chain |
| 116 | + const restOfChain = group(chain.slice(firstSeparatorIndex)); |
| 117 | + |
| 118 | + return groupId |
| 119 | + ? [ |
| 120 | + ...firstExpression, |
| 121 | + ifBreak(restOfChain, indent(restOfChain), { groupId }) |
| 122 | + ] |
| 123 | + : [...firstExpression, indent(restOfChain)]; |
63 | 124 | }; |
64 | 125 |
|
65 | 126 | const MemberAccess = { |
66 | 127 | print: ({ node, path, print }) => { |
67 | 128 | let expressionDoc = path.call(print, 'expression'); |
68 | | - let separator = [softline, '.']; |
69 | | - let labelData; |
| 129 | + const separatorLabel = { |
| 130 | + type: 'separator' |
| 131 | + }; |
| 132 | + |
70 | 133 | if (expressionDoc.label) { |
71 | | - labelData = JSON.parse(expressionDoc.label); |
| 134 | + const labelData = JSON.parse(expressionDoc.label); |
| 135 | + if (labelData && labelData.groupId) { |
| 136 | + // if there's a groupId in the data, we pass it to the separator as |
| 137 | + // this doc[] is going to be stripped of it's metadata |
| 138 | + separatorLabel.groupId = labelData.groupId; |
| 139 | + } |
72 | 140 | expressionDoc = expressionDoc.contents.flat(); |
73 | 141 | } |
74 | | - if (labelData && labelData.groupId) { |
75 | | - separator = ifBreak('.', [softline, '.'], { |
76 | | - groupId: labelData.groupId |
77 | | - }); |
78 | | - } |
79 | 142 |
|
80 | 143 | const doc = [ |
81 | 144 | expressionDoc, |
82 | | - shallIndent(path) ? indent(separator) : separator, |
| 145 | + label(JSON.stringify(separatorLabel), [softline, '.']), |
83 | 146 | node.memberName |
84 | 147 | ].flat(); |
85 | 148 |
|
86 | | - return isEndOfChain(node, path) ? group(doc) : doc; |
| 149 | + return isEndOfChain(node, path) ? group(processChain(doc)) : doc; |
87 | 150 | } |
88 | 151 | }; |
89 | 152 |
|
|
0 commit comments