@openrewrite/rewrite 8.67.0-20251119-160338 → 8.67.0-20251119-202228
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/templating/capture.d.ts +3 -4
- package/dist/javascript/templating/capture.d.ts.map +1 -1
- package/dist/javascript/templating/capture.js +3 -3
- package/dist/javascript/templating/capture.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +7 -1
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +45 -10
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +10 -11
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +18 -36
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/rewrite.js +2 -2
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +27 -13
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +31 -14
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +111 -15
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/templating/capture.ts +7 -7
- package/src/javascript/templating/comparator.ts +50 -11
- package/src/javascript/templating/pattern.ts +32 -24
- package/src/javascript/templating/rewrite.ts +2 -2
- package/src/javascript/templating/template.ts +36 -18
- package/src/javascript/templating/types.ts +127 -16
|
@@ -16,10 +16,45 @@ export interface VariadicOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
max?: number;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Read-only access to captures matched so far during pattern matching.
|
|
21
|
+
* Provides a consistent interface with MatchResult for looking up captured values.
|
|
22
|
+
*/
|
|
23
|
+
export interface CaptureMap {
|
|
24
|
+
/**
|
|
25
|
+
* Gets the value of a capture by Capture object or name.
|
|
26
|
+
* Returns undefined if the capture hasn't been matched yet.
|
|
27
|
+
*/
|
|
28
|
+
get<T>(capture: Capture<T>): T | undefined;
|
|
29
|
+
get(capture: string): any;
|
|
30
|
+
/**
|
|
31
|
+
* Checks if a capture has been matched.
|
|
32
|
+
*/
|
|
33
|
+
has(capture: Capture | string): boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Context passed to capture constraint functions.
|
|
37
|
+
* Provides access to the cursor for AST navigation and previously matched captures.
|
|
38
|
+
*/
|
|
39
|
+
export interface CaptureConstraintContext {
|
|
40
|
+
/**
|
|
41
|
+
* The cursor pointing to the node being matched.
|
|
42
|
+
* Allows navigating the AST (parent, root, etc.).
|
|
43
|
+
*/
|
|
44
|
+
cursor: Cursor;
|
|
45
|
+
/**
|
|
46
|
+
* Read-only view of values captured so far in the matching process.
|
|
47
|
+
* Allows constraints to depend on previous captures.
|
|
48
|
+
* Returns undefined for captures that haven't been processed yet.
|
|
49
|
+
*/
|
|
50
|
+
captures: CaptureMap;
|
|
51
|
+
}
|
|
19
52
|
/**
|
|
20
53
|
* Constraint function for captures.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
54
|
+
*
|
|
55
|
+
* Receives the node being validated and a context providing access to:
|
|
56
|
+
* - cursor: For navigating the AST
|
|
57
|
+
* - captures: For accessing previously matched captures
|
|
23
58
|
*
|
|
24
59
|
* For non-variadic captures: use ConstraintFunction<T> where T is the node type
|
|
25
60
|
* For variadic captures: use ConstraintFunction<T[]> where T[] is the array type
|
|
@@ -27,7 +62,7 @@ export interface VariadicOptions {
|
|
|
27
62
|
* When used with variadic captures, the cursor points to the nearest common parent
|
|
28
63
|
* of the captured elements.
|
|
29
64
|
*/
|
|
30
|
-
export type ConstraintFunction<T> = (node: T,
|
|
65
|
+
export type ConstraintFunction<T> = (node: T, context: CaptureConstraintContext) => boolean;
|
|
31
66
|
/**
|
|
32
67
|
* Options for the capture function.
|
|
33
68
|
*
|
|
@@ -36,39 +71,45 @@ export type ConstraintFunction<T> = (node: T, cursor: Cursor) => boolean;
|
|
|
36
71
|
* - For regular captures: constraint receives a single node of type T
|
|
37
72
|
* - For variadic captures: constraint receives an array of nodes of type T[]
|
|
38
73
|
*
|
|
39
|
-
* The
|
|
40
|
-
* context-aware validation during pattern matching.
|
|
74
|
+
* The context parameter provides access to the cursor and previously matched captures.
|
|
41
75
|
*/
|
|
42
76
|
export interface CaptureOptions<T = any> {
|
|
43
77
|
name?: string;
|
|
44
78
|
variadic?: boolean | VariadicOptions;
|
|
45
79
|
/**
|
|
46
80
|
* Optional constraint function that validates whether a captured node should be accepted.
|
|
47
|
-
* The function
|
|
81
|
+
* The function receives:
|
|
48
82
|
* - node: The captured node (or array of nodes for variadic captures)
|
|
49
|
-
* -
|
|
50
|
-
*
|
|
51
|
-
* Functions can choose to accept just the node parameter if they don't need the cursor.
|
|
83
|
+
* - context: Provides access to cursor and previously matched captures
|
|
52
84
|
*
|
|
53
85
|
* @param node The captured node to validate
|
|
54
|
-
* @param cursor
|
|
86
|
+
* @param context Provides cursor for AST navigation and previously matched captures
|
|
55
87
|
* @returns true if the capture should be accepted, false otherwise
|
|
56
88
|
*
|
|
57
89
|
* @example
|
|
58
90
|
* ```typescript
|
|
59
|
-
* // Simple node validation
|
|
91
|
+
* // Simple node validation
|
|
60
92
|
* capture<J.Literal>('size', {
|
|
61
93
|
* constraint: (node) => typeof node.value === 'number' && node.value > 100
|
|
62
94
|
* })
|
|
63
95
|
*
|
|
64
|
-
* // Context-aware validation
|
|
96
|
+
* // Context-aware validation using cursor
|
|
65
97
|
* capture<J.MethodInvocation>('method', {
|
|
66
|
-
* constraint: (node,
|
|
98
|
+
* constraint: (node, context) => {
|
|
67
99
|
* if (!node.name.simpleName.startsWith('get')) return false;
|
|
68
|
-
* const cls = cursor.firstEnclosing(isClassDeclaration);
|
|
100
|
+
* const cls = context.cursor.firstEnclosing(isClassDeclaration);
|
|
69
101
|
* return cls?.name.simpleName === 'ApiController';
|
|
70
102
|
* }
|
|
71
103
|
* })
|
|
104
|
+
*
|
|
105
|
+
* // Validation depending on previous captures
|
|
106
|
+
* const min = capture('min');
|
|
107
|
+
* const max = capture('max', {
|
|
108
|
+
* constraint: (node, context) => {
|
|
109
|
+
* const minVal = context.captures.get(min);
|
|
110
|
+
* return minVal && node.value > minVal.value;
|
|
111
|
+
* }
|
|
112
|
+
* })
|
|
72
113
|
* ```
|
|
73
114
|
*/
|
|
74
115
|
constraint?: ConstraintFunction<T>;
|
|
@@ -77,9 +118,37 @@ export interface CaptureOptions<T = any> {
|
|
|
77
118
|
* a preamble declaring the capture identifier with this type annotation, allowing
|
|
78
119
|
* the TypeScript parser/compiler to produce a properly type-attributed AST.
|
|
79
120
|
*
|
|
121
|
+
* **Why Use Type Attribution:**
|
|
122
|
+
* When matching against TypeScript code with type information, providing a type ensures
|
|
123
|
+
* the pattern's AST has matching type attribution, which can be important for:
|
|
124
|
+
* - Semantic matching based on types
|
|
125
|
+
* - Matching code that depends on type inference
|
|
126
|
+
* - Ensuring pattern parses with correct type context
|
|
127
|
+
*
|
|
80
128
|
* Can be specified as:
|
|
81
|
-
* - A string type annotation (e.g., "boolean", "string", "number")
|
|
129
|
+
* - A string type annotation (e.g., "boolean", "string", "number", "Promise<any>", "User[]")
|
|
82
130
|
* - A Type instance from the AST (the type will be inferred from the Type)
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* // Match promise chains with proper type attribution
|
|
135
|
+
* const chain = capture({
|
|
136
|
+
* name: 'chain',
|
|
137
|
+
* type: 'Promise<any>', // TypeScript will attribute this as Promise type
|
|
138
|
+
* constraint: (call: J.MethodInvocation) => {
|
|
139
|
+
* // Validate promise chain structure
|
|
140
|
+
* return call.name.simpleName === 'then';
|
|
141
|
+
* }
|
|
142
|
+
* });
|
|
143
|
+
* pattern`${chain}.catch(err => console.log(err))`
|
|
144
|
+
*
|
|
145
|
+
* // Match arrays with type annotation
|
|
146
|
+
* const items = capture({
|
|
147
|
+
* name: 'items',
|
|
148
|
+
* type: 'number[]', // Array of numbers
|
|
149
|
+
* });
|
|
150
|
+
* pattern`${items}.map(x => x * 2)`
|
|
151
|
+
* ```
|
|
83
152
|
*/
|
|
84
153
|
type?: string | Type;
|
|
85
154
|
}
|
|
@@ -360,6 +429,33 @@ export interface TemplateOptions {
|
|
|
360
429
|
*/
|
|
361
430
|
dependencies?: Record<string, string>;
|
|
362
431
|
}
|
|
432
|
+
/**
|
|
433
|
+
* Options for template application.
|
|
434
|
+
*/
|
|
435
|
+
export interface ApplyOptions {
|
|
436
|
+
/**
|
|
437
|
+
* Values for parameters in the template.
|
|
438
|
+
* Can be a Map, MatchResult, or plain object with capture names as keys.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```typescript
|
|
442
|
+
* // Using MatchResult from pattern matching
|
|
443
|
+
* const match = await pattern.match(node, cursor);
|
|
444
|
+
* await template.apply(node, cursor, { values: match });
|
|
445
|
+
*
|
|
446
|
+
* // Using a Map
|
|
447
|
+
* await template.apply(node, cursor, {
|
|
448
|
+
* values: new Map([['x', someNode]])
|
|
449
|
+
* });
|
|
450
|
+
*
|
|
451
|
+
* // Using a plain object
|
|
452
|
+
* await template.apply(node, cursor, {
|
|
453
|
+
* values: { x: someNode }
|
|
454
|
+
* });
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
values?: Map<Capture | string, J> | MatchResult | Record<string, J>;
|
|
458
|
+
}
|
|
363
459
|
/**
|
|
364
460
|
* Represents a replacement rule that can match a pattern and apply a template.
|
|
365
461
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/types.ts"],"names":[],"mappings":"AAeA,OAAO,EAAC,MAAM,EAAE,IAAI,EAAC,MAAM,OAAO,CAAC;AACnC,OAAO,EAAC,CAAC,EAAE,IAAI,EAAC,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAC,YAAY,EAAE,OAAO,EAAC,MAAM,WAAW,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/javascript/templating/types.ts"],"names":[],"mappings":"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"}
|
package/dist/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
8.67.0-20251119-
|
|
1
|
+
8.67.0-20251119-202228
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import {Cursor} from '../..';
|
|
17
17
|
import {J, Type} from '../../java';
|
|
18
|
-
import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
|
|
18
|
+
import {Any, Capture, CaptureConstraintContext, CaptureOptions, ConstraintFunction, TemplateParam, VariadicOptions} from './types';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Combines multiple constraints with AND logic.
|
|
@@ -30,8 +30,8 @@ import {Any, Capture, CaptureOptions, ConstraintFunction, TemplateParam, Variadi
|
|
|
30
30
|
* )
|
|
31
31
|
* });
|
|
32
32
|
*/
|
|
33
|
-
export function and<T>(...constraints:
|
|
34
|
-
return (node: T,
|
|
33
|
+
export function and<T>(...constraints: ConstraintFunction<T>[]): ConstraintFunction<T> {
|
|
34
|
+
return (node: T, context: CaptureConstraintContext) => constraints.every(c => c(node, context));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -46,8 +46,8 @@ export function and<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[]
|
|
|
46
46
|
* )
|
|
47
47
|
* });
|
|
48
48
|
*/
|
|
49
|
-
export function or<T>(...constraints:
|
|
50
|
-
return (node: T,
|
|
49
|
+
export function or<T>(...constraints: ConstraintFunction<T>[]): ConstraintFunction<T> {
|
|
50
|
+
return (node: T, context: CaptureConstraintContext) => constraints.some(c => c(node, context));
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -59,8 +59,8 @@ export function or<T>(...constraints: ((node: T, cursor?: Cursor) => boolean)[])
|
|
|
59
59
|
* constraint: not((node) => typeof node.value === 'string')
|
|
60
60
|
* });
|
|
61
61
|
*/
|
|
62
|
-
export function not<T>(constraint:
|
|
63
|
-
return (node: T,
|
|
62
|
+
export function not<T>(constraint: ConstraintFunction<T>): ConstraintFunction<T> {
|
|
63
|
+
return (node: T, context: CaptureConstraintContext) => !constraint(node, context);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// Symbol to access the internal capture name without triggering Proxy
|
|
@@ -18,7 +18,8 @@ import {J} from '../../java';
|
|
|
18
18
|
import {JS} from '../index';
|
|
19
19
|
import {JavaScriptSemanticComparatorVisitor} from '../comparator';
|
|
20
20
|
import {CaptureMarker, CaptureStorageValue, PlaceholderUtils} from './utils';
|
|
21
|
-
import {DebugLogEntry, MatchExplanation} from './types';
|
|
21
|
+
import {Capture, CaptureConstraintContext, CaptureMap, DebugLogEntry, MatchExplanation} from './types';
|
|
22
|
+
import {CAPTURE_NAME_SYMBOL} from './capture';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Debug callbacks for pattern matching.
|
|
@@ -62,6 +63,28 @@ export interface MatcherCallbacks {
|
|
|
62
63
|
debug?: DebugCallbacks;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Implementation of CaptureMap that wraps the capture storage.
|
|
68
|
+
* Provides read-only access to previously matched captures.
|
|
69
|
+
*/
|
|
70
|
+
class CaptureMapImpl implements CaptureMap {
|
|
71
|
+
constructor(private readonly storage: Map<string, CaptureStorageValue>) {}
|
|
72
|
+
|
|
73
|
+
get<T>(capture: Capture<T>): T | undefined;
|
|
74
|
+
get(capture: string): any;
|
|
75
|
+
get(capture: Capture | string): any {
|
|
76
|
+
// Use symbol to get internal name without triggering Proxy
|
|
77
|
+
const name = typeof capture === 'string' ? capture : ((capture as any)[CAPTURE_NAME_SYMBOL] || capture.getName());
|
|
78
|
+
return this.storage.get(name);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
has(capture: Capture | string): boolean {
|
|
82
|
+
// Use symbol to get internal name without triggering Proxy
|
|
83
|
+
const name = typeof capture === 'string' ? capture : ((capture as any)[CAPTURE_NAME_SYMBOL] || capture.getName());
|
|
84
|
+
return this.storage.has(name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
65
88
|
/**
|
|
66
89
|
* A comparator for pattern matching that is lenient about optional properties.
|
|
67
90
|
* Allows patterns without type annotations to match actual code with type annotations.
|
|
@@ -76,6 +99,19 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
76
99
|
super(lenientTypeMatching);
|
|
77
100
|
}
|
|
78
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Builds the constraint context with the cursor and current captures.
|
|
104
|
+
* @param cursor The cursor to include in the context
|
|
105
|
+
* @returns The constraint context for evaluating capture constraints
|
|
106
|
+
*/
|
|
107
|
+
protected buildConstraintContext(cursor: Cursor): CaptureConstraintContext {
|
|
108
|
+
const state = this.matcher.saveState();
|
|
109
|
+
return {
|
|
110
|
+
cursor,
|
|
111
|
+
captures: new CaptureMapImpl(state.storage)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
79
115
|
override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
|
|
80
116
|
// Check if the pattern node is a capture - this handles unwrapped captures
|
|
81
117
|
// (Wrapped captures in J.RightPadded are handled by visitRightPadded override)
|
|
@@ -91,12 +127,15 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
91
127
|
: new Cursor(p);
|
|
92
128
|
this.targetCursor = cursorAtCapturedNode;
|
|
93
129
|
try {
|
|
94
|
-
// Evaluate constraint with cursor
|
|
130
|
+
// Evaluate constraint with context (cursor + previous captures)
|
|
95
131
|
// Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
|
|
96
|
-
if (captureMarker.constraint && !captureMarker.variadicOptions
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
132
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
133
|
+
const context = this.buildConstraintContext(cursorAtCapturedNode);
|
|
134
|
+
if (!captureMarker.constraint(p, context)) {
|
|
135
|
+
const captureName = captureMarker.captureName || 'unnamed';
|
|
136
|
+
const targetKind = (p as any).kind || 'unknown';
|
|
137
|
+
return this.constraintFailed(captureName, targetKind) as R;
|
|
138
|
+
}
|
|
100
139
|
}
|
|
101
140
|
|
|
102
141
|
const success = this.matcher.handleCapture(captureMarker, p, undefined);
|
|
@@ -164,7 +203,7 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
164
203
|
try {
|
|
165
204
|
// Evaluate constraint with cursor at the captured node (always defined)
|
|
166
205
|
// Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
|
|
167
|
-
if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement as J, cursorAtCapturedNode)) {
|
|
206
|
+
if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement as J, this.buildConstraintContext(cursorAtCapturedNode))) {
|
|
168
207
|
const captureName = captureMarker.captureName || 'unnamed';
|
|
169
208
|
const targetKind = (targetElement as any).kind || 'unknown';
|
|
170
209
|
return this.constraintFailed(captureName, targetKind);
|
|
@@ -604,7 +643,7 @@ export class PatternMatchingComparator extends JavaScriptSemanticComparatorVisit
|
|
|
604
643
|
// The targetCursor points to the parent container (always defined in container matching)
|
|
605
644
|
if (captureMarker.constraint) {
|
|
606
645
|
const cursor = this.targetCursor || new Cursor(targetElements[0]);
|
|
607
|
-
if (!captureMarker.constraint(capturedElements as any, cursor)) {
|
|
646
|
+
if (!captureMarker.constraint(capturedElements as any, this.buildConstraintContext(cursor))) {
|
|
608
647
|
continue; // Try next consumption amount
|
|
609
648
|
}
|
|
610
649
|
}
|
|
@@ -868,7 +907,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
|
|
|
868
907
|
try {
|
|
869
908
|
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
870
909
|
this.debug.log('debug', 'constraint', `Evaluating constraint for capture: ${captureMarker.captureName}`);
|
|
871
|
-
const constraintResult = captureMarker.constraint(p, cursorAtCapturedNode);
|
|
910
|
+
const constraintResult = captureMarker.constraint(p, this.buildConstraintContext(cursorAtCapturedNode));
|
|
872
911
|
if (!constraintResult) {
|
|
873
912
|
this.debug.log('info', 'constraint', `Constraint failed for capture: ${captureMarker.captureName}`);
|
|
874
913
|
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${(p as any).kind}`, `Constraint evaluation returned false`);
|
|
@@ -965,7 +1004,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
|
|
|
965
1004
|
try {
|
|
966
1005
|
if (captureMarker.constraint && !captureMarker.variadicOptions) {
|
|
967
1006
|
this.debug.log('debug', 'constraint', `Evaluating constraint for wrapped capture: ${captureMarker.captureName}`);
|
|
968
|
-
const constraintResult = captureMarker.constraint(targetElement as J, cursorAtCapturedNode);
|
|
1007
|
+
const constraintResult = captureMarker.constraint(targetElement as J, this.buildConstraintContext(cursorAtCapturedNode));
|
|
969
1008
|
if (!constraintResult) {
|
|
970
1009
|
this.debug.log('info', 'constraint', `Constraint failed for wrapped capture: ${captureMarker.captureName}`);
|
|
971
1010
|
this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${(targetElement as any).kind}`, `Constraint evaluation returned false`);
|
|
@@ -1316,7 +1355,7 @@ export class DebugPatternMatchingComparator extends PatternMatchingComparator {
|
|
|
1316
1355
|
if (captureMarker.constraint) {
|
|
1317
1356
|
this.debug.log('debug', 'constraint', `Evaluating variadic constraint for capture: ${captureMarker.captureName} (${capturedElements.length} elements)`);
|
|
1318
1357
|
const cursor = this.targetCursor || new Cursor(targetElements[0]);
|
|
1319
|
-
const constraintResult = captureMarker.constraint(capturedElements as any, cursor);
|
|
1358
|
+
const constraintResult = captureMarker.constraint(capturedElements as any, this.buildConstraintContext(cursor));
|
|
1320
1359
|
if (!constraintResult) {
|
|
1321
1360
|
this.debug.log('info', 'constraint', `Variadic constraint failed for capture: ${captureMarker.captureName}`);
|
|
1322
1361
|
continue;
|
|
@@ -15,7 +15,17 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import {Cursor} from '../..';
|
|
17
17
|
import {J} from '../../java';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Any,
|
|
20
|
+
Capture,
|
|
21
|
+
DebugLogEntry,
|
|
22
|
+
DebugOptions,
|
|
23
|
+
MatchAttemptResult,
|
|
24
|
+
MatchExplanation,
|
|
25
|
+
MatchOptions,
|
|
26
|
+
MatchResult as IMatchResult,
|
|
27
|
+
PatternOptions
|
|
28
|
+
} from './types';
|
|
19
29
|
import {CAPTURE_CAPTURING_SYMBOL, CAPTURE_NAME_SYMBOL, CaptureImpl, RAW_CODE_SYMBOL, RawCode} from './capture';
|
|
20
30
|
import {DebugPatternMatchingComparator, MatcherCallbacks, MatcherState, PatternMatchingComparator} from './comparator';
|
|
21
31
|
import {CaptureMarker, CaptureStorageValue, generateCacheKey, globalAstCache, WRAPPERS_MAP_SYMBOL} from './utils';
|
|
@@ -190,7 +200,7 @@ export class Pattern {
|
|
|
190
200
|
* })
|
|
191
201
|
*/
|
|
192
202
|
configure(options: PatternOptions): Pattern {
|
|
193
|
-
this._options = {...this._options, ...options};
|
|
203
|
+
this._options = { ...this._options, ...options };
|
|
194
204
|
// Invalidate cache when configuration changes
|
|
195
205
|
this._cachedAstPattern = undefined;
|
|
196
206
|
return this;
|
|
@@ -252,23 +262,22 @@ export class Pattern {
|
|
|
252
262
|
/**
|
|
253
263
|
* Creates a matcher for this pattern against a specific AST node.
|
|
254
264
|
*
|
|
255
|
-
* @param
|
|
256
|
-
* @param cursor
|
|
257
|
-
* capture constraints to navigate to parent nodes.
|
|
258
|
-
* created at the ast root, allowing constraints to navigate within the matched subtree.
|
|
265
|
+
* @param tree The AST node to match against
|
|
266
|
+
* @param cursor Cursor at the node's position in a larger tree. Used for context-aware
|
|
267
|
+
* capture constraints to navigate to parent nodes.
|
|
259
268
|
* @param options Optional match options (e.g., debug flag)
|
|
260
269
|
* @returns A MatchResult if the pattern matches, undefined otherwise
|
|
261
270
|
*
|
|
262
271
|
* @example
|
|
263
272
|
* ```typescript
|
|
264
273
|
* // Normal match
|
|
265
|
-
* const match = await pattern.match(node);
|
|
274
|
+
* const match = await pattern.match(node, cursor);
|
|
266
275
|
*
|
|
267
276
|
* // Debug this specific call
|
|
268
277
|
* const match = await pattern.match(node, cursor, { debug: true });
|
|
269
278
|
* ```
|
|
270
279
|
*/
|
|
271
|
-
async match(
|
|
280
|
+
async match(tree: J, cursor: Cursor, options?: MatchOptions): Promise<MatchResult | undefined> {
|
|
272
281
|
// Three-level precedence: call > pattern > global
|
|
273
282
|
const debugEnabled =
|
|
274
283
|
options?.debug !== undefined
|
|
@@ -279,8 +288,8 @@ export class Pattern {
|
|
|
279
288
|
|
|
280
289
|
if (debugEnabled) {
|
|
281
290
|
// Use matchWithExplanation and log the result
|
|
282
|
-
const result = await this.matchWithExplanation(
|
|
283
|
-
await this.logMatchResult(
|
|
291
|
+
const result = await this.matchWithExplanation(tree, cursor);
|
|
292
|
+
await this.logMatchResult(tree, cursor, result);
|
|
284
293
|
|
|
285
294
|
if (result.matched) {
|
|
286
295
|
// result.result is the MatchResult class instance
|
|
@@ -291,7 +300,7 @@ export class Pattern {
|
|
|
291
300
|
}
|
|
292
301
|
|
|
293
302
|
// Fast path - no debug
|
|
294
|
-
const matcher = new Matcher(this,
|
|
303
|
+
const matcher = new Matcher(this, tree, cursor);
|
|
295
304
|
const success = await matcher.matches();
|
|
296
305
|
if (!success) {
|
|
297
306
|
return undefined;
|
|
@@ -305,10 +314,10 @@ export class Pattern {
|
|
|
305
314
|
* Formats and logs the match result to stderr.
|
|
306
315
|
* @private
|
|
307
316
|
*/
|
|
308
|
-
private async logMatchResult(
|
|
317
|
+
private async logMatchResult(tree: J, cursor: Cursor | undefined, result: MatchAttemptResult): Promise<void> {
|
|
309
318
|
const patternSource = this.getPatternSource();
|
|
310
319
|
const patternId = `Pattern #${this.patternId}`;
|
|
311
|
-
const nodeKind = (
|
|
320
|
+
const nodeKind = (tree as any).kind || 'unknown';
|
|
312
321
|
// Format kind: extract short name (e.g., "org.openrewrite.java.tree.J$Binary" -> "J$Binary")
|
|
313
322
|
const shortKind = typeof nodeKind === 'string'
|
|
314
323
|
? nodeKind.split('.').pop() || nodeKind
|
|
@@ -324,7 +333,7 @@ export class Pattern {
|
|
|
324
333
|
let treeStr: string;
|
|
325
334
|
try {
|
|
326
335
|
const printer = TreePrinters.printer(JS.Kind.CompilationUnit);
|
|
327
|
-
treeStr = await printer.print(
|
|
336
|
+
treeStr = await printer.print(tree);
|
|
328
337
|
} catch (e) {
|
|
329
338
|
treeStr = '(tree printing unavailable)';
|
|
330
339
|
}
|
|
@@ -508,15 +517,15 @@ export class Pattern {
|
|
|
508
517
|
* - Explanation of failure (if not matched)
|
|
509
518
|
* - Debug log entries showing the matching process
|
|
510
519
|
*
|
|
511
|
-
* @param
|
|
512
|
-
* @param cursor
|
|
520
|
+
* @param tree The AST node to match against
|
|
521
|
+
* @param cursor Cursor at the node's position in a larger tree
|
|
513
522
|
* @param debugOptions Optional debug options (defaults to all logging enabled)
|
|
514
523
|
* @returns Detailed result with debug information
|
|
515
524
|
*
|
|
516
525
|
* @example
|
|
517
526
|
* const x = capture('x');
|
|
518
527
|
* const pat = pattern`console.log(${x})`;
|
|
519
|
-
* const attempt = await pat.matchWithExplanation(node);
|
|
528
|
+
* const attempt = await pat.matchWithExplanation(node, cursor);
|
|
520
529
|
* if (attempt.matched) {
|
|
521
530
|
* console.log('Matched!');
|
|
522
531
|
* console.log('Captured x:', attempt.result.get('x'));
|
|
@@ -526,8 +535,8 @@ export class Pattern {
|
|
|
526
535
|
* }
|
|
527
536
|
*/
|
|
528
537
|
async matchWithExplanation(
|
|
529
|
-
|
|
530
|
-
cursor
|
|
538
|
+
tree: J,
|
|
539
|
+
cursor: Cursor,
|
|
531
540
|
debugOptions?: DebugOptions
|
|
532
541
|
): Promise<MatchAttemptResult> {
|
|
533
542
|
// Default to full debug logging if not specified
|
|
@@ -538,7 +547,7 @@ export class Pattern {
|
|
|
538
547
|
...debugOptions
|
|
539
548
|
};
|
|
540
549
|
|
|
541
|
-
const matcher = new Matcher(this,
|
|
550
|
+
const matcher = new Matcher(this, tree, cursor, options);
|
|
542
551
|
const success = await matcher.matches();
|
|
543
552
|
|
|
544
553
|
if (success) {
|
|
@@ -671,17 +680,16 @@ class Matcher {
|
|
|
671
680
|
*
|
|
672
681
|
* @param pattern The pattern to match
|
|
673
682
|
* @param ast The AST node to match against
|
|
674
|
-
* @param cursor
|
|
683
|
+
* @param cursor Cursor at the AST node's position
|
|
675
684
|
* @param debugOptions Optional debug options for instrumentation
|
|
676
685
|
*/
|
|
677
686
|
constructor(
|
|
678
687
|
private readonly pattern: Pattern,
|
|
679
688
|
private readonly ast: J,
|
|
680
|
-
cursor
|
|
689
|
+
cursor: Cursor,
|
|
681
690
|
debugOptions?: DebugOptions
|
|
682
691
|
) {
|
|
683
|
-
|
|
684
|
-
this.cursor = cursor ?? new Cursor(ast, undefined);
|
|
692
|
+
this.cursor = cursor;
|
|
685
693
|
this.debugOptions = debugOptions ?? {};
|
|
686
694
|
}
|
|
687
695
|
|
|
@@ -57,10 +57,10 @@ class RewriteRuleImpl implements RewriteRule {
|
|
|
57
57
|
if (typeof this.after === 'function') {
|
|
58
58
|
// Call the function to get a template, then apply it
|
|
59
59
|
const template = this.after(match);
|
|
60
|
-
result = await template.apply(
|
|
60
|
+
result = await template.apply(node, cursor, { values: match });
|
|
61
61
|
} else {
|
|
62
62
|
// Use template.apply() as before
|
|
63
|
-
result = await this.after.apply(
|
|
63
|
+
result = await this.after.apply(node, cursor, { values: match });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (result) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import {Cursor, Tree} from '../..';
|
|
17
17
|
import {J} from '../../java';
|
|
18
|
-
import {
|
|
18
|
+
import {ApplyOptions, Parameter, TemplateOptions, TemplateParameter} from './types';
|
|
19
19
|
import {MatchResult} from './pattern';
|
|
20
20
|
import {generateCacheKey, globalAstCache, WRAPPERS_MAP_SYMBOL} from './utils';
|
|
21
21
|
import {CAPTURE_NAME_SYMBOL, RAW_CODE_SYMBOL} from './capture';
|
|
@@ -174,6 +174,18 @@ export class Template {
|
|
|
174
174
|
private options: TemplateOptions = {};
|
|
175
175
|
private _cachedTemplate?: J;
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Creates a new template.
|
|
179
|
+
*
|
|
180
|
+
* @param templateParts The string parts of the template
|
|
181
|
+
* @param parameters The parameters between the string parts
|
|
182
|
+
*/
|
|
183
|
+
constructor(
|
|
184
|
+
private readonly templateParts: TemplateStringsArray,
|
|
185
|
+
private readonly parameters: Parameter[]
|
|
186
|
+
) {
|
|
187
|
+
}
|
|
188
|
+
|
|
177
189
|
/**
|
|
178
190
|
* Creates a new builder for constructing templates programmatically.
|
|
179
191
|
*
|
|
@@ -191,18 +203,6 @@ export class Template {
|
|
|
191
203
|
return new TemplateBuilder();
|
|
192
204
|
}
|
|
193
205
|
|
|
194
|
-
/**
|
|
195
|
-
* Creates a new template.
|
|
196
|
-
*
|
|
197
|
-
* @param templateParts The string parts of the template
|
|
198
|
-
* @param parameters The parameters between the string parts
|
|
199
|
-
*/
|
|
200
|
-
constructor(
|
|
201
|
-
private readonly templateParts: TemplateStringsArray,
|
|
202
|
-
private readonly parameters: Parameter[]
|
|
203
|
-
) {
|
|
204
|
-
}
|
|
205
|
-
|
|
206
206
|
/**
|
|
207
207
|
* Configures this template with additional options.
|
|
208
208
|
*
|
|
@@ -217,7 +217,7 @@ export class Template {
|
|
|
217
217
|
* })
|
|
218
218
|
*/
|
|
219
219
|
configure(options: TemplateOptions): Template {
|
|
220
|
-
this.options = {
|
|
220
|
+
this.options = {...this.options, ...options};
|
|
221
221
|
// Invalidate cache when configuration changes
|
|
222
222
|
this._cachedTemplate = undefined;
|
|
223
223
|
return this;
|
|
@@ -236,7 +236,7 @@ export class Template {
|
|
|
236
236
|
* @returns The cached or newly computed template tree
|
|
237
237
|
* @internal
|
|
238
238
|
*/
|
|
239
|
-
async getTemplateTree(): Promise<JS.CompilationUnit> {
|
|
239
|
+
private async getTemplateTree(): Promise<JS.CompilationUnit> {
|
|
240
240
|
// Level 1: Instance cache (fastest path)
|
|
241
241
|
if (this._cachedTemplate) {
|
|
242
242
|
return this._cachedTemplate as JS.CompilationUnit;
|
|
@@ -286,12 +286,30 @@ export class Template {
|
|
|
286
286
|
/**
|
|
287
287
|
* Applies this template and returns the resulting tree.
|
|
288
288
|
*
|
|
289
|
+
* @param tree Input tree to transform
|
|
289
290
|
* @param cursor The cursor pointing to the current location in the AST
|
|
290
|
-
* @param
|
|
291
|
-
* @param values values for parameters in template
|
|
291
|
+
* @param options Optional configuration including values for parameters
|
|
292
292
|
* @returns A Promise resolving to the generated AST node
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* // Simple application without values
|
|
297
|
+
* const result = await tmpl.apply(node, cursor);
|
|
298
|
+
*
|
|
299
|
+
* // With values from pattern match
|
|
300
|
+
* const match = await pat.match(node, cursor);
|
|
301
|
+
* const result = await tmpl.apply(node, cursor, { values: match });
|
|
302
|
+
*
|
|
303
|
+
* // With explicit values
|
|
304
|
+
* const result = await tmpl.apply(node, cursor, {
|
|
305
|
+
* values: { x: someNode, y: anotherNode }
|
|
306
|
+
* });
|
|
307
|
+
* ```
|
|
293
308
|
*/
|
|
294
|
-
async apply(
|
|
309
|
+
async apply(tree: J, cursor: Cursor, options?: ApplyOptions): Promise<J | undefined> {
|
|
310
|
+
// Extract values from options
|
|
311
|
+
const values = options?.values;
|
|
312
|
+
|
|
295
313
|
// Normalize the values map: convert any Capture keys to string keys
|
|
296
314
|
let normalizedValues: Pick<Map<string, J>, 'get'> | undefined;
|
|
297
315
|
let wrappersMap: Map<string, J.RightPadded<J> | J.RightPadded<J>[]> = new Map();
|