@openrewrite/rewrite 8.69.0-20251209-122812 → 8.69.0-20251209-133839
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +2 -18
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/tabs-and-indents-visitor.d.ts +4 -3
- package/dist/javascript/tabs-and-indents-visitor.d.ts.map +1 -1
- package/dist/javascript/tabs-and-indents-visitor.js +176 -184
- package/dist/javascript/tabs-and-indents-visitor.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/parser.ts +2 -18
- package/src/javascript/tabs-and-indents-visitor.ts +176 -179
|
@@ -5,14 +5,16 @@ import { TabsAndIndentsStyle } from "./style";
|
|
|
5
5
|
export declare class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
|
|
6
6
|
private readonly tabsAndIndentsStyle;
|
|
7
7
|
private stopAfter?;
|
|
8
|
-
private readonly
|
|
8
|
+
private readonly indentSize;
|
|
9
|
+
private readonly useTabCharacter;
|
|
9
10
|
constructor(tabsAndIndentsStyle: TabsAndIndentsStyle, stopAfter?: Tree | undefined);
|
|
11
|
+
private indentString;
|
|
10
12
|
protected preVisit(tree: J, _p: P): Promise<J | undefined>;
|
|
11
13
|
private setupCursorMessagesForTree;
|
|
12
14
|
private getParentIndentContext;
|
|
13
15
|
private computeMyIndent;
|
|
14
16
|
private prefixContainsNewline;
|
|
15
|
-
private
|
|
17
|
+
private isJsxChildElement;
|
|
16
18
|
private computeIndentKind;
|
|
17
19
|
postVisit(tree: J, _p: P): Promise<J | undefined>;
|
|
18
20
|
private isInsideJsxTag;
|
|
@@ -27,7 +29,6 @@ export declare class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
27
29
|
private postVisitLeftPadded;
|
|
28
30
|
visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: P): Promise<J.RightPadded<T> | undefined>;
|
|
29
31
|
private preVisitRightPadded;
|
|
30
|
-
private elementPrefixContainsNewline;
|
|
31
32
|
/**
|
|
32
33
|
* Check if an element is a spread - either directly marked with Spread marker,
|
|
33
34
|
* or a PropertyAssignment whose name has a Spread marker (for object spreads).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs-and-indents-visitor.d.ts","sourceRoot":"","sources":["../../src/javascript/tabs-and-indents-visitor.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,iBAAiB,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAGH,CAAC,EAOJ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAC,MAAM,EAAmB,IAAI,EAAC,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAC,mBAAmB,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"tabs-and-indents-visitor.d.ts","sourceRoot":"","sources":["../../src/javascript/tabs-and-indents-visitor.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAC,iBAAiB,EAAC,MAAM,WAAW,CAAC;AAC5C,OAAO,EAGH,CAAC,EAOJ,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAC,MAAM,EAAmB,IAAI,EAAC,MAAM,SAAS,CAAC;AAGtD,OAAO,EAAC,mBAAmB,EAAC,MAAM,SAAS,CAAC;AAK5C,qBAAa,qBAAqB,CAAC,CAAC,CAAE,SAAQ,iBAAiB,CAAC,CAAC,CAAC;IAIlD,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAAuB,OAAO,CAAC,SAAS,CAAC;IAHzF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;gBAEb,mBAAmB,EAAE,mBAAmB,EAAU,SAAS,CAAC,EAAE,IAAI,YAAA;IAM/F,OAAO,CAAC,YAAY;cAOJ,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAKhE,OAAO,CAAC,0BAA0B;IA8DlC,OAAO,CAAC,sBAAsB;IAmB9B,OAAO,CAAC,eAAe;IA0CvB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;IAeV,SAAS,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAkEhE,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,uBAAuB;IAyD/B,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,iBAAiB;IAUZ,cAAc,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAuBlG,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,kBAAkB;IAmBb,eAAe,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAC1E,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EACrB,CAAC,EAAE,CAAC,GACL,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IA2BvC,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,mBAAmB;IAoBd,gBAAgB,CAAC,CAAC,SAAS,CAAC,GAAG,OAAO,EAC/C,KAAK,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EACvB,CAAC,EAAE,CAAC,GACL,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAyBxC,OAAO,CAAC,mBAAmB;IAyC3B;;;OAGG;IACH,OAAO,CAAC,eAAe;IAgBjB,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAanF,OAAO,CAAC,oBAAoB;IA6C5B,OAAO,CAAC,aAAa;CAMxB"}
|
|
@@ -38,12 +38,14 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
38
38
|
super();
|
|
39
39
|
this.tabsAndIndentsStyle = tabsAndIndentsStyle;
|
|
40
40
|
this.stopAfter = stopAfter;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
this.indentSize = this.tabsAndIndentsStyle.indentSize;
|
|
42
|
+
this.useTabCharacter = this.tabsAndIndentsStyle.useTabCharacter;
|
|
43
|
+
}
|
|
44
|
+
indentString(indent) {
|
|
45
|
+
if (this.useTabCharacter) {
|
|
46
|
+
return "\t".repeat(Math.floor(indent / this.indentSize));
|
|
46
47
|
}
|
|
48
|
+
return " ".repeat(indent);
|
|
47
49
|
}
|
|
48
50
|
preVisit(tree, _p) {
|
|
49
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -52,52 +54,85 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
setupCursorMessagesForTree(cursor, tree) {
|
|
55
|
-
var _a, _b, _c
|
|
57
|
+
var _a, _b, _c;
|
|
58
|
+
// Check if this MethodInvocation starts a method chain (select.after has newline)
|
|
59
|
+
if (tree.kind === java_1.J.Kind.MethodInvocation) {
|
|
60
|
+
const mi = tree;
|
|
61
|
+
if (mi.select && mi.select.after.whitespace.includes("\n")) {
|
|
62
|
+
// This MethodInvocation has a chained method call after it
|
|
63
|
+
// Store the ORIGINAL parent indent context in "chainedIndentContext"
|
|
64
|
+
// This will be propagated down and used when we reach the chain's innermost element
|
|
65
|
+
const parentContext = this.getParentIndentContext(cursor);
|
|
66
|
+
cursor.messages.set("chainedIndentContext", parentContext);
|
|
67
|
+
// For children (arguments), use continuation indent
|
|
68
|
+
// But the prefix will be normalized in postVisit using chainedIndentContext
|
|
69
|
+
const [parentIndent, parentIndentKind] = parentContext;
|
|
70
|
+
cursor.messages.set("indentContext", [parentIndent + this.indentSize, parentIndentKind]);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Check if we're at the base of a chain (no select) and parent has chainedIndentContext
|
|
74
|
+
if (!mi.select) {
|
|
75
|
+
const chainedContext = (_a = cursor.parent) === null || _a === void 0 ? void 0 : _a.messages.get("chainedIndentContext");
|
|
76
|
+
if (chainedContext !== undefined) {
|
|
77
|
+
// Consume the chainedIndentContext - this is the base of the chain
|
|
78
|
+
// The base element gets the original indent (no extra continuation)
|
|
79
|
+
// Propagate chainedIndentContext so the `name` child knows not to add indent
|
|
80
|
+
cursor.messages.set("indentContext", chainedContext);
|
|
81
|
+
cursor.messages.set("chainedIndentContext", chainedContext);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Check if we're the `name` of a MethodInvocation at the base of a chain
|
|
87
|
+
if (tree.kind === java_1.J.Kind.Identifier) {
|
|
88
|
+
const parentValue = (_b = cursor.parent) === null || _b === void 0 ? void 0 : _b.value;
|
|
89
|
+
if ((parentValue === null || parentValue === void 0 ? void 0 : parentValue.kind) === java_1.J.Kind.MethodInvocation) {
|
|
90
|
+
const parentMi = parentValue;
|
|
91
|
+
// Check if parent has chainedIndentContext (meaning it's at the base of a chain)
|
|
92
|
+
// and we are the `name` (not an argument or something else)
|
|
93
|
+
const parentChainedContext = (_c = cursor.parent) === null || _c === void 0 ? void 0 : _c.messages.get("chainedIndentContext");
|
|
94
|
+
if (parentChainedContext !== undefined && !parentMi.select) {
|
|
95
|
+
// We're the name of a chain-base MethodInvocation - use the chained indent directly
|
|
96
|
+
cursor.messages.set("indentContext", parentChainedContext);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
56
101
|
const [parentMyIndent, parentIndentKind] = this.getParentIndentContext(cursor);
|
|
57
102
|
const myIndent = this.computeMyIndent(tree, parentMyIndent, parentIndentKind);
|
|
58
|
-
cursor.messages.set("myIndent", myIndent);
|
|
59
103
|
// For Binary, behavior depends on whether it's already on a continuation line
|
|
104
|
+
let indentKind;
|
|
60
105
|
if (tree.kind === java_1.J.Kind.Binary) {
|
|
61
|
-
const hasNewline = ((_b = (_a = tree.prefix) === null || _a === void 0 ? void 0 : _a.whitespace) === null || _b === void 0 ? void 0 : _b.includes("\n")) ||
|
|
62
|
-
((_d = (_c = tree.prefix) === null || _c === void 0 ? void 0 : _c.comments) === null || _d === void 0 ? void 0 : _d.some(c => c.suffix.includes("\n")));
|
|
63
106
|
// If Binary has newline OR parent is in align mode, children align
|
|
64
|
-
const
|
|
65
|
-
|
|
107
|
+
const hasNewline = this.prefixContainsNewline(tree);
|
|
108
|
+
indentKind = (hasNewline || parentIndentKind === 'align') ? 'align' : 'continuation';
|
|
66
109
|
}
|
|
67
110
|
else {
|
|
68
|
-
|
|
111
|
+
indentKind = this.computeIndentKind(tree);
|
|
69
112
|
}
|
|
113
|
+
cursor.messages.set("indentContext", [myIndent, indentKind]);
|
|
70
114
|
}
|
|
71
115
|
getParentIndentContext(cursor) {
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
let parentIndent;
|
|
75
|
-
let parentKind;
|
|
116
|
+
// Walk up the cursor chain to find the nearest indent context
|
|
117
|
+
// We need to walk because intermediate nodes like RightPadded may not have context set
|
|
76
118
|
for (let c = cursor.parent; c != null; c = c.parent) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (parentKind === undefined) {
|
|
84
|
-
const kind = c.messages.get("indentKind");
|
|
85
|
-
if (kind !== undefined) {
|
|
86
|
-
parentKind = kind;
|
|
87
|
-
}
|
|
119
|
+
// chainedIndentContext stores the original context - prefer it
|
|
120
|
+
const chainedContext = c.messages.get("chainedIndentContext");
|
|
121
|
+
if (chainedContext !== undefined) {
|
|
122
|
+
return chainedContext;
|
|
88
123
|
}
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
124
|
+
const context = c.messages.get("indentContext");
|
|
125
|
+
if (context !== undefined) {
|
|
126
|
+
return context;
|
|
92
127
|
}
|
|
93
128
|
}
|
|
94
|
-
return [
|
|
129
|
+
return [0, 'continuation'];
|
|
95
130
|
}
|
|
96
131
|
computeMyIndent(tree, parentMyIndent, parentIndentKind) {
|
|
97
132
|
var _a, _b, _c;
|
|
98
|
-
// CompilationUnit is the root - it always has myIndent=
|
|
133
|
+
// CompilationUnit is the root - it always has myIndent=0 regardless of prefix content
|
|
99
134
|
if (tree.kind === tree_1.JS.Kind.CompilationUnit) {
|
|
100
|
-
return
|
|
135
|
+
return 0;
|
|
101
136
|
}
|
|
102
137
|
// TemplateExpressionSpan: reset indent context - template literal content determines its own indentation
|
|
103
138
|
// The expression inside ${...} should be indented based on where it appears in the template, not outer code
|
|
@@ -107,44 +142,52 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
107
142
|
const prefix = (_c = (_b = (_a = span.expression) === null || _a === void 0 ? void 0 : _a.prefix) === null || _b === void 0 ? void 0 : _b.whitespace) !== null && _c !== void 0 ? _c : "";
|
|
108
143
|
const lastNewline = prefix.lastIndexOf("\n");
|
|
109
144
|
if (lastNewline >= 0) {
|
|
110
|
-
return prefix.
|
|
145
|
+
return prefix.length - lastNewline - 1;
|
|
111
146
|
}
|
|
112
|
-
return
|
|
147
|
+
return 0;
|
|
113
148
|
}
|
|
114
149
|
if (tree.kind === java_1.J.Kind.IfElse || parentIndentKind === 'align') {
|
|
115
150
|
return parentMyIndent;
|
|
116
151
|
}
|
|
152
|
+
// Certain structures don't add indent for themselves - they stay at parent level
|
|
153
|
+
// - TryCatch: catch clause is part of try statement
|
|
154
|
+
// - TypeLiteral: { members } in type definitions
|
|
155
|
+
// - EnumValueSet: enum members get same indent as the set itself
|
|
156
|
+
if (tree.kind === java_1.J.Kind.TryCatch || tree.kind === tree_1.JS.Kind.TypeLiteral || tree.kind === java_1.J.Kind.EnumValueSet) {
|
|
157
|
+
return parentMyIndent;
|
|
158
|
+
}
|
|
117
159
|
// Only add indent if this element starts on a new line
|
|
118
160
|
// Check both the element's prefix and any Spread marker's prefix
|
|
119
161
|
const hasNewline = this.prefixContainsNewline(tree);
|
|
120
162
|
if (!hasNewline) {
|
|
121
163
|
// Special case for JSX: children of JsxTag don't have newlines in their prefix
|
|
122
|
-
// (newlines are in text Literal nodes), but
|
|
123
|
-
if (this.
|
|
124
|
-
return parentMyIndent + this.
|
|
164
|
+
// (newlines are in text Literal nodes), but JSX children should still get block indent
|
|
165
|
+
if (this.isJsxChildElement(tree)) {
|
|
166
|
+
return parentMyIndent + this.indentSize;
|
|
125
167
|
}
|
|
126
168
|
return parentMyIndent;
|
|
127
169
|
}
|
|
128
170
|
// Add indent for block children or continuation
|
|
129
|
-
return parentMyIndent + this.
|
|
171
|
+
return parentMyIndent + this.indentSize;
|
|
130
172
|
}
|
|
131
173
|
prefixContainsNewline(tree) {
|
|
132
|
-
var _a, _b
|
|
133
|
-
// Check the element
|
|
134
|
-
if (
|
|
135
|
-
((_d = (_c = tree.prefix) === null || _c === void 0 ? void 0 : _c.comments) === null || _d === void 0 ? void 0 : _d.some(c => c.suffix.includes("\n")))) {
|
|
174
|
+
var _a, _b;
|
|
175
|
+
// Check if the element starts on a new line (only the last whitespace matters)
|
|
176
|
+
if (tree.prefix && (0, java_1.lastWhitespace)(tree.prefix).includes("\n")) {
|
|
136
177
|
return true;
|
|
137
178
|
}
|
|
138
179
|
// For elements with Spread marker, check the Spread marker's prefix
|
|
139
|
-
const spreadMarker = (
|
|
180
|
+
const spreadMarker = (_b = (_a = tree.markers) === null || _a === void 0 ? void 0 : _a.markers) === null || _b === void 0 ? void 0 : _b.find(m => m.kind === tree_1.JS.Markers.Spread);
|
|
140
181
|
if (spreadMarker && (0, java_1.spaceContainsNewline)(spreadMarker.prefix)) {
|
|
141
182
|
return true;
|
|
142
183
|
}
|
|
143
184
|
return false;
|
|
144
185
|
}
|
|
145
|
-
|
|
146
|
-
// Check if this is a
|
|
147
|
-
|
|
186
|
+
isJsxChildElement(tree) {
|
|
187
|
+
// Check if this is a JSX child element whose parent is a JsxTag
|
|
188
|
+
// JSX children (JsxTag, JsxEmbeddedExpression) don't have newlines in their own prefix
|
|
189
|
+
// (newlines are in text Literal nodes), but they should still get block indent
|
|
190
|
+
if (tree.kind !== tree_1.JS.Kind.JsxTag && tree.kind !== tree_1.JS.Kind.JsxEmbeddedExpression) {
|
|
148
191
|
return false;
|
|
149
192
|
}
|
|
150
193
|
const parentTree = this.cursor.parentTree();
|
|
@@ -155,6 +198,8 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
155
198
|
case java_1.J.Kind.Block:
|
|
156
199
|
case java_1.J.Kind.Case:
|
|
157
200
|
case tree_1.JS.Kind.JsxTag:
|
|
201
|
+
case tree_1.JS.Kind.TypeLiteral:
|
|
202
|
+
case java_1.J.Kind.EnumValueSet:
|
|
158
203
|
return 'block';
|
|
159
204
|
case tree_1.JS.Kind.CompilationUnit:
|
|
160
205
|
return 'align';
|
|
@@ -164,19 +209,40 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
164
209
|
}
|
|
165
210
|
postVisit(tree, _p) {
|
|
166
211
|
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
-
var _a, _b, _c;
|
|
212
|
+
var _a, _b, _c, _d, _e;
|
|
168
213
|
if (this.stopAfter != null && (0, tree_2.isScope)(this.stopAfter, tree)) {
|
|
169
214
|
(_a = this.cursor) === null || _a === void 0 ? void 0 : _a.root.messages.set("stop", true);
|
|
170
215
|
}
|
|
171
|
-
|
|
172
|
-
|
|
216
|
+
// If parent has chainedIndentContext but no indentContext yet, we're exiting a chain element
|
|
217
|
+
// Set indentContext on parent = [chainedIndent + indentSize, chainedIndentKind]
|
|
218
|
+
// This only happens once at the innermost element; subsequent parents will already have indentContext
|
|
219
|
+
const parentChainedContext = (_b = this.cursor.parent) === null || _b === void 0 ? void 0 : _b.messages.get("chainedIndentContext");
|
|
220
|
+
const parentHasIndentContext = (_c = this.cursor.parent) === null || _c === void 0 ? void 0 : _c.messages.has("indentContext");
|
|
221
|
+
if (parentChainedContext !== undefined && !parentHasIndentContext) {
|
|
222
|
+
const [chainedIndent, chainedIndentKind] = parentChainedContext;
|
|
223
|
+
this.cursor.parent.messages.set("indentContext", [chainedIndent + this.indentSize, chainedIndentKind]);
|
|
224
|
+
}
|
|
225
|
+
const indentContext = this.cursor.messages.get("indentContext");
|
|
226
|
+
if (indentContext === undefined) {
|
|
173
227
|
return tree;
|
|
174
228
|
}
|
|
229
|
+
let [myIndent] = indentContext;
|
|
230
|
+
// For chain-start MethodInvocations, the prefix contains whitespace before the chain BASE
|
|
231
|
+
// Use chainedIndentContext (the original indent) for the prefix, not the continuation indent
|
|
232
|
+
const chainedContext = this.cursor.messages.get("chainedIndentContext");
|
|
233
|
+
if (chainedContext !== undefined && tree.kind === java_1.J.Kind.MethodInvocation) {
|
|
234
|
+
const mi = tree;
|
|
235
|
+
if (mi.select && mi.select.after.whitespace.includes("\n")) {
|
|
236
|
+
// This is a chain-start - use original indent for prefix normalization
|
|
237
|
+
myIndent = chainedContext[0];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
175
240
|
let result = tree;
|
|
241
|
+
const indentStr = this.indentString(myIndent);
|
|
176
242
|
// Check if the element has a Spread marker - if so, normalize its prefix instead
|
|
177
|
-
const spreadMarker = (
|
|
243
|
+
const spreadMarker = (_e = (_d = result.markers) === null || _d === void 0 ? void 0 : _d.markers) === null || _e === void 0 ? void 0 : _e.find(m => m.kind === tree_1.JS.Markers.Spread);
|
|
178
244
|
if (spreadMarker && (0, java_1.spaceContainsNewline)(spreadMarker.prefix)) {
|
|
179
|
-
const normalizedPrefix = (0, java_1.normalizeSpaceIndent)(spreadMarker.prefix,
|
|
245
|
+
const normalizedPrefix = (0, java_1.normalizeSpaceIndent)(spreadMarker.prefix, indentStr);
|
|
180
246
|
if (normalizedPrefix !== spreadMarker.prefix) {
|
|
181
247
|
result = (0, immer_1.produce)(result, draft => {
|
|
182
248
|
const spreadIdx = draft.markers.markers.findIndex(m => m.kind === tree_1.JS.Markers.Spread);
|
|
@@ -188,7 +254,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
188
254
|
}
|
|
189
255
|
else if (result.prefix && (0, java_1.spaceContainsNewline)(result.prefix)) {
|
|
190
256
|
// Normalize the entire prefix space including comment suffixes
|
|
191
|
-
const normalizedPrefix = (0, java_1.normalizeSpaceIndent)(result.prefix,
|
|
257
|
+
const normalizedPrefix = (0, java_1.normalizeSpaceIndent)(result.prefix, indentStr);
|
|
192
258
|
if (normalizedPrefix !== result.prefix) {
|
|
193
259
|
result = (0, immer_1.produce)(result, draft => {
|
|
194
260
|
draft.prefix = normalizedPrefix;
|
|
@@ -214,13 +280,14 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
214
280
|
}
|
|
215
281
|
// Check if this literal is the last child of a JsxTag - if so, its trailing whitespace
|
|
216
282
|
// should use the parent tag's indent, not the content indent
|
|
217
|
-
const
|
|
283
|
+
const parentContext = this.cursor.parentTree().messages.get("indentContext");
|
|
284
|
+
const parentIndent = parentContext === null || parentContext === void 0 ? void 0 : parentContext[0];
|
|
218
285
|
const isLastChild = parentIndent !== undefined && this.isLastChildOfJsxTag(literal);
|
|
219
286
|
// For JSX text content, the newline is in the value, not the prefix.
|
|
220
287
|
// Since the content IS effectively on a new line, it should get block child indent.
|
|
221
288
|
// myIndent is the parent's indent (because Literal prefix has no newline),
|
|
222
|
-
// so we need to add
|
|
223
|
-
const contentIndent = myIndent + this.
|
|
289
|
+
// so we need to add indentSize for content lines.
|
|
290
|
+
const contentIndent = myIndent + this.indentSize;
|
|
224
291
|
// Split by newlines and normalize each line's indentation
|
|
225
292
|
const lines = literal.valueSource.split('\n');
|
|
226
293
|
const result = [];
|
|
@@ -235,7 +302,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
235
302
|
// Line has only whitespace (or is empty)
|
|
236
303
|
if (isLastChild && i === lines.length - 1) {
|
|
237
304
|
// Trailing whitespace of last child - use parent indent for closing tag alignment
|
|
238
|
-
result.push(parentIndent);
|
|
305
|
+
result.push(this.indentString(parentIndent));
|
|
239
306
|
}
|
|
240
307
|
else if (i < lines.length - 1) {
|
|
241
308
|
// Empty line in the middle (followed by more lines) - keep empty
|
|
@@ -243,12 +310,12 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
243
310
|
}
|
|
244
311
|
else {
|
|
245
312
|
// Trailing whitespace of non-last-child - add content indent
|
|
246
|
-
result.push(contentIndent);
|
|
313
|
+
result.push(this.indentString(contentIndent));
|
|
247
314
|
}
|
|
248
315
|
}
|
|
249
316
|
else {
|
|
250
317
|
// Line has content - add proper indent
|
|
251
|
-
result.push(contentIndent + content);
|
|
318
|
+
result.push(this.indentString(contentIndent) + content);
|
|
252
319
|
}
|
|
253
320
|
}
|
|
254
321
|
const normalizedValueSource = result.join('\n');
|
|
@@ -277,7 +344,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
277
344
|
return block;
|
|
278
345
|
}
|
|
279
346
|
return (0, immer_1.produce)(block, draft => {
|
|
280
|
-
draft.end = (0, java_1.replaceLastWhitespace)(draft.end, ws => (0, java_1.replaceIndentAfterLastNewline)(ws, myIndent));
|
|
347
|
+
draft.end = (0, java_1.replaceLastWhitespace)(draft.end, ws => (0, java_1.replaceIndentAfterLastNewline)(ws, this.indentString(myIndent)));
|
|
281
348
|
});
|
|
282
349
|
}
|
|
283
350
|
visitContainer(container, p) {
|
|
@@ -285,7 +352,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
285
352
|
// Create cursor for this container
|
|
286
353
|
this.cursor = new tree_2.Cursor(container, this.cursor);
|
|
287
354
|
// Pre-visit hook: set up cursor messages
|
|
288
|
-
this.preVisitContainer(
|
|
355
|
+
this.preVisitContainer();
|
|
289
356
|
// Visit children (similar to base visitor but without cursor management)
|
|
290
357
|
let ret = (yield (0, visitor_2.produceAsync)(container, (draft) => __awaiter(this, void 0, void 0, function* () {
|
|
291
358
|
draft.before = yield this.visitSpace(container.before, p);
|
|
@@ -299,56 +366,16 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
299
366
|
return ret;
|
|
300
367
|
});
|
|
301
368
|
}
|
|
302
|
-
preVisitContainer(
|
|
303
|
-
var _a
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
// BUT stop at scope boundaries:
|
|
308
|
-
// 1. Blocks - nested code inside callbacks should NOT use outer chainedIndent
|
|
309
|
-
// 2. Other Containers - nested function call arguments should NOT use outer chainedIndent
|
|
310
|
-
for (let c = this.cursor.parent; c; c = c.parent) {
|
|
311
|
-
// Stop searching if we hit a Block (function body, arrow function body, etc.)
|
|
312
|
-
// This prevents chainedIndent from leaking into nested scopes
|
|
313
|
-
if (((_c = c.value) === null || _c === void 0 ? void 0 : _c.kind) === java_1.J.Kind.Block) {
|
|
314
|
-
break;
|
|
315
|
-
}
|
|
316
|
-
// Stop searching if we hit another Container (arguments of another function call)
|
|
317
|
-
// This prevents chainedIndent from leaking into nested function calls
|
|
318
|
-
if (((_d = c.value) === null || _d === void 0 ? void 0 : _d.kind) === java_1.J.Kind.Container) {
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
const chainedIndent = c.messages.get("chainedIndent");
|
|
322
|
-
if (chainedIndent !== undefined) {
|
|
323
|
-
myIndent = chainedIndent;
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
this.cursor.messages.set("myIndent", myIndent);
|
|
328
|
-
this.cursor.messages.set("indentKind", 'continuation');
|
|
369
|
+
preVisitContainer() {
|
|
370
|
+
var _a;
|
|
371
|
+
const parentContext = (_a = this.cursor.parent) === null || _a === void 0 ? void 0 : _a.messages.get("indentContext");
|
|
372
|
+
const [myIndent] = parentContext !== null && parentContext !== void 0 ? parentContext : [0, 'continuation'];
|
|
373
|
+
this.cursor.messages.set("indentContext", [myIndent, 'continuation']);
|
|
329
374
|
}
|
|
330
375
|
postVisitContainer(container) {
|
|
331
|
-
var _a
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
// BUT stop at scope boundaries:
|
|
335
|
-
// 1. Blocks - nested code inside callbacks should NOT use outer chainedIndent
|
|
336
|
-
// 2. Other Containers - nested function call arguments should NOT use outer chainedIndent
|
|
337
|
-
for (let c = this.cursor.parent; c; c = c.parent) {
|
|
338
|
-
// Stop searching if we hit a Block (function body, arrow function body, etc.)
|
|
339
|
-
if (((_c = c.value) === null || _c === void 0 ? void 0 : _c.kind) === java_1.J.Kind.Block) {
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
// Stop searching if we hit another Container (arguments of another function call)
|
|
343
|
-
if (((_d = c.value) === null || _d === void 0 ? void 0 : _d.kind) === java_1.J.Kind.Container) {
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
const chainedIndent = c.messages.get("chainedIndent");
|
|
347
|
-
if (chainedIndent !== undefined) {
|
|
348
|
-
parentIndent = chainedIndent;
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
376
|
+
var _a;
|
|
377
|
+
const parentContext = (_a = this.cursor.parent) === null || _a === void 0 ? void 0 : _a.messages.get("indentContext");
|
|
378
|
+
const [parentIndent] = parentContext !== null && parentContext !== void 0 ? parentContext : [0, 'continuation'];
|
|
352
379
|
// Normalize the last element's after whitespace (closing delimiter like `)`)
|
|
353
380
|
// The closing delimiter should align with the parent's indent level
|
|
354
381
|
if (container.elements.length > 0) {
|
|
@@ -356,7 +383,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
356
383
|
if (effectiveLastWs.includes("\n")) {
|
|
357
384
|
return (0, immer_1.produce)(container, draft => {
|
|
358
385
|
const lastDraft = draft.elements[draft.elements.length - 1];
|
|
359
|
-
lastDraft.after = (0, java_1.replaceLastWhitespace)(lastDraft.after, ws => (0, java_1.replaceIndentAfterLastNewline)(ws, parentIndent));
|
|
386
|
+
lastDraft.after = (0, java_1.replaceLastWhitespace)(lastDraft.after, ws => (0, java_1.replaceIndentAfterLastNewline)(ws, this.indentString(parentIndent)));
|
|
360
387
|
});
|
|
361
388
|
}
|
|
362
389
|
}
|
|
@@ -387,26 +414,23 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
387
414
|
});
|
|
388
415
|
}
|
|
389
416
|
preVisitLeftPadded(left) {
|
|
390
|
-
var _a, _b
|
|
391
|
-
|
|
392
|
-
const parentIndent
|
|
417
|
+
var _a, _b;
|
|
418
|
+
const parentContext = (_a = this.cursor.parent) === null || _a === void 0 ? void 0 : _a.messages.get("indentContext");
|
|
419
|
+
const [parentIndent, parentIndentKind] = parentContext !== null && parentContext !== void 0 ? parentContext : [0, 'continuation'];
|
|
393
420
|
const hasNewline = left.before.whitespace.includes("\n");
|
|
394
421
|
// Check if parent is a Binary in align mode - if so, don't add continuation indent
|
|
395
|
-
|
|
396
|
-
const parentValue = (_c = this.cursor.parent) === null || _c === void 0 ? void 0 : _c.value;
|
|
397
|
-
const parentIndentKind = (_d = this.cursor.parent) === null || _d === void 0 ? void 0 : _d.messages.get("indentKind");
|
|
422
|
+
const parentValue = (_b = this.cursor.parent) === null || _b === void 0 ? void 0 : _b.value;
|
|
398
423
|
const shouldAlign = (parentValue === null || parentValue === void 0 ? void 0 : parentValue.kind) === java_1.J.Kind.Binary && parentIndentKind === 'align';
|
|
399
424
|
// Compute myIndent INCLUDING continuation if applicable
|
|
400
|
-
// This ensures child elements see the correct parent indent
|
|
401
425
|
let myIndent = parentIndent;
|
|
402
426
|
if (hasNewline && !shouldAlign) {
|
|
403
|
-
myIndent = parentIndent + this.
|
|
427
|
+
myIndent = parentIndent + this.indentSize;
|
|
404
428
|
}
|
|
405
|
-
this.cursor.messages.set("
|
|
429
|
+
this.cursor.messages.set("indentContext", [myIndent, 'continuation']);
|
|
406
430
|
this.cursor.messages.set("hasNewline", hasNewline);
|
|
407
431
|
}
|
|
408
432
|
postVisitLeftPadded(left) {
|
|
409
|
-
var _a
|
|
433
|
+
var _a;
|
|
410
434
|
if (left === undefined) {
|
|
411
435
|
return undefined;
|
|
412
436
|
}
|
|
@@ -414,10 +438,11 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
414
438
|
if (!hasNewline) {
|
|
415
439
|
return left;
|
|
416
440
|
}
|
|
417
|
-
// Use the
|
|
418
|
-
const
|
|
441
|
+
// Use the indent we computed in preVisitLeftPadded (which includes continuation if applicable)
|
|
442
|
+
const context = this.cursor.messages.get("indentContext");
|
|
443
|
+
const [targetIndent] = context !== null && context !== void 0 ? context : [0, 'continuation'];
|
|
419
444
|
return (0, immer_1.produce)(left, draft => {
|
|
420
|
-
draft.before.whitespace = (0, java_1.replaceIndentAfterLastNewline)(draft.before.whitespace, targetIndent);
|
|
445
|
+
draft.before.whitespace = (0, java_1.replaceIndentAfterLastNewline)(draft.before.whitespace, this.indentString(targetIndent));
|
|
421
446
|
});
|
|
422
447
|
}
|
|
423
448
|
visitRightPadded(right, p) {
|
|
@@ -443,74 +468,42 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
443
468
|
});
|
|
444
469
|
}
|
|
445
470
|
preVisitRightPadded(right) {
|
|
446
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
447
|
-
|
|
448
|
-
const parentIndent
|
|
449
|
-
|
|
450
|
-
//
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
if (hasNewline && isMethodChain) {
|
|
457
|
-
// Search up the cursor hierarchy for an existing chainedIndent (to avoid stacking in method chains)
|
|
458
|
-
let existingChainedIndent;
|
|
459
|
-
for (let c = this.cursor.parent; c; c = c.parent) {
|
|
460
|
-
existingChainedIndent = c.messages.get("chainedIndent");
|
|
461
|
-
if (existingChainedIndent !== undefined) {
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (existingChainedIndent === undefined) {
|
|
466
|
-
myIndent = parentIndent + this.singleIndent;
|
|
467
|
-
// Set chainedIndent on parent so further chain elements don't stack
|
|
468
|
-
(_g = this.cursor.parent) === null || _g === void 0 ? void 0 : _g.messages.set("chainedIndent", myIndent);
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
myIndent = existingChainedIndent;
|
|
472
|
-
}
|
|
471
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
472
|
+
const parentContext = (_a = this.cursor.parent) === null || _a === void 0 ? void 0 : _a.messages.get("indentContext");
|
|
473
|
+
const [parentIndent, parentIndentKind] = parentContext !== null && parentContext !== void 0 ? parentContext : [0, 'continuation'];
|
|
474
|
+
// Check if parent has chainedIndentContext - if so, this is the select of a method chain
|
|
475
|
+
// Propagate chainedIndentContext but do NOT set indentContext
|
|
476
|
+
const parentChainedContext = (_b = this.cursor.parent) === null || _b === void 0 ? void 0 : _b.messages.get("chainedIndentContext");
|
|
477
|
+
if (parentChainedContext !== undefined) {
|
|
478
|
+
this.cursor.messages.set("chainedIndentContext", parentChainedContext);
|
|
479
|
+
// Do NOT set indentContext - child elements will use chainedIndentContext
|
|
480
|
+
return;
|
|
473
481
|
}
|
|
474
|
-
|
|
475
|
-
|
|
482
|
+
// Check if Parentheses wraps a Binary expression - if so, let Binary handle its own indent
|
|
483
|
+
const rightPaddedParentKind = (_d = (_c = this.cursor.parent) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.kind;
|
|
484
|
+
const elementKind = (_e = right.element) === null || _e === void 0 ? void 0 : _e.kind;
|
|
485
|
+
const isParenthesesWrappingBinary = rightPaddedParentKind === java_1.J.Kind.Parentheses &&
|
|
486
|
+
elementKind === java_1.J.Kind.Binary;
|
|
487
|
+
let myIndent = parentIndent;
|
|
488
|
+
if (!isParenthesesWrappingBinary && parentIndentKind !== 'align' && (0, java_1.isJava)(right.element) && this.prefixContainsNewline(right.element)) {
|
|
489
|
+
myIndent = parentIndent + this.indentSize;
|
|
476
490
|
// For spread elements with newlines, mark continuation as established
|
|
477
|
-
// This allows subsequent elements on the SAME line to inherit the continuation level
|
|
478
491
|
const element = right.element;
|
|
479
492
|
if (this.isSpreadElement(element)) {
|
|
480
|
-
(
|
|
493
|
+
(_f = this.cursor.parent) === null || _f === void 0 ? void 0 : _f.messages.set("continuationIndent", myIndent);
|
|
481
494
|
}
|
|
482
495
|
}
|
|
483
|
-
else if ((0, java_1.isJava)(right.element) && !this.
|
|
496
|
+
else if ((0, java_1.isJava)(right.element) && !this.prefixContainsNewline(right.element)) {
|
|
484
497
|
// Element has no newline - check if a previous sibling established continuation
|
|
485
|
-
const continuationIndent = (
|
|
498
|
+
const continuationIndent = (_g = this.cursor.parent) === null || _g === void 0 ? void 0 : _g.messages.get("continuationIndent");
|
|
486
499
|
if (continuationIndent !== undefined) {
|
|
487
500
|
myIndent = continuationIndent;
|
|
488
501
|
}
|
|
489
502
|
}
|
|
490
|
-
this.cursor.messages.set("myIndent", myIndent);
|
|
491
503
|
// Set 'align' for most RightPadded elements to prevent double-continuation
|
|
492
|
-
// EXCEPT when Parentheses wraps a Binary expression -
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const elementKind = (_m = right.element) === null || _m === void 0 ? void 0 : _m.kind;
|
|
496
|
-
const isParenthesesWrappingBinary = rightPaddedParentKind === java_1.J.Kind.Parentheses &&
|
|
497
|
-
elementKind === java_1.J.Kind.Binary;
|
|
498
|
-
if (!isParenthesesWrappingBinary) {
|
|
499
|
-
this.cursor.messages.set("indentKind", "align");
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
elementPrefixContainsNewline(element) {
|
|
503
|
-
var _a, _b;
|
|
504
|
-
// Check the element's own prefix
|
|
505
|
-
if ((0, java_1.lastWhitespace)(element.prefix).includes("\n")) {
|
|
506
|
-
return true;
|
|
507
|
-
}
|
|
508
|
-
// Also check for Spread marker's prefix
|
|
509
|
-
const spreadMarker = (_b = (_a = element.markers) === null || _a === void 0 ? void 0 : _a.markers) === null || _b === void 0 ? void 0 : _b.find(m => m.kind === tree_1.JS.Markers.Spread);
|
|
510
|
-
if (spreadMarker && (0, java_1.spaceContainsNewline)(spreadMarker.prefix)) {
|
|
511
|
-
return true;
|
|
512
|
-
}
|
|
513
|
-
return false;
|
|
504
|
+
// EXCEPT when Parentheses wraps a Binary expression - use continuation so Binary children align
|
|
505
|
+
const indentKind = isParenthesesWrappingBinary ? 'continuation' : 'align';
|
|
506
|
+
this.cursor.messages.set("indentContext", [myIndent, indentKind]);
|
|
514
507
|
}
|
|
515
508
|
/**
|
|
516
509
|
* Check if an element is a spread - either directly marked with Spread marker,
|
|
@@ -551,7 +544,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
551
544
|
setupAncestorIndents() {
|
|
552
545
|
const path = [];
|
|
553
546
|
let anchorCursor;
|
|
554
|
-
let anchorIndent =
|
|
547
|
+
let anchorIndent = 0;
|
|
555
548
|
for (let c = this.cursor.parent; c; c = c.parent) {
|
|
556
549
|
path.push(c);
|
|
557
550
|
const v = c.value;
|
|
@@ -560,13 +553,13 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
560
553
|
const idx = ws.lastIndexOf('\n');
|
|
561
554
|
if (idx !== -1) {
|
|
562
555
|
anchorCursor = c;
|
|
563
|
-
anchorIndent = ws.
|
|
556
|
+
anchorIndent = ws.length - idx - 1;
|
|
564
557
|
}
|
|
565
558
|
}
|
|
566
559
|
if (v.kind === tree_1.JS.Kind.CompilationUnit) {
|
|
567
560
|
if (!anchorCursor) {
|
|
568
561
|
anchorCursor = c;
|
|
569
|
-
anchorIndent =
|
|
562
|
+
anchorIndent = 0;
|
|
570
563
|
}
|
|
571
564
|
break;
|
|
572
565
|
}
|
|
@@ -581,8 +574,7 @@ class TabsAndIndentsVisitor extends visitor_1.JavaScriptVisitor {
|
|
|
581
574
|
const savedCursor = this.cursor;
|
|
582
575
|
this.cursor = c;
|
|
583
576
|
if (c === anchorCursor) {
|
|
584
|
-
c.messages.set("
|
|
585
|
-
c.messages.set("indentKind", this.computeIndentKind(v));
|
|
577
|
+
c.messages.set("indentContext", [anchorIndent, this.computeIndentKind(v)]);
|
|
586
578
|
}
|
|
587
579
|
else {
|
|
588
580
|
this.setupCursorMessagesForTree(c, v);
|