@telorun/ide-support 0.20.1 → 0.21.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/cursor-chain.d.ts +4 -0
- package/dist/cel/cursor-chain.d.ts.map +1 -1
- package/dist/cel/cursor-chain.js +1 -1
- package/dist/cel/module-calls.d.ts +56 -0
- package/dist/cel/module-calls.d.ts.map +1 -0
- package/dist/cel/module-calls.js +98 -0
- package/dist/cel/tokens.d.ts.map +1 -1
- package/dist/cel/tokens.js +9 -1
- package/dist/completions/cel-completions.d.ts.map +1 -1
- package/dist/completions/cel-completions.js +15 -0
- package/dist/completions/valid-capabilities.d.ts +1 -1
- package/dist/completions/valid-capabilities.d.ts.map +1 -1
- package/dist/completions/valid-capabilities.js +4 -0
- package/dist/definition/build-definition.d.ts.map +1 -1
- package/dist/definition/build-definition.js +46 -2
- package/dist/diagnostics/normalize.d.ts.map +1 -1
- package/dist/diagnostics/normalize.js +9 -1
- package/dist/hover/build-hover.d.ts.map +1 -1
- package/dist/hover/build-hover.js +17 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/rename/build-rename.d.ts +5 -2
- package/dist/rename/build-rename.d.ts.map +1 -1
- package/dist/rename/build-rename.js +41 -14
- package/dist/rename/find-sites.d.ts +5 -0
- package/dist/rename/find-sites.d.ts.map +1 -1
- package/dist/rename/find-sites.js +28 -0
- package/dist/signature-help/build-signature-help.d.ts +15 -0
- package/dist/signature-help/build-signature-help.d.ts.map +1 -0
- package/dist/signature-help/build-signature-help.js +87 -0
- package/dist/signature-help/index.d.ts +2 -0
- package/dist/signature-help/index.d.ts.map +1 -0
- package/dist/signature-help/index.js +1 -0
- package/dist/types.d.ts +19 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/cel/cursor-chain.ts +1 -1
- package/src/cel/module-calls.ts +138 -0
- package/src/cel/tokens.ts +8 -1
- package/src/completions/cel-completions.ts +15 -0
- package/src/completions/valid-capabilities.ts +6 -0
- package/src/definition/build-definition.ts +49 -1
- package/src/diagnostics/normalize.ts +9 -1
- package/src/hover/build-hover.ts +16 -2
- package/src/index.ts +4 -0
- package/src/rename/build-rename.ts +55 -10
- package/src/rename/find-sites.ts +30 -0
- package/src/signature-help/build-signature-help.ts +100 -0
- package/src/signature-help/index.ts +1 -0
- package/src/types.ts +17 -2
|
@@ -19,6 +19,10 @@ export interface CelCursorChain {
|
|
|
19
19
|
* prefix's members are offered and functions are not. */
|
|
20
20
|
member: boolean;
|
|
21
21
|
}
|
|
22
|
+
/** Where `segment.source` starts in document offsets. The segment range spans
|
|
23
|
+
* the delimiters (`${{ … }}`) and any trimmed whitespace, so the body has to be
|
|
24
|
+
* located inside it rather than assumed to start at `range[0]`. */
|
|
25
|
+
export declare function bodyStart(text: string, segment: CelSegment): number;
|
|
22
26
|
/**
|
|
23
27
|
* Split the text before the cursor into a resolved prefix and a partial token.
|
|
24
28
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-chain.d.ts","sourceRoot":"","sources":["../../src/cel/cursor-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd;8DAC0D;IAC1D,MAAM,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"cursor-chain.d.ts","sourceRoot":"","sources":["../../src/cel/cursor-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,iGAAiG;IACjG,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd;8DAC0D;IAC1D,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;oEAEoE;AACpE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,MAAM,CAInE;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GACb,cAAc,GAAG,SAAS,CAY5B"}
|
package/dist/cel/cursor-chain.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Where `segment.source` starts in document offsets. The segment range spans
|
|
2
2
|
* the delimiters (`${{ … }}`) and any trimmed whitespace, so the body has to be
|
|
3
3
|
* located inside it rather than assumed to start at `range[0]`. */
|
|
4
|
-
function bodyStart(text, segment) {
|
|
4
|
+
export function bodyStart(text, segment) {
|
|
5
5
|
const span = text.slice(segment.range[0], segment.range[1]);
|
|
6
6
|
const at = span.indexOf(segment.source);
|
|
7
7
|
return at < 0 ? segment.range[0] : segment.range[0] + at;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Module calls inside a CEL body** — `Self.total(items)`, `Billing.isStale(x)`.
|
|
3
|
+
*
|
|
4
|
+
* In the tree an editor parses, a module call is a receiver-style call whose
|
|
5
|
+
* receiver is a plain identifier naming a module: `Self`, the module's own name
|
|
6
|
+
* or an import alias. Which identifiers those are is the scope's answer
|
|
7
|
+
* (`CelScope.moduleNames`), never a guess from the spelling, and what a call
|
|
8
|
+
* resolves to is the analyzer's (`CelScope.moduleFunction`), so completion,
|
|
9
|
+
* hover, signature help and go-to-declaration all name the function `telo check`
|
|
10
|
+
* binds the call to.
|
|
11
|
+
*/
|
|
12
|
+
import { type CallableFlags, type CelNode, type CelScope, type ResolvedFunction } from "@telorun/analyzer";
|
|
13
|
+
/** One module call written in a CEL body. */
|
|
14
|
+
export interface ModuleCallSite {
|
|
15
|
+
node: Extract<CelNode, {
|
|
16
|
+
kind: "methodCall";
|
|
17
|
+
}>;
|
|
18
|
+
receiver: string;
|
|
19
|
+
name: string;
|
|
20
|
+
/** `Receiver.name`, as the analyzer keys a call. */
|
|
21
|
+
qualified: string;
|
|
22
|
+
receiverRange: [number, number];
|
|
23
|
+
/** The function name alone, in document offsets. */
|
|
24
|
+
nameRange: [number, number];
|
|
25
|
+
}
|
|
26
|
+
/** Whether `node` is a module call: a method call whose receiver is a plain
|
|
27
|
+
* identifier `isModule` accepts. The one recognizer every editor feature reads,
|
|
28
|
+
* mirroring the templating rule — `a.Billing.format(x)` is an ordinary method
|
|
29
|
+
* call, since its receiver is a field access. */
|
|
30
|
+
export declare function isModuleCallNode(node: CelNode, isModule: (receiver: string) => boolean): node is Extract<CelNode, {
|
|
31
|
+
kind: "methodCall";
|
|
32
|
+
}> & {
|
|
33
|
+
receiver: Extract<CelNode, {
|
|
34
|
+
kind: "ident";
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
/** Every call in `ast` whose receiver `isModule` accepts. */
|
|
38
|
+
export declare function moduleCallSites(text: string, ast: CelNode, isModule: (receiver: string) => boolean): ModuleCallSite[];
|
|
39
|
+
/** The module call whose receiver or name the cursor is on — the innermost, when
|
|
40
|
+
* one call's argument holds another. */
|
|
41
|
+
export declare function moduleCallAt(text: string, ast: CelNode, offset: number, scope: CelScope): (ModuleCallSite & {
|
|
42
|
+
onReceiver: boolean;
|
|
43
|
+
}) | undefined;
|
|
44
|
+
/** A function's signature as a reader sees it, with each parameter's span inside
|
|
45
|
+
* the label — the shape signature help highlights the active parameter by. */
|
|
46
|
+
export declare function functionSignature(qualified: string, fn: ResolvedFunction): {
|
|
47
|
+
label: string;
|
|
48
|
+
parameters: Array<{
|
|
49
|
+
label: [number, number];
|
|
50
|
+
documentation?: string;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
/** What a reader is told a function promises: its description and derived
|
|
54
|
+
* determinism, naming the chain to the leaf that decided it. */
|
|
55
|
+
export declare function describeFunction(fn: ResolvedFunction, flags: CallableFlags | undefined): string;
|
|
56
|
+
//# sourceMappingURL=module-calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-calls.d.ts","sourceRoot":"","sources":["../../src/cel/module-calls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAEL,KAAK,aAAa,EAClB,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAI3B,6CAA6C;AAC7C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oDAAoD;IACpD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAYD;;;kDAGkD;AAClD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GACtC,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC,GAAG;IACpD,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/C,CAEA;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GACtC,cAAc,EAAE,CAgBlB;AAED;yCACyC;AACzC,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,GACd,CAAC,cAAc,GAAG;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,SAAS,CAQxD;AAED;+EAC+E;AAC/E,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,gBAAgB,GACnB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAe3F;AAED;iEACiE;AACjE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,CAiB/F"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **Module calls inside a CEL body** — `Self.total(items)`, `Billing.isStale(x)`.
|
|
3
|
+
*
|
|
4
|
+
* In the tree an editor parses, a module call is a receiver-style call whose
|
|
5
|
+
* receiver is a plain identifier naming a module: `Self`, the module's own name
|
|
6
|
+
* or an import alias. Which identifiers those are is the scope's answer
|
|
7
|
+
* (`CelScope.moduleNames`), never a guess from the spelling, and what a call
|
|
8
|
+
* resolves to is the analyzer's (`CelScope.moduleFunction`), so completion,
|
|
9
|
+
* hover, signature help and go-to-declaration all name the function `telo check`
|
|
10
|
+
* binds the call to.
|
|
11
|
+
*/
|
|
12
|
+
import { renderChain, } from "@telorun/analyzer";
|
|
13
|
+
import { walkCel } from "../cel-chain.js";
|
|
14
|
+
import { schemaTypeName } from "./symbols.js";
|
|
15
|
+
/** The span of a method's name after its receiver, read off the document text —
|
|
16
|
+
* the parsed node carries no span for the name itself. */
|
|
17
|
+
function nameRangeOf(text, node) {
|
|
18
|
+
const at = text.indexOf(node.name, node.receiver.range[1]);
|
|
19
|
+
return at >= 0 && at + node.name.length <= node.range[1] ? [at, at + node.name.length] : undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Whether `node` is a module call: a method call whose receiver is a plain
|
|
22
|
+
* identifier `isModule` accepts. The one recognizer every editor feature reads,
|
|
23
|
+
* mirroring the templating rule — `a.Billing.format(x)` is an ordinary method
|
|
24
|
+
* call, since its receiver is a field access. */
|
|
25
|
+
export function isModuleCallNode(node, isModule) {
|
|
26
|
+
return node.kind === "methodCall" && node.receiver.kind === "ident" && isModule(node.receiver.name);
|
|
27
|
+
}
|
|
28
|
+
/** Every call in `ast` whose receiver `isModule` accepts. */
|
|
29
|
+
export function moduleCallSites(text, ast, isModule) {
|
|
30
|
+
const out = [];
|
|
31
|
+
walkCel(ast, (node) => {
|
|
32
|
+
if (!isModuleCallNode(node, isModule))
|
|
33
|
+
return;
|
|
34
|
+
const nameRange = nameRangeOf(text, node);
|
|
35
|
+
if (!nameRange)
|
|
36
|
+
return;
|
|
37
|
+
out.push({
|
|
38
|
+
node,
|
|
39
|
+
receiver: node.receiver.name,
|
|
40
|
+
name: node.name,
|
|
41
|
+
qualified: `${node.receiver.name}.${node.name}`,
|
|
42
|
+
receiverRange: node.receiver.range,
|
|
43
|
+
nameRange,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** The module call whose receiver or name the cursor is on — the innermost, when
|
|
49
|
+
* one call's argument holds another. */
|
|
50
|
+
export function moduleCallAt(text, ast, offset, scope) {
|
|
51
|
+
const within = (range) => offset >= range[0] && offset <= range[1];
|
|
52
|
+
let hit;
|
|
53
|
+
for (const site of moduleCallSites(text, ast, (receiver) => scope.moduleNames.has(receiver))) {
|
|
54
|
+
if (within(site.nameRange))
|
|
55
|
+
hit = { ...site, onReceiver: false };
|
|
56
|
+
else if (within(site.receiverRange))
|
|
57
|
+
hit = { ...site, onReceiver: true };
|
|
58
|
+
}
|
|
59
|
+
return hit;
|
|
60
|
+
}
|
|
61
|
+
/** A function's signature as a reader sees it, with each parameter's span inside
|
|
62
|
+
* the label — the shape signature help highlights the active parameter by. */
|
|
63
|
+
export function functionSignature(qualified, fn) {
|
|
64
|
+
let label = `${qualified}(`;
|
|
65
|
+
const parameters = [];
|
|
66
|
+
fn.params.forEach((param, index) => {
|
|
67
|
+
if (index > 0)
|
|
68
|
+
label += ", ";
|
|
69
|
+
const text = `${param.name}${param.optional ? "?" : ""}: ${schemaTypeName(param.schema) ?? "dyn"}`;
|
|
70
|
+
const description = param.schema?.description;
|
|
71
|
+
parameters.push({
|
|
72
|
+
label: [label.length, label.length + text.length],
|
|
73
|
+
...(typeof description === "string" ? { documentation: description } : {}),
|
|
74
|
+
});
|
|
75
|
+
label += text;
|
|
76
|
+
});
|
|
77
|
+
label += `) → ${schemaTypeName(fn.returns) ?? fn.celType}`;
|
|
78
|
+
return { label, parameters };
|
|
79
|
+
}
|
|
80
|
+
/** What a reader is told a function promises: its description and derived
|
|
81
|
+
* determinism, naming the chain to the leaf that decided it. */
|
|
82
|
+
export function describeFunction(fn, flags) {
|
|
83
|
+
const lines = [];
|
|
84
|
+
const description = fn.manifest.metadata?.description;
|
|
85
|
+
if (typeof description === "string" && description)
|
|
86
|
+
lines.push(description);
|
|
87
|
+
if (flags) {
|
|
88
|
+
const facts = [];
|
|
89
|
+
facts.push(flags.deterministic
|
|
90
|
+
? "deterministic"
|
|
91
|
+
: `non-deterministic${flags.nondeterministicVia.length ? ` (${renderChain(flags.nondeterministicVia)})` : ""}`);
|
|
92
|
+
if (flags.hostBacked) {
|
|
93
|
+
facts.push(`host-backed${flags.hostBackedVia.length ? ` (${renderChain(flags.hostBackedVia)})` : ""}`);
|
|
94
|
+
}
|
|
95
|
+
lines.push(facts.join(" · "));
|
|
96
|
+
}
|
|
97
|
+
return lines.join("\n\n");
|
|
98
|
+
}
|
package/dist/cel/tokens.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/cel/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAA+B,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/cel/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAA+B,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKrD;wCACwC;AACxC,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAeD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,QAAQ,GAAG,SAAS,GAC1B,YAAY,EAAE,CA2HhB"}
|
package/dist/cel/tokens.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { CelParseError } from "@telorun/analyzer";
|
|
17
17
|
import { flattenChain } from "../cel-chain.js";
|
|
18
|
+
import { isModuleCallNode } from "./module-calls.js";
|
|
18
19
|
import { celRootSymbols, celSymbolAt } from "./symbols.js";
|
|
19
20
|
const LITERAL_TYPE = (value) => {
|
|
20
21
|
if (typeof value === "number" || typeof value === "bigint")
|
|
@@ -113,7 +114,14 @@ export function celSegmentTokens(text, segment, scope) {
|
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
115
116
|
case "methodCall": {
|
|
116
|
-
|
|
117
|
+
// A module call's receiver is a module — a namespace a function is called
|
|
118
|
+
// through — and never a value the scope could confirm as a root name.
|
|
119
|
+
if (scope && isModuleCallNode(node, (receiver) => scope.moduleNames.has(receiver))) {
|
|
120
|
+
out.push({ range: node.receiver.range, type: "namespace" });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
visit(node.receiver);
|
|
124
|
+
}
|
|
117
125
|
const span = spanOf(node.name, node.receiver.range[1], node.range[1]);
|
|
118
126
|
if (span)
|
|
119
127
|
out.push({ range: span, type: "function" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cel-completions.d.ts","sourceRoot":"","sources":["../../src/completions/cel-completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cel-completions.d.ts","sourceRoot":"","sources":["../../src/completions/cel-completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,aAAa,CAAC;AAKlE,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAqBD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,aAAa,GAAG,SAAS,GAC/B,gBAAgB,EAAE,CAmEpB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { celCursorChain } from "../cel/cursor-chain.js";
|
|
2
|
+
import { describeFunction, functionSignature } from "../cel/module-calls.js";
|
|
2
3
|
import { celFunctions, celMemberSymbols, celRootSymbols } from "../cel/symbols.js";
|
|
3
4
|
/** Markdown listing every overload, with the function's own description above
|
|
4
5
|
* it. A single-signature function needs no list — its one signature is already
|
|
@@ -36,6 +37,20 @@ export function celCompletions(text, segment, offset, concretePath, target, quer
|
|
|
36
37
|
const chain = celCursorChain(text, segment, offset);
|
|
37
38
|
const prefix = chain?.prefix ?? [];
|
|
38
39
|
if (chain?.member) {
|
|
40
|
+
// `<Module>.` offers the functions that module makes callable here — its own
|
|
41
|
+
// through `Self` or its name, an import's exported ones through the alias.
|
|
42
|
+
if (prefix.length === 1 && scope.moduleNames.has(prefix[0])) {
|
|
43
|
+
return scope.moduleFunctionsOf(prefix[0]).map(({ name, function: fn }) => {
|
|
44
|
+
const qualified = `${prefix[0]}.${name}`;
|
|
45
|
+
const about = describeFunction(fn, scope.moduleCallFlags(qualified));
|
|
46
|
+
return {
|
|
47
|
+
label: name,
|
|
48
|
+
kind: "value",
|
|
49
|
+
detail: functionSignature(qualified, fn).label,
|
|
50
|
+
...(about ? { documentation: about } : {}),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
39
54
|
return celMemberSymbols(scope, prefix).map((s) => toResult(s));
|
|
40
55
|
}
|
|
41
56
|
const roots = celRootSymbols(scope);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const CAPABILITY_VALUES: readonly ["Telo.Service", "Telo.Runnable", "Telo.Invocable", "Telo.Provider", "Telo.Mount", "Telo.Type"];
|
|
1
|
+
export declare const CAPABILITY_VALUES: readonly ["Telo.Service", "Telo.Runnable", "Telo.Invocable", "Telo.Provider", "Telo.Mount", "Telo.Sink", "Telo.Callable", "Telo.Type"];
|
|
2
2
|
/** One-line role summary per capability, surfaced on hover. Kept in sync with
|
|
3
3
|
* the capability list in `CLAUDE.md` / the kernel builtins. */
|
|
4
4
|
export declare const CAPABILITY_DOCS: Record<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valid-capabilities.d.ts","sourceRoot":"","sources":["../../src/completions/valid-capabilities.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"valid-capabilities.d.ts","sourceRoot":"","sources":["../../src/completions/valid-capabilities.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,wIASpB,CAAC;AAEX;gEACgE;AAChE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAWlD,CAAC"}
|
|
@@ -4,6 +4,8 @@ export const CAPABILITY_VALUES = [
|
|
|
4
4
|
"Telo.Invocable",
|
|
5
5
|
"Telo.Provider",
|
|
6
6
|
"Telo.Mount",
|
|
7
|
+
"Telo.Sink",
|
|
8
|
+
"Telo.Callable",
|
|
7
9
|
"Telo.Type",
|
|
8
10
|
];
|
|
9
11
|
/** One-line role summary per capability, surfaced on hover. Kept in sync with
|
|
@@ -14,5 +16,7 @@ export const CAPABILITY_DOCS = {
|
|
|
14
16
|
"Telo.Invocable": "Request handler: `invoke(inputs)` (scripts, endpoints).",
|
|
15
17
|
"Telo.Provider": "Value-flow source: `init()` + optional `provide()` (config, secrets).",
|
|
16
18
|
"Telo.Mount": "Mounted into a Service (HTTP APIs, middleware).",
|
|
19
|
+
"Telo.Sink": "Record-stream destination: `write(record)` + `flush()` / `flushSync()` / `close()`, written to directly rather than dispatched.",
|
|
20
|
+
"Telo.Callable": "Function: synchronous `call(args)` against a declared `params` / `returns` signature, called from CEL through a module name.",
|
|
17
21
|
"Telo.Type": "Pure schema definition — no runtime instance.",
|
|
18
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-definition.d.ts","sourceRoot":"","sources":["../../src/definition/build-definition.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"build-definition.d.ts","sourceRoot":"","sources":["../../src/definition/build-definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAGhB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAYpD;;;;;;;;;;;;;;;gBAegB;AAChB,wBAAgB,eAAe,CAC7B,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;AACpB;;qDAEqD;AACrD,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,gBAAgB,GAAG,SAAS,CAsC9B"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { parseToAst, } from "@telorun/analyzer";
|
|
1
|
+
import { CelParseError, parseToAst, } from "@telorun/analyzer";
|
|
2
|
+
import { moduleCallAt } from "../cel/module-calls.js";
|
|
2
3
|
import { resolveNodeAtPosition } from "../completions/resolve-node.js";
|
|
4
|
+
import { docIdentity } from "../doc-identity.js";
|
|
3
5
|
import { splitAliasQualified } from "./alias-qualified-value.js";
|
|
4
6
|
import { locateContextBinding } from "./locate-context-binding.js";
|
|
5
7
|
import { locateStepDeclaration } from "./locate-step.js";
|
|
6
|
-
import { moduleForFile } from "./manifest-navigation.js";
|
|
8
|
+
import { locateImport, moduleForFile } from "./manifest-navigation.js";
|
|
7
9
|
import { resolveCelTarget } from "./resolve-cel-target.js";
|
|
8
10
|
import { isKindSlot, resolveKindTarget } from "./resolve-kind-target.js";
|
|
9
11
|
import { resolveRefTarget } from "./resolve-ref-target.js";
|
|
@@ -34,6 +36,9 @@ analysis) {
|
|
|
34
36
|
return undefined;
|
|
35
37
|
const currentModule = moduleForFile(graph, currentFilePath) ?? graph.entry;
|
|
36
38
|
if (resolved.cel) {
|
|
39
|
+
const call = moduleCallTarget(graph, currentModule, text, astDocs, resolved, analysis);
|
|
40
|
+
if (call !== NOT_A_MODULE_CALL)
|
|
41
|
+
return call;
|
|
37
42
|
return resolveCelTarget(graph, currentModule, resolved.cel.segment, resolved.cel.offset, {
|
|
38
43
|
// A step is resolved in the CURRENT document — `steps.<name>.result` is
|
|
39
44
|
// readable only inside the resource that declares it, which is one
|
|
@@ -55,3 +60,42 @@ analysis) {
|
|
|
55
60
|
return resolveKindTarget(graph, currentModule, split);
|
|
56
61
|
return undefined;
|
|
57
62
|
}
|
|
63
|
+
const NOT_A_MODULE_CALL = Symbol("not a module call");
|
|
64
|
+
/**
|
|
65
|
+
* A module call under the cursor navigates to the callable resource it binds
|
|
66
|
+
* to — resolved the way a `!ref` of the same spelling is, since a call names a
|
|
67
|
+
* resource through the same grammar — and its alias to the import declaring it.
|
|
68
|
+
* Needs the analysis: which receivers are modules is the scope's answer.
|
|
69
|
+
*/
|
|
70
|
+
function moduleCallTarget(graph, currentModule, text, docs, resolved, analysis) {
|
|
71
|
+
const query = analysis?.celScope;
|
|
72
|
+
if (!resolved.cel || !query)
|
|
73
|
+
return NOT_A_MODULE_CALL;
|
|
74
|
+
const identity = docIdentity(docs[resolved.docIndex]);
|
|
75
|
+
const resource = query.resourceFor(identity.kind, identity.name);
|
|
76
|
+
if (!resource)
|
|
77
|
+
return NOT_A_MODULE_CALL;
|
|
78
|
+
let ast;
|
|
79
|
+
try {
|
|
80
|
+
ast = resolved.cel.segment.ast();
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (!(error instanceof CelParseError))
|
|
84
|
+
throw error;
|
|
85
|
+
return NOT_A_MODULE_CALL;
|
|
86
|
+
}
|
|
87
|
+
const scope = query.scopeAt(resource, resolved.concretePath ?? "");
|
|
88
|
+
const call = moduleCallAt(text, ast, resolved.cel.offset, scope);
|
|
89
|
+
if (!call)
|
|
90
|
+
return NOT_A_MODULE_CALL;
|
|
91
|
+
const imported = graph.importEdges.get(currentModule.owner.source)?.has(call.receiver) === true;
|
|
92
|
+
if (call.onReceiver)
|
|
93
|
+
return imported ? locateImport(currentModule, call.receiver) : undefined;
|
|
94
|
+
if (!scope.moduleFunction(call.qualified))
|
|
95
|
+
return undefined;
|
|
96
|
+
return resolveRefTarget(graph, currentModule, {
|
|
97
|
+
...(imported ? { alias: call.receiver } : {}),
|
|
98
|
+
name: call.name,
|
|
99
|
+
onAlias: false,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/diagnostics/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI3E;;;;;;;;;;iDAUiD;AACjD,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,kBAAkB,EACrB,GAAG,EAAE,iBAAiB,GACrB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/diagnostics/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI3E;;;;;;;;;;iDAUiD;AACjD,wBAAgB,mBAAmB,CACjC,CAAC,EAAE,kBAAkB,EACrB,GAAG,EAAE,iBAAiB,GACrB,oBAAoB,CAyBtB"}
|
|
@@ -14,7 +14,15 @@ import { resolveSeverity } from "./severity.js";
|
|
|
14
14
|
* editor for no difference the user can see. */
|
|
15
15
|
export function normalizeDiagnostic(d, ctx) {
|
|
16
16
|
const fix = diagnosticFix(d);
|
|
17
|
-
const suggestions = fix
|
|
17
|
+
const suggestions = fix
|
|
18
|
+
? [
|
|
19
|
+
{
|
|
20
|
+
kind: "replace",
|
|
21
|
+
replacement: fix.replacement,
|
|
22
|
+
...(fix.tag ? { tag: fix.tag } : {}),
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
: undefined;
|
|
18
26
|
return {
|
|
19
27
|
range: resolveRange(d, ctx),
|
|
20
28
|
severity: resolveSeverity(d),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-hover.d.ts","sourceRoot":"","sources":["../../src/hover/build-hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAGhB,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"build-hover.d.ts","sourceRoot":"","sources":["../../src/hover/build-hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAGhB,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA+F/C,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,IAAI,CAAC,EAAE,WAAW,EAAE;AACpB;;cAEc;AACd,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,WAAW,GAAG,SAAS,CAczB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CelParseError, parseToAst, readRefSlot, } from "@telorun/analyzer";
|
|
2
2
|
import { chainAt } from "../cel-chain.js";
|
|
3
|
+
import { describeFunction, functionSignature, moduleCallAt } from "../cel/module-calls.js";
|
|
3
4
|
import { celSymbolAt } from "../cel/symbols.js";
|
|
4
5
|
import { docIdentity } from "../doc-identity.js";
|
|
5
6
|
import { navigateSchema } from "../completions/detect-context.js";
|
|
@@ -94,7 +95,7 @@ analysis) {
|
|
|
94
95
|
if (!resolved)
|
|
95
96
|
return undefined;
|
|
96
97
|
if (resolved.cel) {
|
|
97
|
-
const hover = hoverForCel(resolved, astDocs, analysis?.celScope);
|
|
98
|
+
const hover = hoverForCel(text, resolved, astDocs, analysis?.celScope);
|
|
98
99
|
// A CEL body is still a field value; when the cursor is on nothing
|
|
99
100
|
// nameable inside it (an operator, a literal), fall through to the field's
|
|
100
101
|
// own hover rather than reporting nothing.
|
|
@@ -114,7 +115,7 @@ analysis) {
|
|
|
114
115
|
* still say what `steps.encode.result` IS even though nothing in the manifest
|
|
115
116
|
* declares it.
|
|
116
117
|
*/
|
|
117
|
-
function hoverForCel(resolved, docs, scopeQuery) {
|
|
118
|
+
function hoverForCel(text, resolved, docs, scopeQuery) {
|
|
118
119
|
if (!resolved.cel || !scopeQuery)
|
|
119
120
|
return undefined;
|
|
120
121
|
const identity = docIdentity(docs[resolved.docIndex]);
|
|
@@ -133,13 +134,26 @@ function hoverForCel(resolved, docs, scopeQuery) {
|
|
|
133
134
|
throw error;
|
|
134
135
|
return undefined;
|
|
135
136
|
}
|
|
137
|
+
const scope = scopeQuery.scopeAt(resource, resolved.concretePath ?? "");
|
|
138
|
+
// A module call's name hovers as the function it binds to: its signature and
|
|
139
|
+
// what it promises about determinism.
|
|
140
|
+
const call = moduleCallAt(text, ast, resolved.cel.offset, scope);
|
|
141
|
+
if (call && !call.onReceiver) {
|
|
142
|
+
const fn = scope.moduleFunction(call.qualified);
|
|
143
|
+
if (!fn)
|
|
144
|
+
return undefined;
|
|
145
|
+
const lines = ["```\n" + functionSignature(call.qualified, fn).label + "\n```"];
|
|
146
|
+
const about = describeFunction(fn, scope.moduleCallFlags(call.qualified));
|
|
147
|
+
if (about)
|
|
148
|
+
lines.push("", about);
|
|
149
|
+
return { contents: lines.join("\n") };
|
|
150
|
+
}
|
|
136
151
|
const hit = chainAt(ast, resolved.cel.offset);
|
|
137
152
|
if (!hit)
|
|
138
153
|
return undefined;
|
|
139
154
|
// The chain UP TO the cursor, not the whole chain: hovering `resources` in
|
|
140
155
|
// `resources.db.url` describes `resources`.
|
|
141
156
|
const parts = hit.parts.slice(0, hit.index + 1).map((p) => p.name);
|
|
142
|
-
const scope = scopeQuery.scopeAt(resource, resolved.concretePath ?? "");
|
|
143
157
|
const symbol = celSymbolAt(scope, parts);
|
|
144
158
|
if (!symbol)
|
|
145
159
|
return undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ export * from "./hover/index.js";
|
|
|
5
5
|
export * from "./semantic-tokens/index.js";
|
|
6
6
|
export * from "./definition/index.js";
|
|
7
7
|
export * from "./rename/index.js";
|
|
8
|
+
export * from "./signature-help/index.js";
|
|
8
9
|
export * from "./import-upgrades/index.js";
|
|
9
10
|
export * from "./workspace/index.js";
|
|
10
11
|
export { walkCel, flattenChain, chainAt, type ChainPart } from "./cel-chain.js";
|
|
12
|
+
export { functionSignature } from "./cel/module-calls.js";
|
|
11
13
|
//# 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,mBAAmB,CAAC;AAClC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AAIrC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,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,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AAIrC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,9 +5,13 @@ export * from "./hover/index.js";
|
|
|
5
5
|
export * from "./semantic-tokens/index.js";
|
|
6
6
|
export * from "./definition/index.js";
|
|
7
7
|
export * from "./rename/index.js";
|
|
8
|
+
export * from "./signature-help/index.js";
|
|
8
9
|
export * from "./import-upgrades/index.js";
|
|
9
10
|
export * from "./workspace/index.js";
|
|
10
11
|
// The repo's single CEL-tree walk. Exported because every host that has to
|
|
11
12
|
// answer "where is this name read" needs it and a second copy would be a second
|
|
12
13
|
// answer — the editor asks it before deleting a resource.
|
|
13
14
|
export { walkCel, flattenChain, chainAt } from "./cel-chain.js";
|
|
15
|
+
// A function's signature as every surface renders it — hover, completion,
|
|
16
|
+
// signature help and `telo cel functions` alike.
|
|
17
|
+
export { functionSignature } from "./cel/module-calls.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AstDocument, type LoadedGraph } from "@telorun/analyzer";
|
|
1
|
+
import { type AstDocument, type LoadedGraph, type ManifestAnalysis } from "@telorun/analyzer";
|
|
2
2
|
import type { RenamePreparation, RenameResult } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* Rename a name and every reference to it.
|
|
@@ -35,5 +35,8 @@ import type { RenamePreparation, RenameResult } from "./types.js";
|
|
|
35
35
|
*/
|
|
36
36
|
export declare function prepareRename(text: string, line: number, character: number, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[]): RenamePreparation;
|
|
37
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[]
|
|
38
|
+
export declare function buildRename(text: string, line: number, character: number, newName: string, graph: LoadedGraph, currentFilePath: string, docs?: AstDocument[],
|
|
39
|
+
/** Decides whether a resource's name denotes a type (a named shape) or a
|
|
40
|
+
* value. Without it every resource name is checked as a value. */
|
|
41
|
+
analysis?: ManifestAnalysis): RenameResult;
|
|
39
42
|
//# sourceMappingURL=build-rename.d.ts.map
|
|
@@ -1 +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,
|
|
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,EAEhB,KAAK,gBAAgB,EAGtB,MAAM,mBAAmB,CAAC;AAe3B,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;AACpB;mEACmE;AACnE,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,YAAY,CA2Bd"}
|
|
@@ -2,7 +2,8 @@ import { CelParseError, buildLineOffsets, checkName, offsetToPosition, parseToAs
|
|
|
2
2
|
import { chainAt } from "../cel-chain.js";
|
|
3
3
|
import { resolveNodeAtPosition, scalarString } from "../completions/resolve-node.js";
|
|
4
4
|
import { moduleDoc, moduleForFile, moduleFiles } from "../definition/manifest-navigation.js";
|
|
5
|
-
import {
|
|
5
|
+
import { moduleCallSites } from "../cel/module-calls.js";
|
|
6
|
+
import { declarationSites, functionCallSites, resourceDeclarations, resourceSites, stepDeclarations, stepSites, } from "./find-sites.js";
|
|
6
7
|
/**
|
|
7
8
|
* Rename a name and every reference to it.
|
|
8
9
|
*
|
|
@@ -50,7 +51,7 @@ export function prepareRename(text, line, character, graph, currentFilePath, doc
|
|
|
50
51
|
start: offsetToPosition(span[0], lineOffsets),
|
|
51
52
|
end: offsetToPosition(span[1], lineOffsets),
|
|
52
53
|
});
|
|
53
|
-
const symbol = symbolAt(resolved, astDocs, toRange);
|
|
54
|
+
const symbol = symbolAt(resolved, astDocs, toRange, text, ownReceivers(mod));
|
|
54
55
|
if (!symbol)
|
|
55
56
|
return { ok: false, reason: "Nothing renameable here." };
|
|
56
57
|
if ("reason" in symbol)
|
|
@@ -59,25 +60,28 @@ export function prepareRename(text, line, character, graph, currentFilePath, doc
|
|
|
59
60
|
return refusal ? { ok: false, reason: refusal } : { ok: true, symbol };
|
|
60
61
|
}
|
|
61
62
|
/** Prepare, validate the new name, then collect every edit. */
|
|
62
|
-
export function buildRename(text, line, character, newName, graph, currentFilePath, docs
|
|
63
|
+
export function buildRename(text, line, character, newName, graph, currentFilePath, docs,
|
|
64
|
+
/** Decides whether a resource's name denotes a type (a named shape) or a
|
|
65
|
+
* value. Without it every resource name is checked as a value. */
|
|
66
|
+
analysis) {
|
|
63
67
|
const prepared = prepareRename(text, line, character, graph, currentFilePath, docs);
|
|
64
68
|
if (!prepared.ok)
|
|
65
69
|
return prepared;
|
|
66
70
|
const { symbol } = prepared;
|
|
67
71
|
if (newName === symbol.name)
|
|
68
72
|
return { ok: true, symbol, files: [] };
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
73
|
+
const mod = moduleForFile(graph, currentFilePath);
|
|
74
|
+
// The new name is checked against the half of the convention its declaration
|
|
75
|
+
// belongs to — the same rule `telo check` enforces. Renaming *into* a name the
|
|
76
|
+
// analyzer would reject is a mistake worth catching in the rename box rather
|
|
77
|
+
// than as a squiggle afterwards.
|
|
78
|
+
const violation = checkName(newName, nameLevelOf(symbol, mod, analysis), surfaceLabel(symbol));
|
|
74
79
|
if (violation)
|
|
75
80
|
return { ok: false, reason: violation.message };
|
|
76
|
-
const mod = moduleForFile(graph, currentFilePath);
|
|
77
81
|
const files = filesForRename(symbol, mod, currentFilePath, text, docs);
|
|
78
82
|
const out = [];
|
|
79
83
|
for (const file of files) {
|
|
80
|
-
const edits = editsIn(file, symbol, newName);
|
|
84
|
+
const edits = editsIn(file, symbol, newName, ownReceivers(mod));
|
|
81
85
|
if (edits.length > 0)
|
|
82
86
|
out.push({ uri: file.uri, edits });
|
|
83
87
|
}
|
|
@@ -86,6 +90,22 @@ export function buildRename(text, line, character, newName, graph, currentFilePa
|
|
|
86
90
|
}
|
|
87
91
|
return { ok: true, symbol, files: out };
|
|
88
92
|
}
|
|
93
|
+
/** Steps and config keys are values; a resource is whatever its capability makes
|
|
94
|
+
* it — a named shape's name is a type. */
|
|
95
|
+
function nameLevelOf(symbol, mod, analysis) {
|
|
96
|
+
if (symbol.kind !== "resource" || !analysis)
|
|
97
|
+
return "value";
|
|
98
|
+
const sources = new Set(moduleFiles(mod).map((file) => file.source));
|
|
99
|
+
const declaration = analysis.manifests.find((m) => m.metadata?.name === symbol.name &&
|
|
100
|
+
sources.has(m.metadata?.source ?? ""));
|
|
101
|
+
return declaration ? analysis.nameLevel(declaration) : "value";
|
|
102
|
+
}
|
|
103
|
+
/** The receivers a module call names a function of THIS module through: `Self`
|
|
104
|
+
* and the module's own name. */
|
|
105
|
+
function ownReceivers(mod) {
|
|
106
|
+
const name = moduleDoc(mod)?.metadata?.name;
|
|
107
|
+
return new Set(typeof name === "string" && name ? ["Self", name] : ["Self"]);
|
|
108
|
+
}
|
|
89
109
|
const DECLARATION_BLOCKS = new Set(["variables", "secrets", "ports"]);
|
|
90
110
|
const MODULE_DOC_KINDS = new Set(["Telo.Application", "Telo.Library"]);
|
|
91
111
|
const KIND_DOC_KINDS = new Set(["Telo.Definition", "Telo.Abstract"]);
|
|
@@ -93,9 +113,9 @@ const SELF_PREFIX = "Self.";
|
|
|
93
113
|
/** Dispatch on what the cursor sits IN rather than on the field it is under —
|
|
94
114
|
* the posture `buildDefinition` takes, so the two features agree about what a
|
|
95
115
|
* given position means. */
|
|
96
|
-
function symbolAt(resolved, astDocs, toRange) {
|
|
116
|
+
function symbolAt(resolved, astDocs, toRange, text, receivers) {
|
|
97
117
|
if (resolved.cel)
|
|
98
|
-
return celSymbol(resolved.cel, toRange);
|
|
118
|
+
return celSymbol(resolved.cel, toRange, text, receivers);
|
|
99
119
|
const path = resolved.path;
|
|
100
120
|
const node = resolved.node;
|
|
101
121
|
// A key inside `variables:` / `secrets:` / `ports:` on the module doc.
|
|
@@ -176,7 +196,7 @@ function symbolAt(resolved, astDocs, toRange) {
|
|
|
176
196
|
}
|
|
177
197
|
/** A CEL chain root that names a renameable scope, with the cursor on its
|
|
178
198
|
* member. Anything deeper is a field of a resolved value, not a declaration. */
|
|
179
|
-
function celSymbol(cel, toRange) {
|
|
199
|
+
function celSymbol(cel, toRange, text, receivers) {
|
|
180
200
|
let ast;
|
|
181
201
|
try {
|
|
182
202
|
ast = cel.segment.ast();
|
|
@@ -186,6 +206,10 @@ function celSymbol(cel, toRange) {
|
|
|
186
206
|
throw error;
|
|
187
207
|
return undefined;
|
|
188
208
|
}
|
|
209
|
+
// A call of one of this module's functions renames the function resource.
|
|
210
|
+
const call = moduleCallSites(text, ast, (receiver) => receivers.has(receiver)).find((site) => cel.offset >= site.nameRange[0] && cel.offset <= site.nameRange[1]);
|
|
211
|
+
if (call)
|
|
212
|
+
return { kind: "resource", name: call.name, range: toRange(call.nameRange) };
|
|
189
213
|
const hit = chainAt(ast, cel.offset);
|
|
190
214
|
if (!hit || hit.index !== 1)
|
|
191
215
|
return undefined;
|
|
@@ -312,7 +336,7 @@ function filesForRename(symbol, mod, currentFilePath, text, docs) {
|
|
|
312
336
|
}
|
|
313
337
|
return [];
|
|
314
338
|
}
|
|
315
|
-
function editsIn(file, symbol, newName) {
|
|
339
|
+
function editsIn(file, symbol, newName, receivers) {
|
|
316
340
|
const lineOffsets = buildLineOffsets(file.text);
|
|
317
341
|
const spans = [];
|
|
318
342
|
for (const doc of file.docs) {
|
|
@@ -321,6 +345,9 @@ function editsIn(file, symbol, newName) {
|
|
|
321
345
|
spans.push(span);
|
|
322
346
|
for (const site of resourceSites(doc, symbol.name))
|
|
323
347
|
spans.push(site.range);
|
|
348
|
+
for (const site of functionCallSites(doc, file.text, receivers, symbol.name)) {
|
|
349
|
+
spans.push(site.range);
|
|
350
|
+
}
|
|
324
351
|
for (const span of exportEntrySpans(doc, symbol.name))
|
|
325
352
|
spans.push(span);
|
|
326
353
|
}
|
|
@@ -22,6 +22,11 @@ export interface NameSite {
|
|
|
22
22
|
/** A resource's references within one document: `!ref <name>` (and its `Self.`
|
|
23
23
|
* form) plus `resources.<name>` in CEL. */
|
|
24
24
|
export declare function resourceSites(doc: AstDocument, name: string): NameSite[];
|
|
25
|
+
/** A function's calls within one document: `<receiver>.<name>(…)` in CEL, for a
|
|
26
|
+
* receiver naming the declaring module itself (`Self`, its own name). A call
|
|
27
|
+
* through an import alias is another module's reference and is not matched.
|
|
28
|
+
* `text` is the document text the CEL offsets are measured against. */
|
|
29
|
+
export declare function functionCallSites(doc: AstDocument, text: string, receivers: ReadonlySet<string>, name: string): NameSite[];
|
|
25
30
|
/** A step's references within its declaring document: `steps.<name>` in CEL.
|
|
26
31
|
* Deliberately document-scoped — `steps.<name>.result` is readable only inside
|
|
27
32
|
* the resource whose body declares the step, and a resource is one document. */
|