@telorun/ide-support 0.12.0 → 0.13.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.
@@ -5,36 +5,13 @@
5
5
  * scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
6
6
  * replacement without anything doing it on purpose.) Writing the bare value
7
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.
8
+ * exactly the kind of text that stops being one scalar once unquoted.
11
9
  *
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;
10
+ * **The rule itself lives in `@telorun/analyzer`** (`yaml-source-edit.ts`),
11
+ * not here. It started here because both editors' quick fixes had to agree
12
+ * about how a value is written; `telo migrate` then became a third writer of
13
+ * the same files, and the analyzer is below all three. Re-exported rather than
14
+ * moved outright so a host keeps importing its repair rendering from the
15
+ * package that owns the rest of its diagnostic presentation. */
16
+ export { applyTextEdits, isPlainSafe, quoteStyleOf, renderFixReplacement, type QuoteStyle, type TextEdit, } from "@telorun/analyzer";
40
17
  //# sourceMappingURL=fix-edit.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"fix-edit.d.ts","sourceRoot":"","sources":["../../src/diagnostics/fix-edit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;iEAciE;AAEjE,OAAO,EACL,cAAc,EACd,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,KAAK,UAAU,EACf,KAAK,QAAQ,GACd,MAAM,mBAAmB,CAAC"}
@@ -5,74 +5,12 @@
5
5
  * scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
6
6
  * replacement without anything doing it on purpose.) Writing the bare value
7
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.
8
+ * exactly the kind of text that stops being one scalar once unquoted.
11
9
  *
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
- }
10
+ * **The rule itself lives in `@telorun/analyzer`** (`yaml-source-edit.ts`),
11
+ * not here. It started here because both editors' quick fixes had to agree
12
+ * about how a value is written; `telo migrate` then became a third writer of
13
+ * the same files, and the analyzer is below all three. Re-exported rather than
14
+ * moved outright so a host keeps importing its repair rendering from the
15
+ * package that owns the rest of its diagnostic presentation. */
16
+ export { applyTextEdits, isPlainSafe, quoteStyleOf, renderFixReplacement, } from "@telorun/analyzer";
@@ -21,9 +21,9 @@ export declare function compromisedFiles(graph: LoadedGraph): Set<string>;
21
21
  * (`suppressed`). The presentation policy lives here — not in the analyzer,
22
22
  * which only emits raw channels — so every host applies it identically.
23
23
  *
24
- * `diagnostics` folds parse, version-reconciliation, import-resolution, and the
25
- * *live* analysis diagnostics (those on non-compromised files) into one list,
26
- * in that order. `suppressed` carries the analysis cascade dropped from
24
+ * `diagnostics` folds parse, migration, version-reconciliation,
25
+ * import-resolution, and the *live* analysis diagnostics (those on
26
+ * non-compromised files) into one list, in that order. `suppressed` carries the analysis cascade dropped from
27
27
  * compromised files, still available to a host that wants to render it dimmed /
28
28
  * as related information rather than hide it.
29
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"graph-diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics/graph-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAG3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAShE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,WAAW,EAClB,mBAAmB,EAAE,kBAAkB,EAAE,GACxC;IAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAAC,UAAU,EAAE,kBAAkB,EAAE,CAAA;CAAE,CAoBzE"}
1
+ {"version":3,"file":"graph-diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics/graph-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAG3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAShE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,WAAW,EAClB,mBAAmB,EAAE,kBAAkB,EAAE,GACxC;IAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAAC,UAAU,EAAE,kBAAkB,EAAE,CAAA;CAAE,CA0BzE"}
@@ -1,4 +1,4 @@
1
- import { importResolutionDiagnostics, } from "@telorun/analyzer";
1
+ import { importResolutionDiagnostics, remapMigratedPaths, } from "@telorun/analyzer";
2
2
  import { findPositions } from "./find-positions.js";
