@telorun/ide-support 0.5.0 → 0.7.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.
Files changed (53) hide show
  1. package/dist/completions/build.d.ts +2 -2
  2. package/dist/completions/build.d.ts.map +1 -1
  3. package/dist/completions/build.js +39 -64
  4. package/dist/completions/detect-context.d.ts +16 -43
  5. package/dist/completions/detect-context.d.ts.map +1 -1
  6. package/dist/completions/detect-context.js +63 -262
  7. package/dist/completions/import-source.d.ts +12 -8
  8. package/dist/completions/import-source.d.ts.map +1 -1
  9. package/dist/completions/import-source.js +67 -49
  10. package/dist/completions/resolve-node.d.ts +50 -0
  11. package/dist/completions/resolve-node.d.ts.map +1 -0
  12. package/dist/completions/resolve-node.js +269 -0
  13. package/dist/completions/valid-capabilities.d.ts +3 -0
  14. package/dist/completions/valid-capabilities.d.ts.map +1 -1
  15. package/dist/completions/valid-capabilities.js +10 -0
  16. package/dist/definition/build-definition.d.ts +13 -0
  17. package/dist/definition/build-definition.d.ts.map +1 -0
  18. package/dist/definition/build-definition.js +98 -0
  19. package/dist/definition/index.d.ts +2 -0
  20. package/dist/definition/index.d.ts.map +1 -0
  21. package/dist/definition/index.js +1 -0
  22. package/dist/hover/build-hover.d.ts +4 -0
  23. package/dist/hover/build-hover.d.ts.map +1 -0
  24. package/dist/hover/build-hover.js +125 -0
  25. package/dist/hover/index.d.ts +2 -0
  26. package/dist/hover/index.d.ts.map +1 -0
  27. package/dist/hover/index.js +1 -0
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -0
  31. package/dist/semantic-tokens/build-semantic-tokens.d.ts +12 -0
  32. package/dist/semantic-tokens/build-semantic-tokens.d.ts.map +1 -0
  33. package/dist/semantic-tokens/build-semantic-tokens.js +55 -0
  34. package/dist/semantic-tokens/index.d.ts +2 -0
  35. package/dist/semantic-tokens/index.d.ts.map +1 -0
  36. package/dist/semantic-tokens/index.js +1 -0
  37. package/dist/types.d.ts +66 -18
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +3 -0
  40. package/package.json +2 -2
  41. package/src/completions/build.ts +40 -72
  42. package/src/completions/detect-context.ts +74 -291
  43. package/src/completions/import-source.ts +71 -53
  44. package/src/completions/resolve-node.ts +405 -0
  45. package/src/completions/valid-capabilities.ts +11 -0
  46. package/src/definition/build-definition.ts +123 -0
  47. package/src/definition/index.ts +1 -0
  48. package/src/hover/build-hover.ts +149 -0
  49. package/src/hover/index.ts +1 -0
  50. package/src/index.ts +3 -0
  51. package/src/semantic-tokens/build-semantic-tokens.ts +64 -0
  52. package/src/semantic-tokens/index.ts +1 -0
  53. package/src/types.ts +78 -18
