@openrewrite/rewrite 8.69.0 → 8.69.1
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/java/type.d.ts +5 -0
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +12 -0
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +9 -0
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +5 -9
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/{format.d.ts → format/format.d.ts} +15 -33
- package/dist/javascript/format/format.d.ts.map +1 -0
- package/dist/javascript/{format.js → format/format.js} +56 -313
- package/dist/javascript/format/format.js.map +1 -0
- package/dist/javascript/format/index.d.ts +3 -0
- package/dist/javascript/format/index.d.ts.map +1 -0
- package/dist/javascript/format/index.js +38 -0
- package/dist/javascript/format/index.js.map +1 -0
- package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts +28 -0
- package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts.map +1 -0
- package/dist/javascript/format/minimum-viable-spacing-visitor.js +308 -0
- package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -0
- package/dist/javascript/format/normalize-whitespace-visitor.d.ts +14 -0
- package/dist/javascript/format/normalize-whitespace-visitor.d.ts.map +1 -0
- package/dist/javascript/format/normalize-whitespace-visitor.js +65 -0
- package/dist/javascript/format/normalize-whitespace-visitor.js.map +1 -0
- package/dist/javascript/format/prettier-config-loader.d.ts +92 -0
- package/dist/javascript/format/prettier-config-loader.d.ts.map +1 -0
- package/dist/javascript/format/prettier-config-loader.js +419 -0
- package/dist/javascript/format/prettier-config-loader.js.map +1 -0
- package/dist/javascript/format/prettier-format.d.ts +111 -0
- package/dist/javascript/format/prettier-format.d.ts.map +1 -0
- package/dist/javascript/format/prettier-format.js +496 -0
- package/dist/javascript/format/prettier-format.js.map +1 -0
- package/dist/javascript/{tabs-and-indents-visitor.d.ts → format/tabs-and-indents-visitor.d.ts} +4 -4
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -0
- package/dist/javascript/{tabs-and-indents-visitor.js → format/tabs-and-indents-visitor.js} +7 -7
- package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -0
- package/dist/javascript/format/whitespace-reconciler.d.ts +106 -0
- package/dist/javascript/format/whitespace-reconciler.d.ts.map +1 -0
- package/dist/javascript/format/whitespace-reconciler.js +291 -0
- package/dist/javascript/format/whitespace-reconciler.js.map +1 -0
- package/dist/javascript/markers.d.ts.map +1 -1
- package/dist/javascript/markers.js +21 -0
- package/dist/javascript/markers.js.map +1 -1
- package/dist/javascript/parser.d.ts +15 -3
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +107 -24
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/recipes/auto-format.d.ts +3 -0
- package/dist/javascript/recipes/auto-format.d.ts.map +1 -1
- package/dist/javascript/recipes/auto-format.js +22 -1
- package/dist/javascript/recipes/auto-format.js.map +1 -1
- package/dist/javascript/style.d.ts +52 -1
- package/dist/javascript/style.d.ts.map +1 -1
- package/dist/javascript/style.js +43 -2
- package/dist/javascript/style.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +3 -4
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +6 -18
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/yaml/assertions.d.ts +4 -0
- package/dist/yaml/assertions.d.ts.map +1 -0
- package/dist/yaml/assertions.js +31 -0
- package/dist/yaml/assertions.js.map +1 -0
- package/dist/yaml/index.d.ts +2 -1
- package/dist/yaml/index.d.ts.map +1 -1
- package/dist/yaml/index.js +2 -1
- package/dist/yaml/index.js.map +1 -1
- package/package.json +5 -4
- package/src/java/type.ts +12 -0
- package/src/javascript/assertions.ts +9 -0
- package/src/javascript/comparator.ts +6 -11
- package/src/javascript/{format.ts → format/format.ts} +59 -267
- package/src/javascript/format/index.ts +21 -0
- package/src/javascript/format/minimum-viable-spacing-visitor.ts +256 -0
- package/src/javascript/format/normalize-whitespace-visitor.ts +42 -0
- package/src/javascript/format/prettier-config-loader.ts +422 -0
- package/src/javascript/format/prettier-format.ts +622 -0
- package/src/javascript/{tabs-and-indents-visitor.ts → format/tabs-and-indents-visitor.ts} +8 -8
- package/src/javascript/format/whitespace-reconciler.ts +345 -0
- package/src/javascript/markers.ts +19 -0
- package/src/javascript/parser.ts +107 -20
- package/src/javascript/recipes/auto-format.ts +28 -1
- package/src/javascript/style.ts +41 -2
- package/src/test/rewrite-test.ts +6 -18
- package/src/yaml/assertions.ts +28 -0
- package/src/yaml/index.ts +2 -1
- package/dist/javascript/format.d.ts.map +0 -1
- package/dist/javascript/format.js.map +0 -1
- package/dist/javascript/tabs-and-indents-visitor.d.ts.map +0 -1
- package/dist/javascript/tabs-and-indents-visitor.js.map +0 -1
|
@@ -13,19 +13,23 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {JS} from "
|
|
17
|
-
import {JavaScriptVisitor} from "
|
|
18
|
-
import {Comment, J, lastWhitespace, replaceLastWhitespace, Statement} from "
|
|
16
|
+
import {JS} from "../tree";
|
|
17
|
+
import {JavaScriptVisitor} from "../visitor";
|
|
18
|
+
import {Comment, J, lastWhitespace, replaceLastWhitespace, Statement} from "../../java";
|
|
19
19
|
import {Draft, produce} from "immer";
|
|
20
|
-
import {Cursor, isScope, Tree} from "
|
|
21
|
-
import {BlankLinesStyle, getStyle, SpacesStyle, StyleKind, TabsAndIndentsStyle, WrappingAndBracesStyle} from "
|
|
22
|
-
import {NamedStyles} from "
|
|
23
|
-
import {produceAsync} from "
|
|
24
|
-
import {
|
|
25
|
-
import {Generator} from "./markers";
|
|
20
|
+
import {Cursor, isScope, Tree} from "../../tree";
|
|
21
|
+
import {BlankLinesStyle, getStyle, SpacesStyle, StyleKind, TabsAndIndentsStyle, WrappingAndBracesStyle} from "../style";
|
|
22
|
+
import {NamedStyles} from "../../style";
|
|
23
|
+
import {produceAsync} from "../../visitor";
|
|
24
|
+
import {Generator} from "../markers";
|
|
26
25
|
import {TabsAndIndentsVisitor} from "./tabs-and-indents-visitor";
|
|
26
|
+
import {NormalizeWhitespaceVisitor} from "./normalize-whitespace-visitor";
|
|
27
|
+
import {MinimumViableSpacingVisitor} from "./minimum-viable-spacing-visitor";
|
|
28
|
+
import {applyPrettierFormatting, getPrettierStyle} from "./prettier-format";
|
|
27
29
|
|
|
28
30
|
export {TabsAndIndentsVisitor} from "./tabs-and-indents-visitor";
|
|
31
|
+
export {NormalizeWhitespaceVisitor} from "./normalize-whitespace-visitor";
|
|
32
|
+
export {MinimumViableSpacingVisitor} from "./minimum-viable-spacing-visitor";
|
|
29
33
|
|
|
30
34
|
export const maybeAutoFormat = async <J2 extends J, P>(before: J2, after: J2, p: P, stopAfter?: J, parent?: Cursor): Promise<J2> => {
|
|
31
35
|
if (before !== after) {
|
|
@@ -34,7 +38,13 @@ export const maybeAutoFormat = async <J2 extends J, P>(before: J2, after: J2, p:
|
|
|
34
38
|
return after;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
export const autoFormat = async <J2 extends J, P>(
|
|
41
|
+
export const autoFormat = async <J2 extends J, P>(
|
|
42
|
+
j: J2,
|
|
43
|
+
p: P,
|
|
44
|
+
stopAfter?: J,
|
|
45
|
+
parent?: Cursor,
|
|
46
|
+
styles?: NamedStyles<string>[]
|
|
47
|
+
): Promise<J2> =>
|
|
38
48
|
(await new AutoformatVisitor(stopAfter, styles).visit(j, p, parent) as J2);
|
|
39
49
|
|
|
40
50
|
/**
|
|
@@ -44,16 +54,26 @@ export const autoFormat = async <J2 extends J, P>(j: J2, p: P, stopAfter?: J, pa
|
|
|
44
54
|
* 1. Styles passed to the constructor
|
|
45
55
|
* 2. Styles from source file markers (NamedStyles)
|
|
46
56
|
* 3. IntelliJ defaults
|
|
57
|
+
*
|
|
58
|
+
* When a PrettierStyle is present (either in the styles array or as a marker on the source file),
|
|
59
|
+
* Prettier is used for formatting. Otherwise, built-in formatting visitors are used.
|
|
47
60
|
*/
|
|
48
61
|
export class AutoformatVisitor<P> extends JavaScriptVisitor<P> {
|
|
49
|
-
private readonly styles?: NamedStyles[];
|
|
62
|
+
private readonly styles?: NamedStyles<string>[];
|
|
50
63
|
|
|
51
|
-
constructor(private stopAfter?: Tree, styles?: NamedStyles[]) {
|
|
64
|
+
constructor(private stopAfter?: Tree, styles?: NamedStyles<string>[]) {
|
|
52
65
|
super();
|
|
53
66
|
this.styles = styles;
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
async visit<R extends J>(tree: Tree, p: P, cursor?: Cursor): Promise<R | undefined> {
|
|
70
|
+
// Check for PrettierStyle in styles array or as marker on source file
|
|
71
|
+
// If found, delegate entirely to Prettier (skip other formatting visitors)
|
|
72
|
+
const prettierStyle = getPrettierStyle(tree, cursor, this.styles);
|
|
73
|
+
if (prettierStyle) {
|
|
74
|
+
return applyPrettierFormatting(tree as R, prettierStyle, p, cursor, this.stopAfter);
|
|
75
|
+
}
|
|
76
|
+
|
|
57
77
|
const visitors = [
|
|
58
78
|
new NormalizeWhitespaceVisitor(this.stopAfter),
|
|
59
79
|
new MinimumViableSpacingVisitor(this.stopAfter),
|
|
@@ -75,29 +95,6 @@ export class AutoformatVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
75
95
|
}
|
|
76
96
|
}
|
|
77
97
|
|
|
78
|
-
export class NormalizeWhitespaceVisitor<P> extends JavaScriptVisitor<P> {
|
|
79
|
-
// called NormalizeFormat in Java
|
|
80
|
-
// Ensures that whitespace is on the outermost AST element possible
|
|
81
|
-
|
|
82
|
-
constructor(private stopAfter?: Tree) {
|
|
83
|
-
super();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
override async visit<R extends J>(tree: Tree, p: P, parent?: Cursor): Promise<R | undefined> {
|
|
87
|
-
if (this.cursor?.getNearestMessage("stop") != null) {
|
|
88
|
-
return tree as R;
|
|
89
|
-
}
|
|
90
|
-
return super.visit(tree, p, parent);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
override async postVisit(tree: J, p: P): Promise<J | undefined> {
|
|
94
|
-
if (this.stopAfter != null && isScope(this.stopAfter, tree)) {
|
|
95
|
-
this.cursor?.root.messages.set("stop", true);
|
|
96
|
-
}
|
|
97
|
-
return super.postVisit(tree, p);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
98
|
export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
102
99
|
constructor(private style: SpacesStyle, private stopAfter?: Tree) {
|
|
103
100
|
super();
|
|
@@ -580,6 +577,30 @@ export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
580
577
|
});
|
|
581
578
|
}
|
|
582
579
|
|
|
580
|
+
protected async visitTypeParameter(typeParam: J.TypeParameter, p: P): Promise<J | undefined> {
|
|
581
|
+
const ret = await super.visitTypeParameter(typeParam, p) as J.TypeParameter;
|
|
582
|
+
return produce(ret, draft => {
|
|
583
|
+
if (draft.bounds && draft.bounds.elements.length >= 2) {
|
|
584
|
+
const constraintType = draft.bounds.elements[0];
|
|
585
|
+
const defaultType = draft.bounds.elements[1];
|
|
586
|
+
const hasConstraint = constraintType.element.kind !== J.Kind.Empty;
|
|
587
|
+
const hasDefault = defaultType.element.kind !== J.Kind.Empty;
|
|
588
|
+
|
|
589
|
+
if (hasConstraint) {
|
|
590
|
+
// Space before '=' for default type (in the `after` of constraint type)
|
|
591
|
+
if (hasDefault && !constraintType.after.whitespace.includes("\n")) {
|
|
592
|
+
constraintType.after.whitespace = this.style.aroundOperators.assignment ? " " : "";
|
|
593
|
+
}
|
|
594
|
+
} else if (hasDefault) {
|
|
595
|
+
// No constraint, just default: space before '=' is in bounds.before
|
|
596
|
+
if (!draft.bounds.before.whitespace.includes("\n")) {
|
|
597
|
+
draft.bounds.before.whitespace = this.style.aroundOperators.assignment ? " " : "";
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
583
604
|
private async spaceAfterRightPadded<T extends J>(right: J.RightPadded<T>, spaceAfter: boolean): Promise<J.RightPadded<T>> {
|
|
584
605
|
if (right.after.comments.length > 0) {
|
|
585
606
|
// Perform the space rule for the suffix of the last comment only. Same as IntelliJ.
|
|
@@ -905,238 +926,6 @@ export class WrappingAndBracesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
905
926
|
}
|
|
906
927
|
}
|
|
907
928
|
|
|
908
|
-
|
|
909
|
-
export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
|
|
910
|
-
constructor(private stopAfter?: Tree) {
|
|
911
|
-
super();
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
override async visit<R extends J>(tree: Tree, p: P, parent?: Cursor): Promise<R | undefined> {
|
|
915
|
-
if (this.cursor?.getNearestMessage("stop") != null) {
|
|
916
|
-
return tree as R;
|
|
917
|
-
}
|
|
918
|
-
return super.visit(tree, p, parent);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
override async postVisit(tree: J, p: P): Promise<J | undefined> {
|
|
922
|
-
if (this.stopAfter != null && isScope(this.stopAfter, tree)) {
|
|
923
|
-
this.cursor?.root.messages.set("stop", true);
|
|
924
|
-
}
|
|
925
|
-
return super.postVisit(tree, p);
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
protected async visitAwait(await_: JS.Await, p: P): Promise<J | undefined> {
|
|
929
|
-
const ret = await super.visitAwait(await_, p) as JS.Await;
|
|
930
|
-
return produce(ret, draft => {
|
|
931
|
-
this.ensureSpace(draft.expression.prefix)
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
protected async visitClassDeclaration(classDecl: J.ClassDeclaration, p: P): Promise<J | undefined> {
|
|
936
|
-
let c = await super.visitClassDeclaration(classDecl, p) as J.ClassDeclaration;
|
|
937
|
-
let first = c.leadingAnnotations.length === 0;
|
|
938
|
-
|
|
939
|
-
if (c.modifiers.length > 0) {
|
|
940
|
-
if (!first && c.modifiers[0].prefix.whitespace === "") {
|
|
941
|
-
c = produce(c, draft => {
|
|
942
|
-
this.ensureSpace(draft.modifiers[0].prefix);
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
c = produce(c, draft => {
|
|
946
|
-
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
947
|
-
this.ensureSpace(draft.modifiers[i].prefix);
|
|
948
|
-
}
|
|
949
|
-
});
|
|
950
|
-
first = false;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
if (c.classKind.prefix.whitespace === "" && !first) {
|
|
954
|
-
c = produce(c, draft => {
|
|
955
|
-
this.ensureSpace(draft.classKind.prefix);
|
|
956
|
-
});
|
|
957
|
-
first = false;
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// anonymous classes have an empty name
|
|
961
|
-
if (c.name.simpleName !== "") {
|
|
962
|
-
c = produce(c, draft => {
|
|
963
|
-
this.ensureSpace(draft.name.prefix);
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Note: typeParameters should NOT have space before them - they immediately follow the class name
|
|
968
|
-
// e.g., "class DataTable<Row>" not "class DataTable <Row>"
|
|
969
|
-
|
|
970
|
-
if (c.extends && c.extends.before.whitespace === "") {
|
|
971
|
-
c = produce(c, draft => {
|
|
972
|
-
this.ensureSpace(draft.extends!.before);
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
if (c.implements && c.implements.before.whitespace === "") {
|
|
977
|
-
c = produce(c, draft => {
|
|
978
|
-
this.ensureSpace(draft.implements!.before);
|
|
979
|
-
if (draft.implements != undefined && draft.implements.elements.length > 0) {
|
|
980
|
-
this.ensureSpace(draft.implements.elements[0].element.prefix);
|
|
981
|
-
}
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
c = produce(c, draft => {
|
|
986
|
-
draft.body.prefix.whitespace = "";
|
|
987
|
-
});
|
|
988
|
-
|
|
989
|
-
return c;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
protected async visitMethodDeclaration(method: J.MethodDeclaration, p: P): Promise<J | undefined> {
|
|
993
|
-
let m = await super.visitMethodDeclaration(method, p) as J.MethodDeclaration;
|
|
994
|
-
let first = m.leadingAnnotations.length === 0;
|
|
995
|
-
|
|
996
|
-
if (m.modifiers.length > 0) {
|
|
997
|
-
if (!first && m.modifiers[0].prefix.whitespace === "") {
|
|
998
|
-
m = produce(m, draft => {
|
|
999
|
-
this.ensureSpace(draft.modifiers[0].prefix);
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
m = produce(m, draft => {
|
|
1003
|
-
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
1004
|
-
this.ensureSpace(draft.modifiers[i].prefix);
|
|
1005
|
-
}
|
|
1006
|
-
});
|
|
1007
|
-
first = false;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
// FunctionDeclaration marker check must come AFTER modifiers processing
|
|
1011
|
-
// to avoid adding unwanted space before the first modifier (e.g., 'async')
|
|
1012
|
-
if (findMarker(method, JS.Markers.FunctionDeclaration)) {
|
|
1013
|
-
first = false;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
if (!first && m.name.prefix.whitespace === "") {
|
|
1017
|
-
m = produce(m, draft => {
|
|
1018
|
-
this.ensureSpace(draft.name.prefix);
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
if (m.throws && m.throws.before.whitespace === "") {
|
|
1023
|
-
m = produce(m, draft => {
|
|
1024
|
-
this.ensureSpace(draft.throws!.before);
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
return m;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
protected async visitNamespaceDeclaration(namespaceDeclaration: JS.NamespaceDeclaration, p: P): Promise<J | undefined> {
|
|
1032
|
-
const ret = await super.visitNamespaceDeclaration(namespaceDeclaration, p) as JS.NamespaceDeclaration;
|
|
1033
|
-
return produce(ret, draft => {
|
|
1034
|
-
if (draft.modifiers.length > 0) {
|
|
1035
|
-
draft.keywordType.before.whitespace=" ";
|
|
1036
|
-
}
|
|
1037
|
-
this.ensureSpace(draft.name.element.prefix);
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
protected async visitNewClass(newClass: J.NewClass, p: P): Promise<J | undefined> {
|
|
1042
|
-
const ret = await super.visitNewClass(newClass, p) as J.NewClass;
|
|
1043
|
-
return produce(ret, draft => {
|
|
1044
|
-
if (draft.class) {
|
|
1045
|
-
if (draft.class.kind == J.Kind.Identifier) {
|
|
1046
|
-
this.ensureSpace((draft.class as Draft<J.Identifier>).prefix);
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
});
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
protected async visitReturn(returnNode: J.Return, p: P): Promise<J | undefined> {
|
|
1053
|
-
const r = await super.visitReturn(returnNode, p) as J.Return;
|
|
1054
|
-
if (r.expression && r.expression.prefix.whitespace === "" &&
|
|
1055
|
-
!r.markers.markers.find(m => m.id === "org.openrewrite.java.marker.ImplicitReturn")) {
|
|
1056
|
-
return produce(r, draft => {
|
|
1057
|
-
this.ensureSpace(draft.expression!.prefix);
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
return r;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
protected async visitThrow(thrown: J.Throw, p: P): Promise<J | undefined> {
|
|
1064
|
-
const ret = await super.visitThrow(thrown, p) as J.Throw;
|
|
1065
|
-
return ret && produce(ret, draft => {
|
|
1066
|
-
this.ensureSpace(draft.exception.prefix);
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
protected async visitTypeDeclaration(typeDeclaration: JS.TypeDeclaration, p: P): Promise<J | undefined> {
|
|
1071
|
-
const ret = await super.visitTypeDeclaration(typeDeclaration, p) as JS.TypeDeclaration;
|
|
1072
|
-
return produce(ret, draft => {
|
|
1073
|
-
if (draft.modifiers.length > 0) {
|
|
1074
|
-
this.ensureSpace(draft.name.before);
|
|
1075
|
-
}
|
|
1076
|
-
this.ensureSpace(draft.name.element.prefix);
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
protected async visitTypeOf(typeOf: JS.TypeOf, p: P): Promise<J | undefined> {
|
|
1081
|
-
const ret = await super.visitTypeOf(typeOf, p) as JS.TypeOf;
|
|
1082
|
-
return produce(ret, draft => {
|
|
1083
|
-
this.ensureSpace(draft.expression.prefix);
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
protected async visitTypeParameter(typeParam: J.TypeParameter, p: P): Promise<J | undefined> {
|
|
1088
|
-
const ret = await super.visitTypeParameter(typeParam, p) as J.TypeParameter;
|
|
1089
|
-
return produce(ret, draft => {
|
|
1090
|
-
if (draft.bounds && draft.bounds.elements.length > 0) {
|
|
1091
|
-
this.ensureSpace(draft.bounds.before);
|
|
1092
|
-
this.ensureSpace(draft.bounds.elements[0].element.prefix);
|
|
1093
|
-
}
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
protected async visitVariableDeclarations(v: J.VariableDeclarations, p: P): Promise<J | undefined> {
|
|
1098
|
-
let ret = await super.visitVariableDeclarations(v, p) as J.VariableDeclarations;
|
|
1099
|
-
let first = ret.leadingAnnotations.length === 0;
|
|
1100
|
-
|
|
1101
|
-
if (first && ret.modifiers.length > 0) {
|
|
1102
|
-
ret = produce(ret, draft => {
|
|
1103
|
-
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
1104
|
-
this.ensureSpace(draft.modifiers[i].prefix);
|
|
1105
|
-
}
|
|
1106
|
-
});
|
|
1107
|
-
first = false;
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
if (!first) {
|
|
1111
|
-
ret = produce(ret, draft => {
|
|
1112
|
-
this.ensureSpace(draft.variables[0].element.prefix);
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
return ret;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
protected async visitCase(caseNode: J.Case, p: P): Promise<J | undefined> {
|
|
1121
|
-
const c = await super.visitCase(caseNode, p) as J.Case;
|
|
1122
|
-
|
|
1123
|
-
if (c.guard && c.caseLabels.elements.length > 0 && c.caseLabels.elements[c.caseLabels.elements.length - 1].after.whitespace === "") {
|
|
1124
|
-
return produce(c, draft => {
|
|
1125
|
-
const last = draft.caseLabels.elements.length - 1;
|
|
1126
|
-
draft.caseLabels.elements[last].after.whitespace = " ";
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
return c;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
private ensureSpace(spaceDraft: Draft<J.Space>) {
|
|
1134
|
-
if (spaceDraft.whitespace.length === 0 && spaceDraft.comments.length === 0) {
|
|
1135
|
-
spaceDraft.whitespace = " ";
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
929
|
export class BlankLinesVisitor<P> extends JavaScriptVisitor<P> {
|
|
1141
930
|
constructor(private readonly style: BlankLinesStyle, private stopAfter?: Tree) {
|
|
1142
931
|
super();
|
|
@@ -1339,3 +1128,6 @@ export class BlankLinesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
1339
1128
|
return super.postVisit(tree, p);
|
|
1340
1129
|
}
|
|
1341
1130
|
}
|
|
1131
|
+
|
|
1132
|
+
// Re-export prettier formatting utilities
|
|
1133
|
+
export {prettierFormat} from "./prettier-format";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Main formatting entry point (includes AutoformatVisitor, autoFormat, maybeAutoFormat, and individual visitors)
|
|
18
|
+
export * from "./format";
|
|
19
|
+
|
|
20
|
+
// Prettier formatting (public API)
|
|
21
|
+
export {prettierFormat} from "./prettier-format";
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import {JavaScriptVisitor} from "../visitor";
|
|
17
|
+
import {J} from "../../java";
|
|
18
|
+
import {Cursor, isScope, Tree} from "../../tree";
|
|
19
|
+
import {JS} from "../tree";
|
|
20
|
+
import {Draft, produce} from "immer";
|
|
21
|
+
import {findMarker} from "../../markers";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Ensures minimum viable spacing between AST elements.
|
|
25
|
+
* Adds required spaces where they are missing (e.g., after keywords).
|
|
26
|
+
*/
|
|
27
|
+
export class MinimumViableSpacingVisitor<P> extends JavaScriptVisitor<P> {
|
|
28
|
+
constructor(private stopAfter?: Tree) {
|
|
29
|
+
super();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override async visit<R extends J>(tree: Tree, p: P, parent?: Cursor): Promise<R | undefined> {
|
|
33
|
+
if (this.cursor?.getNearestMessage("stop") != null) {
|
|
34
|
+
return tree as R;
|
|
35
|
+
}
|
|
36
|
+
return super.visit(tree, p, parent);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override async postVisit(tree: J, p: P): Promise<J | undefined> {
|
|
40
|
+
if (this.stopAfter != null && isScope(this.stopAfter, tree)) {
|
|
41
|
+
this.cursor?.root.messages.set("stop", true);
|
|
42
|
+
}
|
|
43
|
+
return super.postVisit(tree, p);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected async visitAwait(await_: JS.Await, p: P): Promise<J | undefined> {
|
|
47
|
+
const ret = await super.visitAwait(await_, p) as JS.Await;
|
|
48
|
+
return produce(ret, draft => {
|
|
49
|
+
this.ensureSpace(draft.expression.prefix)
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected async visitClassDeclaration(classDecl: J.ClassDeclaration, p: P): Promise<J | undefined> {
|
|
54
|
+
let c = await super.visitClassDeclaration(classDecl, p) as J.ClassDeclaration;
|
|
55
|
+
let first = c.leadingAnnotations.length === 0;
|
|
56
|
+
|
|
57
|
+
if (c.modifiers.length > 0) {
|
|
58
|
+
if (!first && c.modifiers[0].prefix.whitespace === "") {
|
|
59
|
+
c = produce(c, draft => {
|
|
60
|
+
this.ensureSpace(draft.modifiers[0].prefix);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
c = produce(c, draft => {
|
|
64
|
+
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
65
|
+
this.ensureSpace(draft.modifiers[i].prefix);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
first = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (c.classKind.prefix.whitespace === "" && !first) {
|
|
72
|
+
c = produce(c, draft => {
|
|
73
|
+
this.ensureSpace(draft.classKind.prefix);
|
|
74
|
+
});
|
|
75
|
+
first = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// anonymous classes have an empty name
|
|
79
|
+
if (c.name.simpleName !== "") {
|
|
80
|
+
c = produce(c, draft => {
|
|
81
|
+
this.ensureSpace(draft.name.prefix);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Note: typeParameters should NOT have space before them - they immediately follow the class name
|
|
86
|
+
// e.g., "class DataTable<Row>" not "class DataTable <Row>"
|
|
87
|
+
|
|
88
|
+
if (c.extends && c.extends.before.whitespace === "") {
|
|
89
|
+
c = produce(c, draft => {
|
|
90
|
+
this.ensureSpace(draft.extends!.before);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (c.implements && c.implements.before.whitespace === "") {
|
|
95
|
+
c = produce(c, draft => {
|
|
96
|
+
this.ensureSpace(draft.implements!.before);
|
|
97
|
+
if (draft.implements != undefined && draft.implements.elements.length > 0) {
|
|
98
|
+
this.ensureSpace(draft.implements.elements[0].element.prefix);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
c = produce(c, draft => {
|
|
104
|
+
draft.body.prefix.whitespace = "";
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return c;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected async visitMethodDeclaration(method: J.MethodDeclaration, p: P): Promise<J | undefined> {
|
|
111
|
+
let m = await super.visitMethodDeclaration(method, p) as J.MethodDeclaration;
|
|
112
|
+
let first = m.leadingAnnotations.length === 0;
|
|
113
|
+
|
|
114
|
+
if (m.modifiers.length > 0) {
|
|
115
|
+
if (!first && m.modifiers[0].prefix.whitespace === "") {
|
|
116
|
+
m = produce(m, draft => {
|
|
117
|
+
this.ensureSpace(draft.modifiers[0].prefix);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
m = produce(m, draft => {
|
|
121
|
+
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
122
|
+
this.ensureSpace(draft.modifiers[i].prefix);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
first = false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// FunctionDeclaration marker check must come AFTER modifiers processing
|
|
129
|
+
// to avoid adding unwanted space before the first modifier (e.g., 'async')
|
|
130
|
+
if (findMarker(method, JS.Markers.FunctionDeclaration)) {
|
|
131
|
+
first = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!first && m.name.prefix.whitespace === "") {
|
|
135
|
+
m = produce(m, draft => {
|
|
136
|
+
this.ensureSpace(draft.name.prefix);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (m.throws && m.throws.before.whitespace === "") {
|
|
141
|
+
m = produce(m, draft => {
|
|
142
|
+
this.ensureSpace(draft.throws!.before);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return m;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
protected async visitNamespaceDeclaration(namespaceDeclaration: JS.NamespaceDeclaration, p: P): Promise<J | undefined> {
|
|
150
|
+
const ret = await super.visitNamespaceDeclaration(namespaceDeclaration, p) as JS.NamespaceDeclaration;
|
|
151
|
+
return produce(ret, draft => {
|
|
152
|
+
if (draft.modifiers.length > 0) {
|
|
153
|
+
draft.keywordType.before.whitespace=" ";
|
|
154
|
+
}
|
|
155
|
+
this.ensureSpace(draft.name.element.prefix);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected async visitNewClass(newClass: J.NewClass, p: P): Promise<J | undefined> {
|
|
160
|
+
const ret = await super.visitNewClass(newClass, p) as J.NewClass;
|
|
161
|
+
return produce(ret, draft => {
|
|
162
|
+
if (draft.class) {
|
|
163
|
+
if (draft.class.kind == J.Kind.Identifier) {
|
|
164
|
+
this.ensureSpace((draft.class as Draft<J.Identifier>).prefix);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected async visitReturn(returnNode: J.Return, p: P): Promise<J | undefined> {
|
|
171
|
+
const r = await super.visitReturn(returnNode, p) as J.Return;
|
|
172
|
+
if (r.expression && r.expression.prefix.whitespace === "" &&
|
|
173
|
+
!r.markers.markers.find(m => m.id === "org.openrewrite.java.marker.ImplicitReturn")) {
|
|
174
|
+
return produce(r, draft => {
|
|
175
|
+
this.ensureSpace(draft.expression!.prefix);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return r;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected async visitThrow(thrown: J.Throw, p: P): Promise<J | undefined> {
|
|
182
|
+
const ret = await super.visitThrow(thrown, p) as J.Throw;
|
|
183
|
+
return ret && produce(ret, draft => {
|
|
184
|
+
this.ensureSpace(draft.exception.prefix);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected async visitTypeDeclaration(typeDeclaration: JS.TypeDeclaration, p: P): Promise<J | undefined> {
|
|
189
|
+
const ret = await super.visitTypeDeclaration(typeDeclaration, p) as JS.TypeDeclaration;
|
|
190
|
+
return produce(ret, draft => {
|
|
191
|
+
if (draft.modifiers.length > 0) {
|
|
192
|
+
this.ensureSpace(draft.name.before);
|
|
193
|
+
}
|
|
194
|
+
this.ensureSpace(draft.name.element.prefix);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected async visitTypeOf(typeOf: JS.TypeOf, p: P): Promise<J | undefined> {
|
|
199
|
+
const ret = await super.visitTypeOf(typeOf, p) as JS.TypeOf;
|
|
200
|
+
return produce(ret, draft => {
|
|
201
|
+
this.ensureSpace(draft.expression.prefix);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
protected async visitTypeParameter(typeParam: J.TypeParameter, p: P): Promise<J | undefined> {
|
|
206
|
+
const ret = await super.visitTypeParameter(typeParam, p) as J.TypeParameter;
|
|
207
|
+
return produce(ret, draft => {
|
|
208
|
+
if (draft.bounds && draft.bounds.elements.length > 0) {
|
|
209
|
+
this.ensureSpace(draft.bounds.before);
|
|
210
|
+
this.ensureSpace(draft.bounds.elements[0].element.prefix);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
protected async visitVariableDeclarations(v: J.VariableDeclarations, p: P): Promise<J | undefined> {
|
|
216
|
+
let ret = await super.visitVariableDeclarations(v, p) as J.VariableDeclarations;
|
|
217
|
+
let first = ret.leadingAnnotations.length === 0;
|
|
218
|
+
|
|
219
|
+
if (first && ret.modifiers.length > 0) {
|
|
220
|
+
ret = produce(ret, draft => {
|
|
221
|
+
for (let i = 1; i < draft.modifiers.length; i++) {
|
|
222
|
+
this.ensureSpace(draft.modifiers[i].prefix);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
first = false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!first) {
|
|
229
|
+
ret = produce(ret, draft => {
|
|
230
|
+
this.ensureSpace(draft.variables[0].element.prefix);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return ret;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
protected async visitCase(caseNode: J.Case, p: P): Promise<J | undefined> {
|
|
239
|
+
const c = await super.visitCase(caseNode, p) as J.Case;
|
|
240
|
+
|
|
241
|
+
if (c.guard && c.caseLabels.elements.length > 0 && c.caseLabels.elements[c.caseLabels.elements.length - 1].after.whitespace === "") {
|
|
242
|
+
return produce(c, draft => {
|
|
243
|
+
const last = draft.caseLabels.elements.length - 1;
|
|
244
|
+
draft.caseLabels.elements[last].after.whitespace = " ";
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return c;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private ensureSpace(spaceDraft: Draft<J.Space>) {
|
|
252
|
+
if (spaceDraft.whitespace.length === 0 && spaceDraft.comments.length === 0) {
|
|
253
|
+
spaceDraft.whitespace = " ";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|