3
3
  /**
4
4
  * Files whose static-analysis output is an unreliable cascade rather than a set
@@ -33,9 +33,9 @@ export function compromisedFiles(graph) {
33
33
  * (`suppressed`). The presentation policy lives here — not in the analyzer,
34
34
  * which only emits raw channels — so every host applies it identically.
35
35
  *
36
- * `diagnostics` folds parse, version-reconciliation, import-resolution, and the
37
- * *live* analysis diagnostics (those on non-compromised files) into one list,
38
- * in that order. `suppressed` carries the analysis cascade dropped from
36
+ * `diagnostics` folds parse, migration, version-reconciliation,
37
+ * import-resolution, and the *live* analysis diagnostics (those on
38
+ * non-compromised files) into one list, in that order. `suppressed` carries the analysis cascade dropped from
39
39
  * compromised files, still available to a host that wants to render it dimmed /
40
40
  * as related information rather than hide it.
41
41
  *
@@ -54,7 +54,12 @@ export function assembleGraphDiagnostics(graph, analysisDiagnostics) {
54
54
  const entrySource = graph.entry.owner.source;
55
55
  const live = [];
56
56
  const suppressed = [];
57
- for (const d of analysisDiagnostics) {
57
+ // Analysis ran over the MIGRATED tree, while positions come from the raw
58
+ // file. Remapping each `data.path` back through the driver's provenance
59
+ // record is what keeps a squiggle on the author's own line after a key
60
+ // rename — and what stops a fix among them writing across a parent's span.
61
+ // A no-op when nothing in the graph was migrated.
62
+ for (const d of remapMigratedPaths(graph, analysisDiagnostics)) {
58
63
  const rawFilePath = d.data?.filePath;
59
64
  const file = findPositions(graph, d.data)?.file ?? rawFilePath ?? entrySource;
60
65
  (compromised.has(file) ? suppressed : live).push(d);
@@ -62,6 +67,7 @@ export function assembleGraphDiagnostics(graph, analysisDiagnostics) {
62
67
  return {
63
68
  diagnostics: [
64
69
  ...graph.parseDiagnostics,
70
+ ...graph.migrationDiagnostics,
65
71
  ...graph.versionDiagnostics,
66
72
  ...importResolutionDiagnostics(graph),
67
73
  ...live,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/ide-support",
3
- "version": "0.12.0",
3
+ "version": "0.13.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.57.0"
39
+ "@telorun/analyzer": "0.58.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^20.0.0",
@@ -5,77 +5,20 @@
5
5
  * scalar's quotes. (The YAML tag sits outside that span, so `!cel` survives a
6
6
  * replacement without anything doing it on purpose.) Writing the bare value
7
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.
8
+ * exactly the kind of text that stops being one scalar once unquoted.
11
9
  *
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. */
10
+ * **The rule itself lives in `@telorun/analyzer`** (`yaml-source-edit.ts`),
11
+ * not here. It started here because both editors' quick fixes had to agree
12
+ * about how a value is written; `telo migrate` then became a third writer of
13
+ * the same files, and the analyzer is below all three. Re-exported rather than
14
+ * moved outright so a host keeps importing its repair rendering from the
15
+ * package that owns the rest of its diagnostic presentation. */
17
16
 
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
- }
17
+ export {
18
+ applyTextEdits,
19
+ isPlainSafe,
20
+ quoteStyleOf,
21
+ renderFixReplacement,
22
+ type QuoteStyle,
23
+ type TextEdit,
24
+ } from "@telorun/analyzer";
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  importResolutionDiagnostics,
3
+ remapMigratedPaths,
3
4
  type AnalysisDiagnostic,
4
5
  type LoadedGraph,
5
6
  } from "@telorun/analyzer";
@@ -37,9 +38,9 @@ export function compromisedFiles(graph: LoadedGraph): Set<string> {
37
38
  * (`suppressed`). The presentation policy lives here — not in the analyzer,
38
39
  * which only emits raw channels — so every host applies it identically.
39
40
  *
40
- * `diagnostics` folds parse, version-reconciliation, import-resolution, and the
41
- * *live* analysis diagnostics (those on non-compromised files) into one list,
42
- * in that order. `suppressed` carries the analysis cascade dropped from
41
+ * `diagnostics` folds parse, migration, version-reconciliation,
42
+ * import-resolution, and the *live* analysis diagnostics (those on
43
+ * non-compromised files) into one list, in that order. `suppressed` carries the analysis cascade dropped from
43
44
  * compromised files, still available to a host that wants to render it dimmed /
44
45
  * as related information rather than hide it.
45
46
  *
@@ -61,7 +62,12 @@ export function assembleGraphDiagnostics(
61
62
  const entrySource = graph.entry.owner.source;
62
63
  const live: AnalysisDiagnostic[] = [];
63
64
  const suppressed: AnalysisDiagnostic[] = [];
64
- for (const d of analysisDiagnostics) {
65
+ // Analysis ran over the MIGRATED tree, while positions come from the raw
66
+ // file. Remapping each `data.path` back through the driver's provenance
67
+ // record is what keeps a squiggle on the author's own line after a key
68
+ // rename — and what stops a fix among them writing across a parent's span.
69
+ // A no-op when nothing in the graph was migrated.
70
+ for (const d of remapMigratedPaths(graph, analysisDiagnostics)) {
65
71
  const rawFilePath = (d.data as { filePath?: string } | undefined)?.filePath;
66
72
  const file = findPositions(graph, d.data)?.file ?? rawFilePath ?? entrySource;
67
73
  (compromised.has(file) ? suppressed : live).push(d);
@@ -70,6 +76,7 @@ export function assembleGraphDiagnostics(
70
76
  return {
71
77
  diagnostics: [
72
78
  ...graph.parseDiagnostics,
79
+ ...graph.migrationDiagnostics,
73
80
  ...graph.versionDiagnostics,
74
81
  ...importResolutionDiagnostics(graph),
75
82
  ...live,