@@ -0,0 +1,98 @@
1
+ import { parseToAst, } from "@telorun/analyzer";
2
+ import { resolveNodeAtPosition } from "../completions/resolve-node.js";
3
+ /** The module whose owner or partials includes `filePath`. */
4
+ function moduleForFile(graph, filePath) {
5
+ for (const mod of graph.modules.values()) {
6
+ if (mod.owner.source === filePath)
7
+ return mod;
8
+ if (mod.partials.some((p) => p.source === filePath))
9
+ return mod;
10
+ }
11
+ return undefined;
12
+ }
13
+ /** First resource named `name` across `files`, located at its `metadata.name`
14
+ * (or its first line as a fallback). Names are unique within a module scope, so
15
+ * the first hit is the definition. */
16
+ function locateResource(files, name) {
17
+ for (const file of files) {
18
+ for (let i = 0; i < file.manifests.length; i++) {
19
+ const manifest = file.manifests[i];
20
+ if (!manifest || manifest.metadata?.name !== name)
21
+ continue;
22
+ const pos = file.positions[i];
23
+ const range = pos?.positionIndex.get("metadata.name") ??
24
+ pos?.positionIndex.get("@key:metadata.name") ??
25
+ (pos ? { start: { line: pos.sourceLine, character: 0 }, end: { line: pos.sourceLine, character: 0 } } : undefined);
26
+ if (range)
27
+ return { uri: file.source, range };
28
+ }
29
+ }
30
+ return undefined;
31
+ }
32
+ /** The `exports.resources` list of a module's owner doc (empty when absent). */
33
+ function exportedResources(mod) {
34
+ const doc = mod.owner.manifests.find((m) => m?.kind === "Telo.Library" || m?.kind === "Telo.Application");
35
+ const list = doc?.exports?.resources;
36
+ return Array.isArray(list) ? list.filter((e) => typeof e === "string") : [];
37
+ }
38
+ /** Follow `name` into `moduleSource` across the export boundary, honoring the
39
+ * `exports.resources` gate and re-export chains (`app → api → domain → …`). A
40
+ * terminal match is a locally-owned instance the module actually exports; a
41
+ * re-export entry `InnerAlias.name` hops through that module's own import edge.
42
+ * `seen` bounds cyclic import graphs. */
43
+ function resolveExported(graph, moduleSource, name, seen = new Set()) {
44
+ if (seen.has(moduleSource))
45
+ return undefined;
46
+ seen.add(moduleSource);
47
+ const mod = graph.modules.get(moduleSource);
48
+ if (!mod)
49
+ return undefined;
50
+ const exports = exportedResources(mod);
51
+ // No `exports.resources` block → ungated (the module hasn't opted into the
52
+ // gate, same as `exports.kinds`): a plain name match keeps navigation working
53
+ // for modules that predate explicit exports.
54
+ if (exports.length === 0) {
55
+ return locateResource([mod.owner, ...mod.partials], name);
56
+ }
57
+ if (exports.includes(name)) {
58
+ const local = locateResource([mod.owner, ...mod.partials], name);
59
+ if (local)
60
+ return local;
61
+ }
62
+ const reexport = exports.find((e) => e.endsWith(`.${name}`));
63
+ if (!reexport)
64
+ return undefined;
65
+ const innerAlias = reexport.slice(0, reexport.length - name.length - 1);
66
+ const edge = graph.importEdges.get(mod.owner.source)?.get(innerAlias);
67
+ return edge ? resolveExported(graph, edge.targetSource, name, seen) : undefined;
68
+ }
69
+ /** Resolve the `!ref` under the cursor to its target resource's definition.
70
+ *
71
+ * The ref grammar mirrors `resolveRefSentinels`: the tag's value is split on
72
+ * the first dot — a bare name (or `Self.name`) is a local resource in the
73
+ * current module; `Alias.name` is an exported instance of the module the import
74
+ * `Alias` points at, followed transitively through re-exports and gated on each
75
+ * module's `exports.resources`. Returns `undefined` when the cursor isn't on a
76
+ * `!ref`, or the target can't be found (e.g. a scope-local name, an unexported
77
+ * instance, or an import that failed to load). */
78
+ export function buildDefinition(text, line, character, graph, currentFilePath, docs) {
79
+ const astDocs = docs ?? parseToAst(text);
80
+ const node = resolveNodeAtPosition(text, astDocs, line, character)?.node;
81
+ if (!node || node.kind !== "scalar" || node.tag !== "!ref")
82
+ return undefined;
83
+ // The scalar's range covers the ref target text (the value after `!ref`).
84
+ const source = text.slice(node.range[0], node.range[1]).trim();
85
+ if (!source)
86
+ return undefined;
87
+ const currentModule = moduleForFile(graph, currentFilePath) ?? graph.entry;
88
+ const dot = source.indexOf(".");
89
+ const alias = dot === -1 ? undefined : source.slice(0, dot);
90
+ const name = dot === -1 ? source : source.slice(dot + 1);
91
+ if (alias === undefined || alias === "Self") {
92
+ return locateResource([currentModule.owner, ...currentModule.partials], name);
93
+ }
94
+ const edge = graph.importEdges.get(currentModule.owner.source)?.get(alias);
95
+ if (!edge)
96
+ return undefined;
97
+ return resolveExported(graph, edge.targetSource, name);
98
+ }
@@ -0,0 +1,2 @@
1
+ export { buildDefinition } from "./build-definition.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/definition/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export { buildDefinition } from "./build-definition.js";
@@ -0,0 +1,4 @@
1
+ import { type AnalysisRegistry, type AstDocument } from "@telorun/analyzer";
2
+ import type { HoverResult } from "../types.js";
3
+ export declare function buildHover(text: string, line: number, character: number, registry: AnalysisRegistry | undefined, docs?: AstDocument[]): HoverResult | undefined;
4
+ //# sourceMappingURL=build-hover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-hover.d.ts","sourceRoot":"","sources":["../../src/hover/build-hover.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoF/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,GACnB,WAAW,GAAG,SAAS,CAOzB"}
@@ -0,0 +1,125 @@
1
+ import { parseToAst, } from "@telorun/analyzer";
2
+ import { navigateSchema } from "../completions/detect-context.js";
3
+ import { resolveNodeAtPosition, scalarString, } from "../completions/resolve-node.js";
4
+ import { CAPABILITY_DOCS } from "../completions/valid-capabilities.js";
5
+ /** Docs for the structural keys shared by every module doc, so hover is useful
6
+ * even at the root, where there is no user-authored schema to navigate. */
7
+ const STRUCTURAL_KEY_DOCS = {
8
+ kind: "The resource kind — `Alias.Name` for an imported kind, or a `Telo.*` root kind.",
9
+ metadata: "Resource identity: `name` (kebab-case, dot-free) and optional `namespace`.",
10
+ imports: "Dependency map: PascalCase alias → `namespace/name@version` source string or object.",
11
+ targets: "Boot sequence run after init — references to `Runnable`/`Service` resources or inline invoke steps.",
12
+ variables: "Typed inputs bound from host env vars (`env:` + JSON-Schema `type:`).",
13
+ secrets: "Secret inputs bound from host env vars (`env:` + `type:`).",
14
+ ports: "Inbound ports the app listens on, each bound to a host env var (Application only).",
15
+ exports: "What importers may reference: `kinds` (kind gate) and `resources` (instance singletons).",
16
+ include: "Partial files loaded into this module scope (paths / globs).",
17
+ capability: "The lifecycle role of the kind this definition registers.",
18
+ schema: "JSON Schema for the kind's config fields, with `x-telo-*` annotations.",
19
+ extends: "Alias-form kind this definition specializes (abstract contract or concrete parent).",
20
+ base: "Construction mapping (`super(...)`) over `self` for a concrete-`extends` definition.",
21
+ controllers: "Controller locator (`pkg:npm`) implementing this kind.",
22
+ };
23
+ /** The kind value of the map that directly encloses `keyName` in the value slot. */
24
+ function typeName(t) {
25
+ if (typeof t === "string")
26
+ return t;
27
+ if (t && typeof t === "object" && typeof t.title === "string")
28
+ return t.title;
29
+ return undefined;
30
+ }
31
+ function kindHover(kind, def) {
32
+ if (!def)
33
+ return `\`${kind}\``;
34
+ const lines = [`### ${kind}`];
35
+ const role = def.capability ? `\`${def.capability}\`` : "resource";
36
+ const module = def.metadata?.module ? ` · module \`${def.metadata.module}\`` : "";
37
+ lines.push(`${role}${module}`);
38
+ const schema = def.schema;
39
+ const desc = schema?.description ?? schema?.title;
40
+ if (typeof desc === "string" && desc)
41
+ lines.push("", desc);
42
+ if (def.extends)
43
+ lines.push("", `Extends \`${def.extends}\``);
44
+ const input = typeName(def.inputType);
45
+ const output = typeName(def.outputType);
46
+ if (input)
47
+ lines.push(`Input \`${input}\``);
48
+ if (output)
49
+ lines.push(`Output \`${output}\``);
50
+ return lines.join("\n");
51
+ }
52
+ function fieldHover(keyName, field) {
53
+ const lines = [];
54
+ const type = Array.isArray(field.type) ? field.type.join(" | ") : field.type;
55
+ const head = type ? `**${keyName}**: \`${type}\`` : `**${keyName}**`;
56
+ lines.push(head);
57
+ if (typeof field.description === "string" && field.description) {
58
+ lines.push("", field.description);
59
+ }
60
+ const ref = field["x-telo-ref"];
61
+ if (typeof ref === "string")
62
+ lines.push("", `Reference → \`${ref}\``);
63
+ if (Array.isArray(field.enum) && field.enum.length > 0) {
64
+ lines.push("", `Allowed: ${field.enum.map((v) => `\`${v}\``).join(", ")}`);
65
+ }
66
+ if (field.default !== undefined)
67
+ lines.push(`Default: \`${JSON.stringify(field.default)}\``);
68
+ return lines.length > 0 ? lines.join("\n") : `**${keyName}**`;
69
+ }
70
+ /** Field schema at the nearest enclosing resource, or undefined when the scope
71
+ * can't be resolved (no kind-bearing ancestor, or the path doesn't navigate). */
72
+ function fieldSchemaFor(resourceKind, relativePath, registry) {
73
+ if (!resourceKind || !registry)
74
+ return undefined;
75
+ const def = registry.resolveDefinition(resourceKind);
76
+ if (!def?.schema)
77
+ return undefined;
78
+ return navigateSchema(def.schema, relativePath);
79
+ }
80
+ export function buildHover(text, line, character, registry, docs) {
81
+ const astDocs = docs ?? parseToAst(text);
82
+ const resolved = resolveNodeAtPosition(text, astDocs, line, character);
83
+ if (!resolved)
84
+ return undefined;
85
+ if (resolved.slot === "value")
86
+ return hoverForValue(resolved, registry);
87
+ return hoverForKey(resolved, registry);
88
+ }
89
+ function hoverForValue(resolved, registry) {
90
+ const key = resolved.path[resolved.path.length - 1];
91
+ const value = scalarString(resolved.node);
92
+ const range = resolved.replaceRange;
93
+ if (key === "kind" && value) {
94
+ return { contents: kindHover(value, registry?.resolveDefinition(value)), range };
95
+ }
96
+ if (key === "capability" && resolved.docKind === "Telo.Definition" && value) {
97
+ const doc = CAPABILITY_DOCS[value];
98
+ return doc ? { contents: `**${value}**\n\n${doc}`, range } : undefined;
99
+ }
100
+ // Field value: describe the field via the enclosing resource's schema. Works
101
+ // when the value sits directly under a kind-bearing map (`siblingKind`); the
102
+ // field path relative to that map is just the key.
103
+ if (key) {
104
+ const field = fieldSchemaFor(resolved.siblingKind, [key], registry);
105
+ if (field)
106
+ return { contents: fieldHover(key, field), range };
107
+ }
108
+ return undefined;
109
+ }
110
+ function hoverForKey(resolved, registry) {
111
+ const keyName = scalarString(resolved.node);
112
+ if (!keyName)
113
+ return undefined;
114
+ const range = resolved.replaceRange;
115
+ const resourceKind = resolved.resourceKind ?? resolved.docKind;
116
+ const relativePath = [...resolved.path.slice(resolved.resourceDepth ?? 0), keyName];
117
+ const field = fieldSchemaFor(resourceKind, relativePath, registry);
118
+ if (field)
119
+ return { contents: fieldHover(keyName, field), range };
120
+ const structural = STRUCTURAL_KEY_DOCS[keyName];
121
+ if (structural && relativePath.length === 1) {
122
+ return { contents: `**${keyName}**\n\n${structural}`, range };
123
+ }
124
+ return undefined;
125
+ }
@@ -0,0 +1,2 @@
1
+ export { buildHover } from "./build-hover.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ export { buildHover } from "./build-hover.js";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  export * from "./types.js";
2
2
  export * from "./completions/index.js";
