@telorun/ide-support 0.11.2 → 0.12.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/README.md +2 -2
- package/dist/diagnostics/fix-edit.d.ts +40 -0
- package/dist/diagnostics/fix-edit.d.ts.map +1 -0
- package/dist/diagnostics/fix-edit.js +78 -0
- package/dist/diagnostics/index.d.ts +1 -0
- package/dist/diagnostics/index.d.ts.map +1 -1
- package/dist/diagnostics/index.js +1 -0
- package/dist/diagnostics/normalize.d.ts +10 -6
- package/dist/diagnostics/normalize.d.ts.map +1 -1
- package/dist/diagnostics/normalize.js +12 -9
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/diagnostics/fix-edit.ts +81 -0
- package/src/diagnostics/index.ts +6 -0
- package/src/diagnostics/normalize.ts +12 -10
- package/src/types.ts +3 -1
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@ Built to be language-agnostic and infinitely extensible.
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# Reconcile your manifest into a running backend
|
|
26
|
-
$ telo ./examples/
|
|
26
|
+
$ telo ./examples/todo-app
|
|
27
27
|
|
|
28
|
-
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:
|
|
28
|
+
{"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8077"}
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Why use Telo?
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Rendering a diagnostic's repair back into YAML source.
|
|
2
|
+
*
|
|
3
|
+
* A `fix.replacement` is a bare VALUE — a CEL expression, a kind name — while
|
|
4
|
+
* the span it replaces is the value node as written, which includes the
|
|
5
|
+
* scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
|
|
6
|
+
* replacement without anything doing it on purpose.) Writing the bare value
|
|
7
|
+
* into a quoted span therefore strips the quotes, and a CEL expression is
|
|
8
|
+
* exactly the kind of text that stops being one scalar once unquoted: a
|
|
9
|
+
* `: ` inside it starts a mapping, a trailing `#` starts a comment, a leading
|
|
10
|
+
* `%` or `&` is an indicator.
|
|
11
|
+
*
|
|
12
|
+
* So the replacement is re-quoted in the style the author used, and promoted
|
|
13
|
+
* to double quotes when a plain scalar could not survive the round-trip. This
|
|
14
|
+
* lives here rather than in one editor because both surfaces — the VS Code
|
|
15
|
+
* extension's quick fix and the Tauri editor's — apply the same repair to the
|
|
16
|
+
* same source, and the two must not disagree about how a value is written. */
|
|
17
|
+
/** Whether `value` can be written as a plain scalar without changing meaning. */
|
|
18
|
+
export declare function isPlainSafe(value: string): boolean;
|
|
19
|
+
/** Quote style of the source text a fix is replacing. */
|
|
20
|
+
export type QuoteStyle = "double" | "single" | "plain";
|
|
21
|
+
export declare function quoteStyleOf(source: string): QuoteStyle;
|
|
22
|
+
/** Render `replacement` so it occupies `originalSource`'s span as the same
|
|
23
|
+
* scalar the author would have written by hand, or `undefined` when the span
|
|
24
|
+
* cannot be rewritten safely.
|
|
25
|
+
*
|
|
26
|
+
* A plain original is kept plain when it can be — rewriting `Run.Sequenc` to
|
|
27
|
+
* `"Run.Sequence"` would be a correct but noisy diff on a kind name — and
|
|
28
|
+
* promoted to double quotes when the new value would not survive unquoted.
|
|
29
|
+
*
|
|
30
|
+
* **A multi-line span is refused.** A block scalar's span covers its `|`/`>-`
|
|
31
|
+
* indicator AND its trailing newline, so writing a single-line scalar over it
|
|
32
|
+
* deletes the line break that ended the mapping entry and glues the next key
|
|
33
|
+
* onto the value — the document stops parsing. Re-emitting a block scalar
|
|
34
|
+
* correctly needs the node's indentation, which no consumer of this function
|
|
35
|
+
* has. A multi-line REPLACEMENT is refused for the mirror reason: its
|
|
36
|
+
* continuation lines would land at column 0, which is not a legal mapping
|
|
37
|
+
* value. `DiagnosticFix` promises a repair that can be applied without review,
|
|
38
|
+
* so the only honest answer for these is no repair. */
|
|
39
|
+
export declare function renderFixReplacement(originalSource: string, replacement: string): string | undefined;
|
|
40
|
+
//# sourceMappingURL=fix-edit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-edit.d.ts","sourceRoot":"","sources":["../../src/diagnostics/fix-edit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;+EAe+E;AAS/E,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAUlD;AAED,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAIvD;AAED;;;;;;;;;;;;;;;;wDAgBwD;AACxD,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAcpB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Rendering a diagnostic's repair back into YAML source.
|
|
2
|
+
*
|
|
3
|
+
* A `fix.replacement` is a bare VALUE — a CEL expression, a kind name — while
|
|
4
|
+
* the span it replaces is the value node as written, which includes the
|
|
5
|
+
* scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
|
|
6
|
+
* replacement without anything doing it on purpose.) Writing the bare value
|
|
7
|
+
* into a quoted span therefore strips the quotes, and a CEL expression is
|
|
8
|
+
* exactly the kind of text that stops being one scalar once unquoted: a
|
|
9
|
+
* `: ` inside it starts a mapping, a trailing `#` starts a comment, a leading
|
|
10
|
+
* `%` or `&` is an indicator.
|
|
11
|
+
*
|
|
12
|
+
* So the replacement is re-quoted in the style the author used, and promoted
|
|
13
|
+
* to double quotes when a plain scalar could not survive the round-trip. This
|
|
14
|
+
* lives here rather than in one editor because both surfaces — the VS Code
|
|
15
|
+
* extension's quick fix and the Tauri editor's — apply the same repair to the
|
|
16
|
+
* same source, and the two must not disagree about how a value is written. */
|
|
17
|
+
/** Characters that make a plain (unquoted) YAML scalar reparse as something
|
|
18
|
+
* else. `-` and `?` are indicators only when followed by a space, so they are
|
|
19
|
+
* handled by the leading-token check rather than listed here. */
|
|
20
|
+
const PLAIN_UNSAFE_LEAD = new Set([
|
|
21
|
+
"&", "*", "!", "|", ">", "%", "@", "`", "#", "'", '"', "{", "[", "}", "]", ",",
|
|
22
|
+
]);
|
|
23
|
+
/** Whether `value` can be written as a plain scalar without changing meaning. */
|
|
24
|
+
export function isPlainSafe(value) {
|
|
25
|
+
if (value === "" || value.trim() !== value)
|
|
26
|
+
return false;
|
|
27
|
+
if (PLAIN_UNSAFE_LEAD.has(value[0]))
|
|
28
|
+
return false;
|
|
29
|
+
// `-`/`?`/`:` lead only matter when a space follows — `-x` is a scalar,
|
|
30
|
+
// `- x` is a sequence entry.
|
|
31
|
+
if (/^[-?:]\s/.test(value))
|
|
32
|
+
return false;
|
|
33
|
+
// A colon-space anywhere opens a mapping; a space-hash opens a comment.
|
|
34
|
+
if (value.includes(": ") || value.includes(" #"))
|
|
35
|
+
return false;
|
|
36
|
+
if (value.endsWith(":"))
|
|
37
|
+
return false;
|
|
38
|
+
return !/[\n\r]/.test(value);
|
|
39
|
+
}
|
|
40
|
+
export function quoteStyleOf(source) {
|
|
41
|
+
if (source.length >= 2 && source.startsWith('"') && source.endsWith('"'))
|
|
42
|
+
return "double";
|
|
43
|
+
if (source.length >= 2 && source.startsWith("'") && source.endsWith("'"))
|
|
44
|
+
return "single";
|
|
45
|
+
return "plain";
|
|
46
|
+
}
|
|
47
|
+
/** Render `replacement` so it occupies `originalSource`'s span as the same
|
|
48
|
+
* scalar the author would have written by hand, or `undefined` when the span
|
|
49
|
+
* cannot be rewritten safely.
|
|
50
|
+
*
|
|
51
|
+
* A plain original is kept plain when it can be — rewriting `Run.Sequenc` to
|
|
52
|
+
* `"Run.Sequence"` would be a correct but noisy diff on a kind name — and
|
|
53
|
+
* promoted to double quotes when the new value would not survive unquoted.
|
|
54
|
+
*
|
|
55
|
+
* **A multi-line span is refused.** A block scalar's span covers its `|`/`>-`
|
|
56
|
+
* indicator AND its trailing newline, so writing a single-line scalar over it
|
|
57
|
+
* deletes the line break that ended the mapping entry and glues the next key
|
|
58
|
+
* onto the value — the document stops parsing. Re-emitting a block scalar
|
|
59
|
+
* correctly needs the node's indentation, which no consumer of this function
|
|
60
|
+
* has. A multi-line REPLACEMENT is refused for the mirror reason: its
|
|
61
|
+
* continuation lines would land at column 0, which is not a legal mapping
|
|
62
|
+
* value. `DiagnosticFix` promises a repair that can be applied without review,
|
|
63
|
+
* so the only honest answer for these is no repair. */
|
|
64
|
+
export function renderFixReplacement(originalSource, replacement) {
|
|
65
|
+
if (/[\n\r]/.test(originalSource) || /[\n\r]/.test(replacement))
|
|
66
|
+
return undefined;
|
|
67
|
+
const style = quoteStyleOf(originalSource);
|
|
68
|
+
if (style === "single") {
|
|
69
|
+
// A single-quoted YAML scalar escapes only the quote, by doubling it. CEL
|
|
70
|
+
// string literals use single quotes constantly, so this is the common case
|
|
71
|
+
// for an expression written in a single-quoted scalar.
|
|
72
|
+
return `'${replacement.replaceAll("'", "''")}'`;
|
|
73
|
+
}
|
|
74
|
+
if (style === "double" || !isPlainSafe(replacement)) {
|
|
75
|
+
return `"${replacement.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
76
|
+
}
|
|
77
|
+
return replacement;
|
|
78
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { findPositions } from "./find-positions.js";
|
|
2
|
+
export { isPlainSafe, quoteStyleOf, renderFixReplacement, type QuoteStyle, } from "./fix-edit.js";
|
|
2
3
|
export { assembleGraphDiagnostics, compromisedFiles } from "./graph-diagnostics.js";
|
|
3
4
|
export { normalizeDiagnostic } from "./normalize.js";
|
|
4
5
|
export { resolveRange } from "./range-resolver.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diagnostics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diagnostics/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,KAAK,UAAU,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { findPositions } from "./find-positions.js";
|
|
2
|
+
export { isPlainSafe, quoteStyleOf, renderFixReplacement, } from "./fix-edit.js";
|
|
2
3
|
export { assembleGraphDiagnostics, compromisedFiles } from "./graph-diagnostics.js";
|
|
3
4
|
export { normalizeDiagnostic } from "./normalize.js";
|
|
4
5
|
export { resolveRange } from "./range-resolver.js";
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AnalysisDiagnostic } from "@telorun/analyzer";
|
|
2
2
|
import type { DiagnosticContext, NormalizedDiagnostic } from "../types.js";
|
|
3
3
|
/** Converts a raw analyzer diagnostic into a host-ready shape:
|
|
4
4
|
* - Guarantees `range` and `severity`.
|
|
5
|
-
* - Surfaces `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* - Surfaces the analyzer's `fix` stamp as a structured `suggestions` entry
|
|
6
|
+
* hosts wire into a CodeAction.
|
|
7
|
+
* Does not rewrite the message — the analyzer already formatted the
|
|
8
|
+
* human-readable hint, keeping CLI and IDE output in sync.
|
|
9
|
+
*
|
|
10
|
+
* One suggestion kind, not one per producer: an unknown kind name and a
|
|
11
|
+
* mis-called CEL function are the same gesture at the host (replace the value
|
|
12
|
+
* at this range), and a second kind would mean a second action path in every
|
|
13
|
+
* editor for no difference the user can see. */
|
|
10
14
|
export declare function normalizeDiagnostic(d: AnalysisDiagnostic, ctx: DiagnosticContext): NormalizedDiagnostic;
|
|
11
15
|
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/diagnostics/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
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,CAatB"}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { diagnosticFix } from "@telorun/analyzer";
|
|
1
2
|
import { resolveRange } from "./range-resolver.js";
|
|
2
3
|
import { resolveSeverity } from "./severity.js";
|
|
3
4
|
/** Converts a raw analyzer diagnostic into a host-ready shape:
|
|
4
5
|
* - Guarantees `range` and `severity`.
|
|
5
|
-
* - Surfaces `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* - Surfaces the analyzer's `fix` stamp as a structured `suggestions` entry
|
|
7
|
+
* hosts wire into a CodeAction.
|
|
8
|
+
* Does not rewrite the message — the analyzer already formatted the
|
|
9
|
+
* human-readable hint, keeping CLI and IDE output in sync.
|
|
10
|
+
*
|
|
11
|
+
* One suggestion kind, not one per producer: an unknown kind name and a
|
|
12
|
+
* mis-called CEL function are the same gesture at the host (replace the value
|
|
13
|
+
* at this range), and a second kind would mean a second action path in every
|
|
14
|
+
* editor for no difference the user can see. */
|
|
10
15
|
export function normalizeDiagnostic(d, ctx) {
|
|
11
|
-
const
|
|
12
|
-
const suggestions =
|
|
13
|
-
? [{ kind: "replace-kind", replacement: suggestedKind }]
|
|
14
|
-
: undefined;
|
|
16
|
+
const fix = diagnosticFix(d);
|
|
17
|
+
const suggestions = fix ? [{ kind: "replace", replacement: fix.replacement }] : undefined;
|
|
15
18
|
return {
|
|
16
19
|
range: resolveRange(d, ctx),
|
|
17
20
|
severity: resolveSeverity(d),
|
package/dist/types.d.ts
CHANGED
|
@@ -97,8 +97,10 @@ export interface NormalizedDiagnostic {
|
|
|
97
97
|
code: string;
|
|
98
98
|
source: string;
|
|
99
99
|
message: string;
|
|
100
|
+
/** Mechanically applicable repairs. `replacement` is the whole corrected
|
|
101
|
+
* value at the diagnostic's range — apply it by replacing that range. */
|
|
100
102
|
suggestions?: Array<{
|
|
101
|
-
kind: "replace
|
|
103
|
+
kind: "replace";
|
|
102
104
|
replacement: string;
|
|
103
105
|
}>;
|
|
104
106
|
/** Preserved verbatim from the source `AnalysisDiagnostic`. Carries
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGzE,YAAY,EACV,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,KAAK,EACN,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjG,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;uBAKmB;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;+EAC+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;qBAIqB;AACrB,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;AAElE;6EAC6E;AAC7E,eAAO,MAAM,qBAAqB,EAAE,SAAS,iBAAiB,EAAsC,CAAC;AAErG;;sEAEsE;AACtE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;;;wDAGwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;wDAGwD;AACxD,MAAM,WAAW,MAAM;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;uCAMuC;AACvC,MAAM,WAAW,qBAAqB;IACpC;;2DAEuD;IACvD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpD;kEAC8D;IAC9D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C;;;oFAGgF;IAChF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C;;0CAEsC;IACtC,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/ide-support",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Editor-host-agnostic IDE support (completions, diagnostic normalization) for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"src/**"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@telorun/analyzer": "0.
|
|
39
|
+
"@telorun/analyzer": "0.57.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^20.0.0",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** Rendering a diagnostic's repair back into YAML source.
|
|
2
|
+
*
|
|
3
|
+
* A `fix.replacement` is a bare VALUE — a CEL expression, a kind name — while
|
|
4
|
+
* the span it replaces is the value node as written, which includes the
|
|
5
|
+
* scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
|
|
6
|
+
* replacement without anything doing it on purpose.) Writing the bare value
|
|
7
|
+
* into a quoted span therefore strips the quotes, and a CEL expression is
|
|
8
|
+
* exactly the kind of text that stops being one scalar once unquoted: a
|
|
9
|
+
* `: ` inside it starts a mapping, a trailing `#` starts a comment, a leading
|
|
10
|
+
* `%` or `&` is an indicator.
|
|
11
|
+
*
|
|
12
|
+
* So the replacement is re-quoted in the style the author used, and promoted
|
|
13
|
+
* to double quotes when a plain scalar could not survive the round-trip. This
|
|
14
|
+
* lives here rather than in one editor because both surfaces — the VS Code
|
|
15
|
+
* extension's quick fix and the Tauri editor's — apply the same repair to the
|
|
16
|
+
* same source, and the two must not disagree about how a value is written. */
|
|
17
|
+
|
|
18
|
+
/** Characters that make a plain (unquoted) YAML scalar reparse as something
|
|
19
|
+
* else. `-` and `?` are indicators only when followed by a space, so they are
|
|
20
|
+
* handled by the leading-token check rather than listed here. */
|
|
21
|
+
const PLAIN_UNSAFE_LEAD = new Set([
|
|
22
|
+
"&", "*", "!", "|", ">", "%", "@", "`", "#", "'", '"', "{", "[", "}", "]", ",",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
/** Whether `value` can be written as a plain scalar without changing meaning. */
|
|
26
|
+
export function isPlainSafe(value: string): boolean {
|
|
27
|
+
if (value === "" || value.trim() !== value) return false;
|
|
28
|
+
if (PLAIN_UNSAFE_LEAD.has(value[0]!)) return false;
|
|
29
|
+
// `-`/`?`/`:` lead only matter when a space follows — `-x` is a scalar,
|
|
30
|
+
// `- x` is a sequence entry.
|
|
31
|
+
if (/^[-?:]\s/.test(value)) return false;
|
|
32
|
+
// A colon-space anywhere opens a mapping; a space-hash opens a comment.
|
|
33
|
+
if (value.includes(": ") || value.includes(" #")) return false;
|
|
34
|
+
if (value.endsWith(":")) return false;
|
|
35
|
+
return !/[\n\r]/.test(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Quote style of the source text a fix is replacing. */
|
|
39
|
+
export type QuoteStyle = "double" | "single" | "plain";
|
|
40
|
+
|
|
41
|
+
export function quoteStyleOf(source: string): QuoteStyle {
|
|
42
|
+
if (source.length >= 2 && source.startsWith('"') && source.endsWith('"')) return "double";
|
|
43
|
+
if (source.length >= 2 && source.startsWith("'") && source.endsWith("'")) return "single";
|
|
44
|
+
return "plain";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Render `replacement` so it occupies `originalSource`'s span as the same
|
|
48
|
+
* scalar the author would have written by hand, or `undefined` when the span
|
|
49
|
+
* cannot be rewritten safely.
|
|
50
|
+
*
|
|
51
|
+
* A plain original is kept plain when it can be — rewriting `Run.Sequenc` to
|
|
52
|
+
* `"Run.Sequence"` would be a correct but noisy diff on a kind name — and
|
|
53
|
+
* promoted to double quotes when the new value would not survive unquoted.
|
|
54
|
+
*
|
|
55
|
+
* **A multi-line span is refused.** A block scalar's span covers its `|`/`>-`
|
|
56
|
+
* indicator AND its trailing newline, so writing a single-line scalar over it
|
|
57
|
+
* deletes the line break that ended the mapping entry and glues the next key
|
|
58
|
+
* onto the value — the document stops parsing. Re-emitting a block scalar
|
|
59
|
+
* correctly needs the node's indentation, which no consumer of this function
|
|
60
|
+
* has. A multi-line REPLACEMENT is refused for the mirror reason: its
|
|
61
|
+
* continuation lines would land at column 0, which is not a legal mapping
|
|
62
|
+
* value. `DiagnosticFix` promises a repair that can be applied without review,
|
|
63
|
+
* so the only honest answer for these is no repair. */
|
|
64
|
+
export function renderFixReplacement(
|
|
65
|
+
originalSource: string,
|
|
66
|
+
replacement: string,
|
|
67
|
+
): string | undefined {
|
|
68
|
+
if (/[\n\r]/.test(originalSource) || /[\n\r]/.test(replacement)) return undefined;
|
|
69
|
+
const style = quoteStyleOf(originalSource);
|
|
70
|
+
|
|
71
|
+
if (style === "single") {
|
|
72
|
+
// A single-quoted YAML scalar escapes only the quote, by doubling it. CEL
|
|
73
|
+
// string literals use single quotes constantly, so this is the common case
|
|
74
|
+
// for an expression written in a single-quoted scalar.
|
|
75
|
+
return `'${replacement.replaceAll("'", "''")}'`;
|
|
76
|
+
}
|
|
77
|
+
if (style === "double" || !isPlainSafe(replacement)) {
|
|
78
|
+
return `"${replacement.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
79
|
+
}
|
|
80
|
+
return replacement;
|
|
81
|
+
}
|
package/src/diagnostics/index.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export { findPositions } from "./find-positions.js";
|
|
2
|
+
export {
|
|
3
|
+
isPlainSafe,
|
|
4
|
+
quoteStyleOf,
|
|
5
|
+
renderFixReplacement,
|
|
6
|
+
type QuoteStyle,
|
|
7
|
+
} from "./fix-edit.js";
|
|
2
8
|
export { assembleGraphDiagnostics, compromisedFiles } from "./graph-diagnostics.js";
|
|
3
9
|
export { normalizeDiagnostic } from "./normalize.js";
|
|
4
10
|
export { resolveRange } from "./range-resolver.js";
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { diagnosticFix, type AnalysisDiagnostic } from "@telorun/analyzer";
|
|
2
2
|
import type { DiagnosticContext, NormalizedDiagnostic } from "../types.js";
|
|
3
3
|
import { resolveRange } from "./range-resolver.js";
|
|
4
4
|
import { resolveSeverity } from "./severity.js";
|
|
5
5
|
|
|
6
6
|
/** Converts a raw analyzer diagnostic into a host-ready shape:
|
|
7
7
|
* - Guarantees `range` and `severity`.
|
|
8
|
-
* - Surfaces `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* - Surfaces the analyzer's `fix` stamp as a structured `suggestions` entry
|
|
9
|
+
* hosts wire into a CodeAction.
|
|
10
|
+
* Does not rewrite the message — the analyzer already formatted the
|
|
11
|
+
* human-readable hint, keeping CLI and IDE output in sync.
|
|
12
|
+
*
|
|
13
|
+
* One suggestion kind, not one per producer: an unknown kind name and a
|
|
14
|
+
* mis-called CEL function are the same gesture at the host (replace the value
|
|
15
|
+
* at this range), and a second kind would mean a second action path in every
|
|
16
|
+
* editor for no difference the user can see. */
|
|
13
17
|
export function normalizeDiagnostic(
|
|
14
18
|
d: AnalysisDiagnostic,
|
|
15
19
|
ctx: DiagnosticContext,
|
|
16
20
|
): NormalizedDiagnostic {
|
|
17
|
-
const
|
|
18
|
-
const suggestions =
|
|
19
|
-
? [{ kind: "replace-kind" as const, replacement: suggestedKind }]
|
|
20
|
-
: undefined;
|
|
21
|
+
const fix = diagnosticFix(d);
|
|
22
|
+
const suggestions = fix ? [{ kind: "replace" as const, replacement: fix.replacement }] : undefined;
|
|
21
23
|
|
|
22
24
|
return {
|
|
23
25
|
range: resolveRange(d, ctx),
|
package/src/types.ts
CHANGED
|
@@ -124,7 +124,9 @@ export interface NormalizedDiagnostic {
|
|
|
124
124
|
code: string;
|
|
125
125
|
source: string;
|
|
126
126
|
message: string;
|
|
127
|
-
|
|
127
|
+
/** Mechanically applicable repairs. `replacement` is the whole corrected
|
|
128
|
+
* value at the diagnostic's range — apply it by replacing that range. */
|
|
129
|
+
suggestions?: Array<{ kind: "replace"; replacement: string }>;
|
|
128
130
|
/** Preserved verbatim from the source `AnalysisDiagnostic`. Carries
|
|
129
131
|
* resource/path stamps that downstream UIs (popovers, "at <path>" hints,
|
|
130
132
|
* CodeAction wiring) read after normalization. Opaque on purpose so this
|