@telorun/ide-support 0.13.2 → 0.14.0
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/cel-chain.d.ts +36 -0
- package/dist/cel-chain.d.ts.map +1 -0
- package/dist/cel-chain.js +77 -0
- package/dist/completions/detect-context.d.ts +5 -5
- package/dist/completions/detect-context.d.ts.map +1 -1
- package/dist/completions/detect-context.js +72 -5
- package/dist/completions/prop-keys.d.ts +1 -1
- package/dist/completions/prop-keys.d.ts.map +1 -1
- package/dist/completions/prop-keys.js +18 -1
- package/dist/definition/resolve-cel-target.d.ts.map +1 -1
- package/dist/definition/resolve-cel-target.js +1 -60
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/rename/build-rename.d.ts +39 -0
- package/dist/rename/build-rename.d.ts.map +1 -0
- package/dist/rename/build-rename.js +407 -0
- package/dist/rename/find-sites.d.ts +49 -0
- package/dist/rename/find-sites.d.ts.map +1 -0
- package/dist/rename/find-sites.js +202 -0
- package/dist/rename/index.d.ts +3 -0
- package/dist/rename/index.d.ts.map +1 -0
- package/dist/rename/index.js +1 -0
- package/dist/rename/types.d.ts +55 -0
- package/dist/rename/types.d.ts.map +1 -0
- package/dist/rename/types.js +1 -0
- package/package.json +2 -2
- package/src/cel-chain.ts +86 -0
- package/src/completions/detect-context.ts +73 -6
- package/src/completions/prop-keys.ts +23 -2
- package/src/definition/resolve-cel-target.ts +1 -68
- package/src/index.ts +1 -0
- package/src/rename/build-rename.ts +513 -0
- package/src/rename/find-sites.ts +215 -0
- package/src/rename/index.ts +9 -0
- package/src/rename/types.ts +51 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CelNode } from "@telorun/analyzer";
|
|
2
|
+
/** One identifier of a dotted CEL chain, with the span a cursor hit-tests
|
|
3
|
+
* against and a rename replaces. */
|
|
4
|
+
export interface ChainPart {
|
|
5
|
+
name: string;
|
|
6
|
+
range: [number, number];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
|
|
10
|
+
* is rooted in something other than a plain identifier (a call, an index), so a
|
|
11
|
+
* navigable — or renameable — prefix is never invented out of a computed
|
|
12
|
+
* expression.
|
|
13
|
+
*/
|
|
14
|
+
export declare function flattenChain(node: CelNode): ChainPart[] | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* A node's children.
|
|
17
|
+
*
|
|
18
|
+
* Exhaustive by construction: a new `CelNode` variant fails the build here
|
|
19
|
+
* rather than silently going unwalked, so the analyzer's node model and the
|
|
20
|
+
* walks over it cannot drift apart unnoticed. That matters twice over — for
|
|
21
|
+
* go-to-definition an unwalked variant is a missed jump, for a rename it is a
|
|
22
|
+
* reference left pointing at the old name.
|
|
23
|
+
*/
|
|
24
|
+
export declare function celChildren(node: CelNode): CelNode[];
|
|
25
|
+
/** Every node of the tree, outermost first. */
|
|
26
|
+
export declare function walkCel(node: CelNode, visit: (node: CelNode) => void): void;
|
|
27
|
+
/**
|
|
28
|
+
* The dotted chain under `offset`, and which of its identifiers was hit. The
|
|
29
|
+
* walk is outermost-first so the longest chain wins — `resources.store.conn`
|
|
30
|
+
* resolves as one chain rather than as its `resources.store` prefix.
|
|
31
|
+
*/
|
|
32
|
+
export declare function chainAt(node: CelNode, offset: number): {
|
|
33
|
+
parts: ChainPart[];
|
|
34
|
+
index: number;
|
|
35
|
+
} | undefined;
|
|
36
|
+
//# sourceMappingURL=cel-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cel-chain.d.ts","sourceRoot":"","sources":["../src/cel-chain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;qCACqC;AACrC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,SAAS,CAKnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,CA0BpD;AAED,+CAA+C;AAC/C,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAG3E;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAYnD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
|
|
3
|
+
* is rooted in something other than a plain identifier (a call, an index), so a
|
|
4
|
+
* navigable — or renameable — prefix is never invented out of a computed
|
|
5
|
+
* expression.
|
|
6
|
+
*/
|
|
7
|
+
export function flattenChain(node) {
|
|
8
|
+
if (node.kind === "ident")
|
|
9
|
+
return [{ name: node.name, range: node.range }];
|
|
10
|
+
if (node.kind !== "member")
|
|
11
|
+
return undefined;
|
|
12
|
+
const head = flattenChain(node.target);
|
|
13
|
+
return head ? [...head, { name: node.property, range: node.propertyRange }] : undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A node's children.
|
|
17
|
+
*
|
|
18
|
+
* Exhaustive by construction: a new `CelNode` variant fails the build here
|
|
19
|
+
* rather than silently going unwalked, so the analyzer's node model and the
|
|
20
|
+
* walks over it cannot drift apart unnoticed. That matters twice over — for
|
|
21
|
+
* go-to-definition an unwalked variant is a missed jump, for a rename it is a
|
|
22
|
+
* reference left pointing at the old name.
|
|
23
|
+
*/
|
|
24
|
+
export function celChildren(node) {
|
|
25
|
+
switch (node.kind) {
|
|
26
|
+
case "literal":
|
|
27
|
+
case "ident":
|
|
28
|
+
return [];
|
|
29
|
+
case "member":
|
|
30
|
+
return [node.target];
|
|
31
|
+
case "index":
|
|
32
|
+
return [node.target, node.index];
|
|
33
|
+
case "call":
|
|
34
|
+
return node.args;
|
|
35
|
+
case "methodCall":
|
|
36
|
+
return [node.receiver, ...node.args];
|
|
37
|
+
case "list":
|
|
38
|
+
return node.items;
|
|
39
|
+
case "map":
|
|
40
|
+
return node.entries.flatMap((e) => [e.key, e.value]);
|
|
41
|
+
case "ternary":
|
|
42
|
+
return [node.cond, node.then, node.else];
|
|
43
|
+
case "unary":
|
|
44
|
+
return [node.operand];
|
|
45
|
+
case "binary":
|
|
46
|
+
return [node.left, node.right];
|
|
47
|
+
}
|
|
48
|
+
const unhandled = node;
|
|
49
|
+
throw new Error(`Unhandled CEL node: ${JSON.stringify(unhandled)}`);
|
|
50
|
+
}
|
|
51
|
+
/** Every node of the tree, outermost first. */
|
|
52
|
+
export function walkCel(node, visit) {
|
|
53
|
+
visit(node);
|
|
54
|
+
for (const child of celChildren(node))
|
|
55
|
+
walkCel(child, visit);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The dotted chain under `offset`, and which of its identifiers was hit. The
|
|
59
|
+
* walk is outermost-first so the longest chain wins — `resources.store.conn`
|
|
60
|
+
* resolves as one chain rather than as its `resources.store` prefix.
|
|
61
|
+
*/
|
|
62
|
+
export function chainAt(node, offset) {
|
|
63
|
+
if (offset < node.range[0] || offset > node.range[1])
|
|
64
|
+
return undefined;
|
|
65
|
+
const parts = flattenChain(node);
|
|
66
|
+
if (parts) {
|
|
67
|
+
const index = parts.findIndex((p) => offset >= p.range[0] && offset <= p.range[1]);
|
|
68
|
+
if (index >= 0)
|
|
69
|
+
return { parts, index };
|
|
70
|
+
}
|
|
71
|
+
for (const child of celChildren(node)) {
|
|
72
|
+
const hit = chainAt(child, offset);
|
|
73
|
+
if (hit)
|
|
74
|
+
return hit;
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
@@ -44,11 +44,11 @@ export type CompletionCtx = {
|
|
|
44
44
|
replaceRange: ReplaceRange;
|
|
45
45
|
};
|
|
46
46
|
/** Navigate a JSON Schema hierarchy following `path`, auto-descending into
|
|
47
|
-
* array items
|
|
48
|
-
* branches define `properties`, returns a
|
|
49
|
-
* is the union (first-wins on key collision)
|
|
50
|
-
* intersection — enough for propKeyCompletions to
|
|
51
|
-
* at this slot can legally carry. */
|
|
47
|
+
* array items, peeling `anyOf` / `oneOf` branches and following document-local
|
|
48
|
+
* `$ref`s. When multiple peeled branches define `properties`, returns a
|
|
49
|
+
* synthetic node whose `properties` is the union (first-wins on key collision)
|
|
50
|
+
* and whose `required` is the intersection — enough for propKeyCompletions to
|
|
51
|
+
* surface every key a value at this slot can legally carry. */
|
|
52
52
|
export declare function navigateSchema(schema: Record<string, any>, path: string[]): Record<string, any> | undefined;
|
|
53
53
|
/** Walks up and down from `cursorLine` looking for a sibling line at the
|
|
54
54
|
* exact same indent whose key is `kind`. The value of the first such line
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;+BAE2B;IAC3B,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACpF;IACE;;;gDAG4C;IAC5C,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;6DACyD;IACzD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;
|
|
1
|
+
{"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,MAAM,CAAC;IACb;;;uEAGmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;+BAE2B;IAC3B,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GACpF;IACE;;;gDAG4C;IAC5C,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;6DACyD;IACzD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,YAAY,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,YAAY,EAAE,YAAY,CAAC;CAC5B,CAAC;AAyEN;;;;;gEAKgE;AAChE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,EAAE,GACb,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6CjC;AAwCD;;;;;wDAKwD;AACxD;;;;;;;sBAOsB;AACtB,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACrC,QAAQ,EAAE,MAAM,EAAE,GACjB,MAAM,EAAE,CAIV;AAED;;;;oBAIoB;AACpB,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,aAAa,GAAG,SAAS,CAsE3B"}
|
|
@@ -24,15 +24,66 @@ function peelCombinators(node) {
|
|
|
24
24
|
visit(node);
|
|
25
25
|
return out;
|
|
26
26
|
}
|
|
27
|
+
/** One JSON Pointer segment: RFC 6901 escapes, then percent-decoding, which a
|
|
28
|
+
* pointer carried in a URI fragment is subject to. A malformed escape is
|
|
29
|
+
* returned raw rather than thrown — `decodeURIComponent` raises `URIError`, and
|
|
30
|
+
* a stray `%` in someone's `$defs` key must not take completion down. */
|
|
31
|
+
function decodePointerSegment(segment) {
|
|
32
|
+
const unescaped = segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
33
|
+
try {
|
|
34
|
+
return decodeURIComponent(unescaped);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return unescaped;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Follow a document-local `$ref` (`#/$defs/<Name>`) against the schema root.
|
|
42
|
+
*
|
|
43
|
+
* The one reference form that occurs inside a kind schema, and the one the
|
|
44
|
+
* editor's resolver accepts — a schema-valued slot points at the hoisted
|
|
45
|
+
* `JsonSchema7` / `KindSchema` fragment this way, and the fragment points at
|
|
46
|
+
* itself to describe a nested schema. Without the hop, completion stopped dead
|
|
47
|
+
* at the first key of every `schema:` / `status:` block.
|
|
48
|
+
*
|
|
49
|
+
* A chain ends at a REPEATED pointer, which is what bounds the walk: a
|
|
50
|
+
* self-referential fragment is the normal case here, so a cycle must degrade to
|
|
51
|
+
* "no completion" rather than hang the editor.
|
|
52
|
+
*/
|
|
53
|
+
function resolveLocalRef(node, root) {
|
|
54
|
+
let current = node;
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
while (current && typeof current.$ref === "string" && current.$ref.startsWith("#/")) {
|
|
57
|
+
const pointer = current.$ref;
|
|
58
|
+
if (seen.has(pointer))
|
|
59
|
+
return undefined;
|
|
60
|
+
seen.add(pointer);
|
|
61
|
+
let target = root;
|
|
62
|
+
for (const segment of pointer.slice(2).split("/")) {
|
|
63
|
+
target = target?.[decodePointerSegment(segment)];
|
|
64
|
+
}
|
|
65
|
+
if (!target || typeof target !== "object")
|
|
66
|
+
return undefined;
|
|
67
|
+
// Keep whatever the slot declared beside the `$ref` (its title, its
|
|
68
|
+
// `x-telo-fragment` stamp) — that is what tells a consumer WHICH shape it
|
|
69
|
+
// pointed at, and draft-07 drops it at the validation layer only.
|
|
70
|
+
const { $ref: _, ...siblings } = current;
|
|
71
|
+
current = { ...target, ...siblings };
|
|
72
|
+
}
|
|
73
|
+
return current;
|
|
74
|
+
}
|
|
27
75
|
/** Navigate a JSON Schema hierarchy following `path`, auto-descending into
|
|
28
|
-
* array items
|
|
29
|
-
* branches define `properties`, returns a
|
|
30
|
-
* is the union (first-wins on key collision)
|
|
31
|
-
* intersection — enough for propKeyCompletions to
|
|
32
|
-
* at this slot can legally carry. */
|
|
76
|
+
* array items, peeling `anyOf` / `oneOf` branches and following document-local
|
|
77
|
+
* `$ref`s. When multiple peeled branches define `properties`, returns a
|
|
78
|
+
* synthetic node whose `properties` is the union (first-wins on key collision)
|
|
79
|
+
* and whose `required` is the intersection — enough for propKeyCompletions to
|
|
80
|
+
* surface every key a value at this slot can legally carry. */
|
|
33
81
|
export function navigateSchema(schema, path) {
|
|
34
82
|
let current = schema;
|
|
35
83
|
for (const segment of path) {
|
|
84
|
+
current = resolveLocalRef(current, schema);
|
|
85
|
+
if (!current)
|
|
86
|
+
return undefined;
|
|
36
87
|
const candidates = peelCombinators(current).flatMap((node) => {
|
|
37
88
|
const expanded = [];
|
|
38
89
|
let cur = node;
|
|
@@ -50,10 +101,26 @@ export function navigateSchema(schema, path) {
|
|
|
50
101
|
break;
|
|
51
102
|
}
|
|
52
103
|
}
|
|
104
|
+
// A map-valued node (a schema's `properties:`, a kind's name-keyed field)
|
|
105
|
+
// names its entries nowhere, so every segment lands on
|
|
106
|
+
// `additionalProperties`. Without this the walk stopped one level into a
|
|
107
|
+
// `schema:` block — at exactly the field the author is writing.
|
|
108
|
+
if (!next) {
|
|
109
|
+
for (const cand of candidates) {
|
|
110
|
+
const additional = cand.additionalProperties;
|
|
111
|
+
if (additional && typeof additional === "object") {
|
|
112
|
+
next = additional;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
53
117
|
if (!next)
|
|
54
118
|
return undefined;
|
|
55
119
|
current = next;
|
|
56
120
|
}
|
|
121
|
+
current = resolveLocalRef(current, schema);
|
|
122
|
+
if (!current)
|
|
123
|
+
return undefined;
|
|
57
124
|
// Auto-descend through a trailing array at the leaf (e.g. cursor inside `mounts:` items)
|
|
58
125
|
while (current.type === "array" && current.items) {
|
|
59
126
|
current = current.items;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnalysisRegistry } from "@telorun/analyzer";
|
|
2
2
|
import type { CompletionResult } from "../types.js";
|
|
3
3
|
export declare function propKeyCompletions(kind: string, yamlPath: string[], existingKeys: Set<string>, registry: AnalysisRegistry | undefined): CompletionResult[];
|
|
4
4
|
//# sourceMappingURL=prop-keys.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prop-keys.d.ts","sourceRoot":"","sources":["../../src/completions/prop-keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"prop-keys.d.ts","sourceRoot":"","sources":["../../src/completions/prop-keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiBpD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,EAClB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,GACrC,gBAAgB,EAAE,CAkCpB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { manifestFragmentOf, TELO_SCHEMA_ANNOTATIONS, } from "@telorun/analyzer";
|
|
1
2
|
import { navigateSchema } from "./detect-context.js";
|
|
2
3
|
/** Kernel-implicit fields. Every Telo resource declares its `kind` and a
|
|
3
4
|
* `metadata` object; the analyzer's schema validator injects them when
|
|
@@ -37,9 +38,25 @@ export function propKeyCompletions(kind, yamlPath, existingKeys, registry) {
|
|
|
37
38
|
const required = new Set(Array.isArray(targetSchema.required) ? targetSchema.required : []);
|
|
38
39
|
const properties = yamlPath.length === 0
|
|
39
40
|
? { ...ROOT_IMPLICIT_PROPS, ...targetSchema.properties }
|
|
40
|
-
: targetSchema.properties;
|
|
41
|
+
: { ...targetSchema.properties, ...annotationKeys(targetSchema) };
|
|
41
42
|
return buildItems(properties, existingKeys, required);
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The `x-telo-*` vocabulary, offered only inside a KIND's own schema.
|
|
46
|
+
*
|
|
47
|
+
* Read off the fragment stamp rather than the position: a kind schema nests, so
|
|
48
|
+
* "am I inside one" is a fact about the node, not about how deep the cursor
|
|
49
|
+
* sits. A plain data schema — an `inputType:`, a `status:` block — stamps
|
|
50
|
+
* `JsonSchema7` and gets nothing, because an annotation there configures a slot
|
|
51
|
+
* that does not exist.
|
|
52
|
+
*
|
|
53
|
+
* The vocabulary lives on the analyzer side and never enters a manifest: a
|
|
54
|
+
* `properties` map holding a literal `x-telo-ref` key reads to the annotation
|
|
55
|
+
* walkers as an annotated node.
|
|
56
|
+
*/
|
|
57
|
+
function annotationKeys(node) {
|
|
58
|
+
return manifestFragmentOf(node) === "KindSchema" ? TELO_SCHEMA_ANNOTATIONS : {};
|
|
59
|
+
}
|
|
43
60
|
function buildItems(properties, existingKeys, required) {
|
|
44
61
|
const items = [];
|
|
45
62
|
for (const [prop, propSchema] of Object.entries(properties)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-cel-target.d.ts","sourceRoot":"","sources":["../../src/definition/resolve-cel-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"resolve-cel-target.d.ts","sourceRoot":"","sources":["../../src/definition/resolve-cel-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAqCpD;;;;;;;;;;;;oBAYoB;AACpB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,YAAY,EAC3B,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,SAAS,CAqB9B"}
|
|
@@ -1,69 +1,10 @@
|
|
|
1
1
|
import { CelParseError, } from "@telorun/analyzer";
|
|
2
2
|
import { locateImport, locateModuleDocKey, locateResource, moduleFiles, } from "./manifest-navigation.js";
|
|
3
3
|
import { resolveExportedResource } from "./resolve-export-chain.js";
|
|
4
|
+
import { chainAt } from "../cel-chain.js";
|
|
4
5
|
/** Root CEL scopes whose members are declared as a block on the module doc, so
|
|
5
6
|
* `variables.port` navigates to `variables:` and then to its `port:` entry. */
|
|
6
7
|
const DECLARATION_SCOPES = new Set(["variables", "secrets", "ports"]);
|
|
7
|
-
/** Flatten `a.b.c` into its identifiers. Returns undefined as soon as the chain
|
|
8
|
-
* is rooted in something other than a plain identifier (a call, an index), so a
|
|
9
|
-
* navigable prefix is never invented out of a computed expression. */
|
|
10
|
-
function flattenChain(node) {
|
|
11
|
-
if (node.kind === "ident")
|
|
12
|
-
return [{ name: node.name, range: node.range }];
|
|
13
|
-
if (node.kind !== "member")
|
|
14
|
-
return undefined;
|
|
15
|
-
const head = flattenChain(node.target);
|
|
16
|
-
return head ? [...head, { name: node.property, range: node.propertyRange }] : undefined;
|
|
17
|
-
}
|
|
18
|
-
/** Exhaustive by construction: a new `CelNode` variant fails the build here
|
|
19
|
-
* rather than silently going unwalked, so the analyzer's node model and this
|
|
20
|
-
* walk cannot drift apart unnoticed. */
|
|
21
|
-
function celChildren(node) {
|
|
22
|
-
switch (node.kind) {
|
|
23
|
-
case "literal":
|
|
24
|
-
case "ident":
|
|
25
|
-
return [];
|
|
26
|
-
case "member":
|
|
27
|
-
return [node.target];
|
|
28
|
-
case "index":
|
|
29
|
-
return [node.target, node.index];
|
|
30
|
-
case "call":
|
|
31
|
-
return node.args;
|
|
32
|
-
case "methodCall":
|
|
33
|
-
return [node.receiver, ...node.args];
|
|
34
|
-
case "list":
|
|
35
|
-
return node.items;
|
|
36
|
-
case "map":
|
|
37
|
-
return node.entries.flatMap((e) => [e.key, e.value]);
|
|
38
|
-
case "ternary":
|
|
39
|
-
return [node.cond, node.then, node.else];
|
|
40
|
-
case "unary":
|
|
41
|
-
return [node.operand];
|
|
42
|
-
case "binary":
|
|
43
|
-
return [node.left, node.right];
|
|
44
|
-
}
|
|
45
|
-
const unhandled = node;
|
|
46
|
-
throw new Error(`Unhandled CEL node: ${JSON.stringify(unhandled)}`);
|
|
47
|
-
}
|
|
48
|
-
/** The dotted chain under `offset`, and which of its identifiers was hit. The
|
|
49
|
-
* walk is outermost-first so the longest chain wins — `resources.Store.conn`
|
|
50
|
-
* resolves as one chain rather than as its `resources.Store` prefix. */
|
|
51
|
-
function chainAt(node, offset) {
|
|
52
|
-
if (offset < node.range[0] || offset > node.range[1])
|
|
53
|
-
return undefined;
|
|
54
|
-
const parts = flattenChain(node);
|
|
55
|
-
if (parts) {
|
|
56
|
-
const index = parts.findIndex((p) => offset >= p.range[0] && offset <= p.range[1]);
|
|
57
|
-
if (index >= 0)
|
|
58
|
-
return { parts, index };
|
|
59
|
-
}
|
|
60
|
-
for (const child of celChildren(node)) {
|
|
61
|
-
const hit = chainAt(child, offset);
|
|
62
|
-
if (hit)
|
|
63
|
-
return hit;
|
|
64
|
-
}
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
8
|
/** `resources.<name>` is a local instance; `resources.<Alias>.<name>` is an
|
|
68
9
|
* imported module's exported one. A local name wins over an import alias, so a
|
|
69
10
|
* deeper access on a local instance (`resources.db.url`) reads as a field of
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export * from "./diagnostics/index.js";
|
|
|
4
4
|
export * from "./hover/index.js";
|
|
5
5
|
export * from "./semantic-tokens/index.js";
|
|
6
6
|
export * from "./definition/index.js";
|
|
7
|
+
export * from "./rename/index.js";
|
|
7
8
|
export * from "./import-upgrades/index.js";
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type AstDocument, type LoadedGraph } from "@telorun/analyzer";
|
|
2
|
+
import type { RenamePreparation, RenameResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rename a name and every reference to it.
|
|
5
|
+
*
|
|
6
|
+
* **This is a refactor, not a fix**, and the distinction is the reason it lives
|
|
7
|
+
* here rather than behind a `DiagnosticFix`. A fix is a whole-value replacement
|
|
8
|
+
* for ONE node, verified by the diagnostic that produced it; a rename is only
|
|
9
|
+
* correct when every reference moves with it, which means the operation's unit
|
|
10
|
+
* is the reference graph, not the node. Renaming `metadata.name` alone leaves
|
|
11
|
+
* every `!ref`, `resources.<name>` and `steps.<name>.result` pointing at a name
|
|
12
|
+
* that no longer exists — so a rename offered as a quick fix would break the
|
|
13
|
+
* file it claimed to repair.
|
|
14
|
+
*
|
|
15
|
+
* **Three renameable surfaces, and every other one is an explicit refusal.**
|
|
16
|
+
* What they have in common is that their reference set is *enumerable from this
|
|
17
|
+
* workspace*: a resource instance, a step, and a `variables:` / `secrets:` /
|
|
18
|
+
* `ports:` key are all module-local. A kind name, a module name and an import
|
|
19
|
+
* alias are not supported yet — their references reach schema annotations
|
|
20
|
+
* (`x-telo-ref`, `extends`, `exports.kinds`) as alias-qualified halves of a
|
|
21
|
+
* larger value, which is a materially bigger surface than a bare identifier and
|
|
22
|
+
* wants its own pass.
|
|
23
|
+
*
|
|
24
|
+
* **A name in `exports.resources` is refused outright**, and that is the load-
|
|
25
|
+
* bearing refusal. Such a name is the library's public ABI: a consumer writes
|
|
26
|
+
* `!ref Alias.name` and reads `resources.Alias.name`, in files this workspace
|
|
27
|
+
* may not contain and, for a published consumer, cannot. Renaming it is a
|
|
28
|
+
* breaking change to be *versioned*, not an edit to be applied — and a rename
|
|
29
|
+
* box that silently ships one would be the worst available framing.
|
|
30
|
+
*
|
|
31
|
+
* **Ambiguity is refused rather than guessed.** A name declared twice in reach
|
|
32
|
+
* (a `with:`-scoped resource shadowing a module-level one, two steps in one
|
|
33
|
+
* resource sharing a spelling) has references that resolve to different
|
|
34
|
+
* declarations, and no edit set is right for both.
|
|
35
|
+
*/
|
|
36
|
+
export declare function prepareRename(text: string, line: number, character: number, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[]): RenamePreparation;
|
|
37
|
+
/** Prepare, validate the new name, then collect every edit. */
|
|
38
|
+
export declare function buildRename(text: string, line: number, character: number, newName: string, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[]): RenameResult;
|
|
39
|
+
//# sourceMappingURL=build-rename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-rename.d.ts","sourceRoot":"","sources":["../../src/rename/build-rename.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,WAAW,EAGhB,KAAK,WAAW,EAGjB,MAAM,mBAAmB,CAAC;AAa3B,OAAO,KAAK,EAGV,iBAAiB,EACjB,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,iBAAiB,CAsBnB;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,YAAY,CA0Bd"}
|