3
3
  export * from "./diagnostics/index.js";
4
+ export * from "./hover/index.js";
5
+ export * from "./semantic-tokens/index.js";
6
+ export * from "./definition/index.js";
4
7
  //# sourceMappingURL=index.d.ts.map
@@ -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"}
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"}
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
1
  export * from "./types.js";
2
2
  export * from "./completions/index.js";
3
3
  export * from "./diagnostics/index.js";
4
+ export * from "./hover/index.js";
5
+ export * from "./semantic-tokens/index.js";
6
+ export * from "./definition/index.js";
@@ -0,0 +1,12 @@
1
+ import { type AnalysisRegistry, type AstDocument } from "@telorun/analyzer";
2
+ import type { SemanticToken } from "../types.js";
3
+ /** Registry-aware semantic tokens: a `kind:` value that resolves to a known
4
+ * definition is a `type`; a `capability:` value is an `interface`; a `!ref`
5
+ * target is a `variable`. Everything else (structure, CEL, tags) is left to the
6
+ * TextMate grammar. Ref targets are colored here rather than in the grammar
7
+ * because a `!ref` after a `key:` is tokenized by the bundled YAML grammar
8
+ * before a Telo pattern can claim it — the AST sees it unambiguously. An
9
+ * unresolved kind gets no token, so a typo stays uncolored — a quiet signal
10
+ * that pairs with the analyzer's `UNDEFINED_KIND` diagnostic. */
11
+ export declare function buildSemanticTokens(text: string, registry: AnalysisRegistry | undefined, docs?: AstDocument[]): SemanticToken[];
12
+ //# sourceMappingURL=build-semantic-tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-semantic-tokens.d.ts","sourceRoot":"","sources":["../../src/semantic-tokens/build-semantic-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAEjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD;;;;;;;kEAOkE;AAClE,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,GAAG,SAAS,EACtC,IAAI,CAAC,EAAE,WAAW,EAAE,GACnB,aAAa,EAAE,CAqCjB"}
@@ -0,0 +1,55 @@
1
+ import { buildLineOffsets, offsetToPosition, parseToAst, } from "@telorun/analyzer";
2
+ import { scalarString } from "../completions/resolve-node.js";
3
+ import { CAPABILITY_VALUES } from "../completions/valid-capabilities.js";
4
+ const CAPABILITIES = new Set(CAPABILITY_VALUES);
5
+ /** Registry-aware semantic tokens: a `kind:` value that resolves to a known
6
+ * definition is a `type`; a `capability:` value is an `interface`; a `!ref`
7
+ * target is a `variable`. Everything else (structure, CEL, tags) is left to the
8
+ * TextMate grammar. Ref targets are colored here rather than in the grammar
9
+ * because a `!ref` after a `key:` is tokenized by the bundled YAML grammar
10
+ * before a Telo pattern can claim it — the AST sees it unambiguously. An
11
+ * unresolved kind gets no token, so a typo stays uncolored — a quiet signal
12
+ * that pairs with the analyzer's `UNDEFINED_KIND` diagnostic. */
13
+ export function buildSemanticTokens(text, registry, docs) {
14
+ const astDocs = docs ?? parseToAst(text);
15
+ const lineOffsets = buildLineOffsets(text);
16
+ const tokens = [];
17
+ const emit = (node, type) => {
18
+ if (!node)
19
+ return;
20
+ const start = offsetToPosition(node.range[0], lineOffsets);
21
+ const end = offsetToPosition(node.range[1], lineOffsets);
22
+ // Kind / capability values never span lines; a clamped single-line token.
23
+ if (start.line !== end.line)
24
+ return;
25
+ tokens.push({ line: start.line, character: start.character, length: end.character - start.character, type });
26
+ };
27
+ const walk = (node) => {
28
+ if (node.kind === "map") {
29
+ for (const pair of node.entries) {
30
+ const key = scalarString(pair.key);
31
+ const value = scalarString(pair.value);
32
+ if (key === "kind" && value && registry?.resolveDefinition(value)) {
33
+ emit(pair.value, "type");
34
+ }
35
+ else if (key === "capability" && value && CAPABILITIES.has(value)) {
36
+ emit(pair.value, "interface");
37
+ }
38
+ if (pair.value)
39
+ walk(pair.value);
40
+ }
41
+ }
42
+ else if (node.kind === "seq") {
43
+ for (const item of node.items)
44
+ walk(item);
45
+ }
46
+ else if (node.kind === "scalar" && node.tag === "!ref") {
47
+ emit(node, "variable");
48
+ }
49
+ };
50
+ for (const doc of astDocs) {
51
+ if (doc.root)
52
+ walk(doc.root);
53
+ }
54
+ return tokens;
55
+ }
@@ -0,0 +1,2 @@
1
+ export { buildSemanticTokens } from "./build-semantic-tokens.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/semantic-tokens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1 @@
1
+ export { buildSemanticTokens } from "./build-semantic-tokens.js";
package/dist/types.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  export { AnalysisRegistry, DiagnosticSeverity } from "@telorun/analyzer";
2
2
  export type { Position, Range, AnalysisDiagnostic, PositionIndex, } from "@telorun/analyzer";
