@telorun/ide-support 0.4.1 → 0.4.4

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="./assets/telo.png" alt="Telo" width="200" />
2
+ <img src="https://raw.githubusercontent.com/telorun/telo/main/assets/telo.png" alt="Telo" width="200" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">Telo</h1>
@@ -61,12 +61,12 @@ targets:
61
61
  kind: Telo.Import
62
62
  metadata:
63
63
  name: Http
64
- source: ../modules/http-server
64
+ source: std/http-server@0.4.0
65
65
  ---
66
66
  kind: Telo.Import
67
67
  metadata:
68
68
  name: Sql
69
- source: ../modules/sql
69
+ source: std/sql@0.2.3
70
70
  ---
71
71
  # SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
72
72
  kind: Sql.Connection
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/completions/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AA2B3E,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAY7B"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/completions/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAsK3E,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA2B7B"}
@@ -1,16 +1,137 @@
1
- import { detectContext } from "./detect-context.js";
1
+ import { detectContext, lookupRefConstraint } from "./detect-context.js";
2
2
  import { importSourceCompletions } from "./import-source.js";
3
3
  import { propKeyCompletions } from "./prop-keys.js";
4
4
  import { CAPABILITY_VALUES } from "./valid-capabilities.js";
5
- function kindCompletions(registry) {
6
- const kinds = new Set(registry
7
- ? registry.validUserFacingKinds()
8
- : ["Telo.Application", "Telo.Library", "Telo.Import", "Telo.Definition"]);
9
- return Array.from(kinds).map((kind) => ({
10
- label: kind,
11
- kind: "class",
12
- detail: "Telo resource kind",
13
- }));
5
+ /** Roughly extract `(kind, metadata.name)` pairs from a multi-doc YAML text.
6
+ * This is intentionally lightweight: it scans for top-level `kind:` and the
7
+ * first `name:` under a `metadata:` block per `---`-separated section, with
8
+ * no full YAML parse. The output is consumed only for completion ranking,
9
+ * so misses on edge-case manifests are acceptable; the analyzer remains
10
+ * the source of truth for validation. */
11
+ function extractInFileResources(text) {
12
+ const out = [];
13
+ const lines = text.split("\n");
14
+ let currentKind;
15
+ let currentName;
16
+ let inMetadata = false;
17
+ const flush = () => {
18
+ if (currentKind && currentName) {
19
+ out.push({ kind: currentKind, name: currentName });
20
+ }
21
+ currentKind = undefined;
22
+ currentName = undefined;
23
+ inMetadata = false;
24
+ };
25
+ for (const line of lines) {
26
+ if (line.trimEnd() === "---") {
27
+ flush();
28
+ continue;
29
+ }
30
+ const kindMatch = line.match(/^kind:\s*(\S+)/);
31
+ if (kindMatch) {
32
+ currentKind = kindMatch[1];
33
+ continue;
34
+ }
35
+ if (/^metadata:\s*$/.test(line)) {
36
+ inMetadata = true;
37
+ continue;
38
+ }
39
+ if (inMetadata) {
40
+ // Lines inside metadata are indented. Pick the first `name:` we see.
41
+ const nameMatch = line.match(/^\s+name:\s*(\S+)/);
42
+ if (nameMatch && !currentName) {
43
+ currentName = nameMatch[1];
44
+ }
45
+ // Leaving the metadata block — any line that is not indented marks
46
+ // the end of the block.
47
+ if (line.length > 0 && !/^\s/.test(line)) {
48
+ inMetadata = false;
49
+ }
50
+ }
51
+ }
52
+ flush();
53
+ return out;
54
+ }
55
+ /** Returns the resource records whose kind satisfies the slot. When the
56
+ * slot has a registry-resolvable `x-telo-ref` constraint, results are
57
+ * filtered to that abstract's implementations; otherwise (or when the
58
+ * user already typed a sibling `kind:`) they're filtered by an exact
59
+ * kind match. Falls back to listing every in-file resource so the
60
+ * user still sees something rather than nothing when the registry
61
+ * doesn't recognize the kind yet. */
62
+ function refNameCompletions(text, refKind, refConstraint, registry, valueStartColumn) {
63
+ const resources = extractInFileResources(text);
64
+ let acceptable;
65
+ if (refKind) {
66
+ acceptable = new Set([refKind]);
67
+ }
68
+ else if (refConstraint && registry) {
69
+ const kinds = registry.userFacingKindsForRef(refConstraint);
70
+ if (kinds)
71
+ acceptable = new Set(kinds);
72
+ }
73
+ const seen = new Set();
74
+ const out = [];
75
+ for (const r of resources) {
76
+ if (acceptable && !acceptable.has(r.kind))
77
+ continue;
78
+ if (seen.has(r.name))
79
+ continue;
80
+ seen.add(r.name);
81
+ out.push({
82
+ label: r.name,
83
+ kind: "value",
84
+ detail: r.kind,
85
+ // Anchor the replace range to the value's start column so names with
86
+ // `.`, `-`, or `/` (legal in resource names) replace the whole typed
87
+ // prefix instead of the trailing word VS Code would pick by default.
88
+ replaceFromColumn: valueStartColumn,
89
+ });
90
+ }
91
+ return out;
92
+ }
93
+ /** Resolve the kinds that satisfy the `x-telo-ref` slot at `parentDocKind` +
94
+ * `parentYamlPath`. Returns `undefined` (caller falls back to the full list)
95
+ * when there's no constraint, the path doesn't resolve, or the ref can't
96
+ * be resolved through the registry. */
97
+ function refConstrainedKinds(registry, parentDocKind, parentYamlPath) {
98
+ const definition = registry.resolveDefinition(parentDocKind);
99
+ if (!definition?.schema)
100
+ return undefined;
101
+ const refString = lookupRefConstraint(definition.schema, parentYamlPath);
102
+ if (!refString)
103
+ return undefined;
104
+ return registry.userFacingKindsForRef(refString);
105
+ }
106
+ function kindCompletions(registry, docKind, yamlPath, valueStartColumn) {
107
+ let kinds;
108
+ if (registry && docKind && yamlPath && yamlPath.length > 0) {
109
+ const filtered = refConstrainedKinds(registry, docKind, yamlPath);
110
+ kinds = filtered ?? registry.validUserFacingKinds();
111
+ }
112
+ else if (registry) {
113
+ kinds = registry.validUserFacingKinds();
114
+ }
115
+ else {
116
+ kinds = ["Telo.Application", "Telo.Library", "Telo.Import", "Telo.Definition"];
117
+ }
118
+ const seen = new Set();
119
+ const results = [];
120
+ for (const kind of kinds) {
121
+ if (seen.has(kind))
122
+ continue;
123
+ seen.add(kind);
124
+ const item = { label: kind, kind: "class", detail: "Telo resource kind" };
125
+ // Anchor the replace range to the value's start column so kinds with `.`
126
+ // (e.g. `Sql.Connection`) cleanly overwrite the existing prefix. Without
127
+ // this, VS Code's default word boundary stops at the last `.` and a pick
128
+ // of `Sql.Connection` while the buffer reads `Sql.Co|` becomes
129
+ // `Sql.Sql.Connection`.
130
+ if (valueStartColumn !== undefined)
131
+ item.replaceFromColumn = valueStartColumn;
132
+ results.push(item);
133
+ }
134
+ return results;
14
135
  }
15
136
  function capabilityCompletions() {
16
137
  return CAPABILITY_VALUES.map((cap) => ({
@@ -23,10 +144,18 @@ export async function buildCompletions(text, line, character, registry, adapter)
23
144
  const ctx = detectContext(text, line, character);
24
145
  if (!ctx)
25
146
  return [];
26
- if (ctx.type === "kind")
27
- return kindCompletions(registry);
147
+ if (ctx.type === "kind") {
148
+ return kindCompletions(registry, ctx.docKind, ctx.yamlPath, ctx.valueStartColumn);
149
+ }
28
150
  if (ctx.type === "capability")
29
151
  return capabilityCompletions();
152
+ if (ctx.type === "ref-name") {
153
+ const definition = registry?.resolveDefinition(ctx.docKind);
154
+ const refConstraint = definition?.schema
155
+ ? lookupRefConstraint(definition.schema, ctx.yamlPath)
156
+ : undefined;
157
+ return refNameCompletions(text, ctx.refKind, refConstraint, registry, ctx.valueStartColumn);
158
+ }
30
159
  if (ctx.type === "field-value") {
31
160
  if (ctx.docKind === "Telo.Import" && ctx.field === "source") {
32
161
  return importSourceCompletions(ctx.prefix, ctx.valueStartColumn, adapter);
@@ -1,5 +1,17 @@
1
1
  export type CompletionCtx = {
2
2
  type: "kind";
3
+ /** Set for indented `kind:` lines. The enclosing docKind + the YAML
4
+ * path to the parent of the `kind:` field (so the value slot's
5
+ * schema node can be looked up to discover `x-telo-ref` constraints).
6
+ * Absent for top-level `kind:` — there, no constraint applies. */
7
+ docKind?: string;
8
+ yamlPath?: string[];
9
+ /** Column where the kind value begins (after `kind:` + whitespace).
10
+ * Editor hosts use this to anchor the replace range so completions
11
+ * cleanly overwrite a kind that contains `.` (e.g. `Sql.Co|` →
12
+ * selecting `Sql.Connection` must replace `Sql.Co`, not just `Co`,
13
+ * which VS Code's default word range would). */
14
+ valueStartColumn: number;
3
15
  } | {
4
16
  type: "capability";
5
17
  } | {
@@ -7,6 +19,20 @@ export type CompletionCtx = {
7
19
  docKind: string;
8
20
  yamlPath: string[];
9
21
  existingKeys: Set<string>;
22
+ } | {
23
+ /** Cursor sits on the value of an object-form ref's `name:` field
24
+ * (e.g. `connection: { kind: Sql.Connection, name: |}`). Editor hosts
25
+ * use `refKind` (from the sibling `kind:` line) to filter the in-doc
26
+ * resource list to matching candidates. */
27
+ type: "ref-name";
28
+ docKind: string;
29
+ /** YAML path to the parent slot (e.g. `["connection"]`). The schema
30
+ * at this path declares the `x-telo-ref` constraint. */
31
+ yamlPath: string[];
32
+ /** The kind value of the sibling `kind:` line, if present. */
33
+ refKind?: string;
34
+ prefix: string;
35
+ valueStartColumn: number;
10
36
  } | {
11
37
  type: "field-value";
12
38
  docKind: string;
@@ -21,22 +47,52 @@ export declare function findDocBounds(lines: string[], cursorLine: number): {
21
47
  end: number;
22
48
  };
23
49
  export declare function extractKindFromDoc(lines: string[], start: number, end: number): string | undefined;
24
- export declare function extractRootKeys(lines: string[], start: number, end: number): Set<string>;
25
- /** Walk backward from cursorLine to build the chain of parent YAML keys. */
50
+ /** Collects every top-level key in the doc bounds, skipping `skipLine` so the
51
+ * cursor's own line is treated as "being edited" its key (if any) stays in
52
+ * the suggestion list. Without this the user can't autocomplete an existing
53
+ * key from its own line (e.g. `ver|sion:`). */
54
+ export declare function extractRootKeys(lines: string[], start: number, end: number, skipLine?: number): Set<string>;
55
+ /** Walk backward from cursorLine to build the chain of parent YAML keys.
56
+ *
57
+ * List-item handling (` - request:` style): the `-` marker sits at the
58
+ * line's textual indent, but the key after it (`request`) lives at indent
59
+ * `+2`. Whether that post-dash key joins the path depends on the cursor's
60
+ * descent:
61
+ * - When the cursor's current target indent is GREATER than the post-dash
62
+ * key's column, the descent passes through that key (e.g. cursor inside
63
+ * `request.method` at indent 6, key `request` at column 4) → push it.
64
+ * - When the cursor's current target indent EQUALS the post-dash key's
65
+ * column, the post-dash key is a sibling at the list-item level
66
+ * (e.g. cursor on `handler:` at indent 4, key `request:` at column 4) →
67
+ * skip it; descend straight to the array's parent.
68
+ *
69
+ * In both cases the next walk step targets `lineIndent` so the `routes:` /
70
+ * `steps:` parent of the array is captured. The schema walker auto-descends
71
+ * arrays, so no `[]` marker is appended. */
26
72
  export declare function buildYamlPath(lines: string[], cursorLine: number, docStart: number, cursorIndent: number): string[];
27
- /** Extract sibling keys already present at `indent` within the doc bounds. */
28
- export declare function extractKeysAtIndent(lines: string[], start: number, end: number, indent: number): Set<string>;
29
- /** Navigate a JSON Schema hierarchy following `path`, auto-descending into array items. */
73
+ /** Extract sibling keys already present at `indent` within the doc bounds.
74
+ * `skipLine` lets the caller exclude the cursor's own line so a key being
75
+ * edited (`ver|sion:`) doesn't filter itself out of the suggestion list. */
76
+ export declare function extractKeysAtIndent(lines: string[], start: number, end: number, indent: number, skipLine?: number): Set<string>;
77
+ /** Navigate a JSON Schema hierarchy following `path`, auto-descending into
78
+ * array items and peeling `anyOf` / `oneOf` branches. When multiple peeled
79
+ * branches define `properties`, returns a synthetic node whose `properties`
80
+ * is the union (first-wins on key collision) and whose `required` is the
81
+ * intersection — enough for propKeyCompletions to surface every key a value
82
+ * at this slot can legally carry. */
30
83
  export declare function navigateSchema(schema: Record<string, any>, path: string[]): Record<string, any> | undefined;
31
- /**
32
- * For blank lines (and lines with only whitespace), infer the intended indent
33
- * from context rather than relying on the cursor column, which is often 0
34
- * even when the cursor is semantically inside a nested block.
35
- *
36
- * Strategy: look at the previous non-empty line.
37
- * - If it ends with `:` (bare object key, no value) cursor is one level deeper.
38
- * - Otherwise cursor is a sibling of that key (same indent).
39
- */
40
- export declare function inferIndentForBlankLine(lines: string[], cursorLine: number, docStart: number): number;
84
+ /** Walks up and down from `cursorLine` looking for a sibling line at the
85
+ * exact same indent whose key is `kind`. The value of the first such line
86
+ * is returned (alias form, e.g. `"Sql.Connection"`). Used by ref-name
87
+ * completion to discover what kind of resource the user is targeting in an
88
+ * object-form ref. Walking stops at the first line with a strictly smaller
89
+ * indent (that's the parent's structural boundary). */
90
+ export declare function findSiblingKindValue(lines: string[], docStart: number, docEnd: number, cursorLine: number, indent: number): string | undefined;
91
+ /** Looks up the `x-telo-ref` string carried by the schema node at `yamlPath`
92
+ * inside `definitionSchema`. Checks both the property node directly and its
93
+ * peeled `anyOf` / `oneOf` branches, since some library schemas place the
94
+ * annotation at the property level and others inside a branch. Returns
95
+ * `undefined` when the path doesn't resolve or no ref constraint is declared. */
96
+ export declare function lookupRefConstraint(definitionSchema: Record<string, any>, yamlPath: string[]): string | undefined;
41
97
  export declare function detectContext(text: string, line: number, character: number): CompletionCtx | undefined;
42
98
  //# sourceMappingURL=detect-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;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,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,mFAAmF;IACnF,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAgBjG;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMlG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAOxF;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,EAAE,CA2BV;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,CAAC,CAab;AAED,2FAA2F;AAC3F,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,CAgBjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYrG;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,aAAa,GAAG,SAAS,CAqD3B"}
1
+ {"version":3,"file":"detect-context.d.ts","sourceRoot":"","sources":["../../src/completions/detect-context.ts"],"names":[],"mappings":"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"}