3
- import type { AnalysisRegistry, DiagnosticSeverity, PositionIndex, Range } from "@telorun/analyzer";
3
+ import type { AnalysisRegistry, DiagnosticSeverity, Position, PositionIndex, Range } from "@telorun/analyzer";
4
4
  export type CompletionKind = "class" | "enumMember" | "property" | "folder" | "module" | "value";
5
+ /** A source span the host replaces wholesale when a completion is accepted. */
6
+ export interface ReplaceRange {
7
+ start: Position;
8
+ end: Position;
9
+ }
5
10
  export interface CompletionResult {
6
11
  label: string;
7
12
  kind: CompletionKind;
@@ -12,22 +17,62 @@ export interface CompletionResult {
12
17
  preselect?: boolean;
13
18
  sortText?: string;
14
19
  filterText?: string;
15
- /** When set, the host should replace text from this 0-based column on the
16
- * cursor's line up to the cursor. Required when the completion value
17
- * contains non-word characters (`/`, `@`, `.`) that the host's default
18
- * word boundary would not include in the replaced range. */
19
- replaceFromColumn?: number;
20
+ /** When set, the host replaces this whole source range with the accepted
21
+ * value the full span of the existing node, not just the prefix up to the
22
+ * cursor. This overwrites any suffix after the cursor (`Sql.Co|nnection` +
23
+ * `Sql.Connection` no leftover `nnection`) and cleanly replaces values
24
+ * containing non-word characters (`/`, `@`, `.`). A zero-width range is a
25
+ * pure insert. */
26
+ replaceRange?: ReplaceRange;
27
+ }
28
+ /** Rendered hover for the symbol under the cursor. `contents` is GitHub-flavored
29
+ * markdown; `range` (when present) is the source span the host underlines. */
30
+ export interface HoverResult {
31
+ contents: string;
32
+ range?: ReplaceRange;
33
+ }
34
+ /** Semantic token type names emitted by `buildSemanticTokens`. Kept to the
35
+ * standard VS Code / LSP set so hosts register them against a stock legend and
36
+ * every theme colors them without extra configuration. `type` marks a resolved
37
+ * resource kind; `interface` marks a capability value; `variable` marks a
38
+ * `!ref` target. */
39
+ export type SemanticTokenType = "type" | "interface" | "variable";
40
+ /** The legend a host registers before mapping `buildSemanticTokens` output. The
41
+ * numeric token-type of each `SemanticToken` is its index in this array. */
42
+ export declare const SEMANTIC_TOKEN_LEGEND: readonly SemanticTokenType[];
43
+ /** One absolute-positioned semantic token. Every Telo semantic token is
44
+ * single-line (kinds and capabilities never wrap), so a `{line, char, length}`
45
+ * triple is sufficient; the host encodes it into its own builder. */
46
+ export interface SemanticToken {
47
+ line: number;
48
+ character: number;
49
+ length: number;
50
+ type: SemanticTokenType;
51
+ }
52
+ /** Where a `!ref` target is defined — for go-to-definition. `uri` is the
53
+ * target file's canonical source (absolute path for local files, an http/oci
54
+ * URL for a registry import); `range` spans the target resource's
55
+ * `metadata.name` (falling back to its first line). */
56
+ export interface DefinitionResult {
57
+ uri: string;
58
+ range: Range;
20
59
  }
21
- export interface RegistryModule {
22
- namespace: string;
23
- name: string;
24
- version: string;
60
+ /** A candidate module ref surfaced by the hub's `/refs` lexical autocomplete.
61
+ * Identity is the location ref, never `namespace/name` — an OCI module has no
62
+ * addressable `namespace/name`. `latestVersion` seeds a pinned `ref@version`
63
+ * insert so a picked completion is directly usable. */
64
+ export interface HubRef {
65
+ ref: string;
66
+ latestVersion: string;
25
67
  description?: string;
26
68
  }
27
69
  /** Host-supplied bridge that lets ide-support reach the filesystem and the
28
- * module registry without depending on Node, Tauri, or vscode APIs. Each
70
+ * federated telo hub without depending on Node, Tauri, or vscode APIs. Each
29
71
  * host (VSCode extension, Telo editor) builds an adapter scoped to the
30
- * currently-edited manifest before calling `buildCompletions`. */
72
+ * currently-edited manifest before calling `buildCompletions`. Hub lookups are
73
+ * ref-keyed: the hub aggregates modules across every transport (OCI, HTTP,
74
+ * direct URL), so completion speaks its `/refs` + `/module/versions` verbs
75
+ * rather than any single registry. */
31
76
  export interface IdeEnvironmentAdapter {
32
77
  /** Subdirectory names within `relPath` (resolved against the manifest's
33
78
  * directory). Returns [] if the path doesn't exist or isn't a directory.
@@ -36,12 +81,15 @@ export interface IdeEnvironmentAdapter {
36
81
  /** True iff `<relPath>/telo.yaml` exists relative to the manifest dir.
37
82
  * Used to mark directories that are valid import targets. */
38
83
  hasManifest(relPath: string): Promise<boolean>;
39
- /** Free-text search against the configured module registry. Matches against
40
- * name, namespace, and description. Empty `query` should return the full
41
- * (capped) catalog. */
42
- searchRegistry(query: string): Promise<RegistryModule[]>;
43
- /** All published versions for a module, newest first. */
44
- listRegistryVersions(namespace: string, name: string): Promise<string[]>;
84
+ /** Fuzzy lexical ref autocomplete against the configured telo hub
85
+ * (`GET /refs?q=`). The query is matched as a substring over every
86
+ * registered ref, so a bare token (`youtrack`) hits the same ref as its full
87
+ * `oci://…` form. Best-effort — hosts swallow network errors and return []. */
88
+ searchRefs(query: string): Promise<HubRef[]>;
89
+ /** All tracked versions for a location ref, newest first
90
+ * (`GET /module/versions?ref=`). The browser cannot call OCI `tags/list`;
91
+ * the hub holds them from ingest. */
92
+ listVersionsForRef(ref: string): Promise<string[]>;
45
93
  }
46
94
  export interface NormalizedDiagnostic {
47
95
  range: Range;
@@ -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,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAEpG,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjG,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;;;iEAG6D;IAC7D,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;mEAGmE;AACnE,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;;4BAEwB;IACxB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACzD,yDAAyD;IACzD,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1E;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,cAAc,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE;;;0EAGsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -1,3 +1,6 @@
1
1
  // Runtime values (classes, enums) — consumers who need `new AnalysisRegistry()`
2
2
  // or `DiagnosticSeverity.Error` import from here.
3
3
  export { AnalysisRegistry, DiagnosticSeverity } from "@telorun/analyzer";
4
+ /** The legend a host registers before mapping `buildSemanticTokens` output. The
5
+ * numeric token-type of each `SemanticToken` is its index in this array. */
6
+ export const SEMANTIC_TOKEN_LEGEND = ["type", "interface", "variable"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/ide-support",
3
- "version": "0.5.0",
3
+ "version": "0.7.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.40.0"
39
+ "@telorun/analyzer": "0.41.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^20.0.0",