@rejot-dev/thalo-lsp 0.0.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/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/capabilities.d.ts +12 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +54 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/handlers/completions/completions.js +33 -0
- package/dist/handlers/completions/completions.js.map +1 -0
- package/dist/handlers/completions/context.js +228 -0
- package/dist/handlers/completions/context.js.map +1 -0
- package/dist/handlers/completions/providers/directive.js +50 -0
- package/dist/handlers/completions/providers/directive.js.map +1 -0
- package/dist/handlers/completions/providers/entity.js +52 -0
- package/dist/handlers/completions/providers/entity.js.map +1 -0
- package/dist/handlers/completions/providers/link.js +113 -0
- package/dist/handlers/completions/providers/link.js.map +1 -0
- package/dist/handlers/completions/providers/metadata-key.js +43 -0
- package/dist/handlers/completions/providers/metadata-key.js.map +1 -0
- package/dist/handlers/completions/providers/metadata-value.js +71 -0
- package/dist/handlers/completions/providers/metadata-value.js.map +1 -0
- package/dist/handlers/completions/providers/providers.js +31 -0
- package/dist/handlers/completions/providers/providers.js.map +1 -0
- package/dist/handlers/completions/providers/schema-block.js +46 -0
- package/dist/handlers/completions/providers/schema-block.js.map +1 -0
- package/dist/handlers/completions/providers/section.js +37 -0
- package/dist/handlers/completions/providers/section.js.map +1 -0
- package/dist/handlers/completions/providers/tag.js +55 -0
- package/dist/handlers/completions/providers/tag.js.map +1 -0
- package/dist/handlers/completions/providers/timestamp.js +32 -0
- package/dist/handlers/completions/providers/timestamp.js.map +1 -0
- package/dist/handlers/completions/providers/type-expr.js +56 -0
- package/dist/handlers/completions/providers/type-expr.js.map +1 -0
- package/dist/handlers/definition.js +166 -0
- package/dist/handlers/definition.js.map +1 -0
- package/dist/handlers/diagnostics.js +77 -0
- package/dist/handlers/diagnostics.js.map +1 -0
- package/dist/handlers/hover.js +73 -0
- package/dist/handlers/hover.js.map +1 -0
- package/dist/handlers/references.js +233 -0
- package/dist/handlers/references.js.map +1 -0
- package/dist/handlers/semantic-tokens.js +36 -0
- package/dist/handlers/semantic-tokens.js.map +1 -0
- package/dist/mod.d.ts +2 -0
- package/dist/mod.js +3 -0
- package/dist/server.bundled.js +1764 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +367 -0
- package/dist/server.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ALL_DIRECTIVES, INSTANCE_DIRECTIVES, SCHEMA_DIRECTIVES } from "@rejot-dev/thalo";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/completions/providers/directive.ts
|
|
4
|
+
/**
|
|
5
|
+
* Get description for a directive.
|
|
6
|
+
*/
|
|
7
|
+
function getDirectiveDescription(directive) {
|
|
8
|
+
switch (directive) {
|
|
9
|
+
case "create": return "Create a new instance entry (lore, opinion, reference, journal)";
|
|
10
|
+
case "update": return "Update an existing entry (reference a previous entry with supersedes:)";
|
|
11
|
+
case "define-entity": return "Define a new entity schema with fields and sections";
|
|
12
|
+
case "alter-entity": return "Modify an existing entity schema (add/remove fields or sections)";
|
|
13
|
+
case "define-synthesis": return "Define a synthesis operation that queries entries and generates content via LLM";
|
|
14
|
+
case "actualize-synthesis": return "Trigger a synthesis to regenerate its output based on current data";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get detail text for a directive.
|
|
19
|
+
*/
|
|
20
|
+
function getDirectiveDetail(directive) {
|
|
21
|
+
if (INSTANCE_DIRECTIVES.includes(directive)) return "Instance directive";
|
|
22
|
+
if (SCHEMA_DIRECTIVES.includes(directive)) return "Schema directive";
|
|
23
|
+
return "Synthesis directive";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provider for directive completion after timestamp.
|
|
27
|
+
*/
|
|
28
|
+
const directiveProvider = {
|
|
29
|
+
name: "directive",
|
|
30
|
+
contextKinds: ["after_timestamp"],
|
|
31
|
+
getCompletions(ctx, _workspace) {
|
|
32
|
+
const partial = ctx.partial.toLowerCase();
|
|
33
|
+
return ALL_DIRECTIVES.filter((d) => !partial || d.toLowerCase().startsWith(partial)).map((directive, index) => ({
|
|
34
|
+
label: directive,
|
|
35
|
+
kind: 14,
|
|
36
|
+
detail: getDirectiveDetail(directive),
|
|
37
|
+
documentation: {
|
|
38
|
+
kind: "markdown",
|
|
39
|
+
value: getDirectiveDescription(directive)
|
|
40
|
+
},
|
|
41
|
+
insertText: `${directive} `,
|
|
42
|
+
sortText: String(index),
|
|
43
|
+
filterText: directive
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { directiveProvider };
|
|
50
|
+
//# sourceMappingURL=directive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directive.js","names":["directiveProvider: CompletionProvider"],"sources":["../../../../src/handlers/completions/providers/directive.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport {\n ALL_DIRECTIVES,\n INSTANCE_DIRECTIVES,\n SCHEMA_DIRECTIVES,\n type Directive,\n} from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Get description for a directive.\n */\nfunction getDirectiveDescription(directive: Directive): string {\n switch (directive) {\n case \"create\":\n return \"Create a new instance entry (lore, opinion, reference, journal)\";\n case \"update\":\n return \"Update an existing entry (reference a previous entry with supersedes:)\";\n case \"define-entity\":\n return \"Define a new entity schema with fields and sections\";\n case \"alter-entity\":\n return \"Modify an existing entity schema (add/remove fields or sections)\";\n case \"define-synthesis\":\n return \"Define a synthesis operation that queries entries and generates content via LLM\";\n case \"actualize-synthesis\":\n return \"Trigger a synthesis to regenerate its output based on current data\";\n }\n}\n\n/**\n * Get detail text for a directive.\n */\nfunction getDirectiveDetail(directive: Directive): string {\n if ((INSTANCE_DIRECTIVES as readonly string[]).includes(directive)) {\n return \"Instance directive\";\n }\n if ((SCHEMA_DIRECTIVES as readonly string[]).includes(directive)) {\n return \"Schema directive\";\n }\n return \"Synthesis directive\";\n}\n\n/**\n * Provider for directive completion after timestamp.\n */\nexport const directiveProvider: CompletionProvider = {\n name: \"directive\",\n contextKinds: [\"after_timestamp\"],\n\n getCompletions(ctx: CompletionContext, _workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n\n return ALL_DIRECTIVES.filter((d) => !partial || d.toLowerCase().startsWith(partial)).map(\n (directive, index) => ({\n label: directive,\n kind: 14 as CompletionItemKind, // Keyword\n detail: getDirectiveDetail(directive),\n documentation: {\n kind: \"markdown\",\n value: getDirectiveDescription(directive),\n },\n insertText: `${directive} `,\n // Sort instance directives before schema directives\n sortText: String(index),\n filterText: directive,\n }),\n );\n },\n};\n"],"mappings":";;;;;;AAcA,SAAS,wBAAwB,WAA8B;AAC7D,SAAQ,WAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,gBACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,mBACH,QAAO;EACT,KAAK,sBACH,QAAO;;;;;;AAOb,SAAS,mBAAmB,WAA8B;AACxD,KAAK,oBAA0C,SAAS,UAAU,CAChE,QAAO;AAET,KAAK,kBAAwC,SAAS,UAAU,CAC9D,QAAO;AAET,QAAO;;;;;AAMT,MAAaA,oBAAwC;CACnD,MAAM;CACN,cAAc,CAAC,kBAAkB;CAEjC,eAAe,KAAwB,YAAyC;EAC9E,MAAM,UAAU,IAAI,QAAQ,aAAa;AAEzC,SAAO,eAAe,QAAQ,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,WAAW,QAAQ,CAAC,CAAC,KAClF,WAAW,WAAW;GACrB,OAAO;GACP,MAAM;GACN,QAAQ,mBAAmB,UAAU;GACrC,eAAe;IACb,MAAM;IACN,OAAO,wBAAwB,UAAU;IAC1C;GACD,YAAY,GAAG,UAAU;GAEzB,UAAU,OAAO,MAAM;GACvB,YAAY;GACb,EACF;;CAEJ"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isInstanceDirective } from "@rejot-dev/thalo";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/completions/providers/entity.ts
|
|
4
|
+
/**
|
|
5
|
+
* Provider for entity type completion after directive.
|
|
6
|
+
*
|
|
7
|
+
* Entity types are defined via `define-entity` and stored in the SchemaRegistry.
|
|
8
|
+
*/
|
|
9
|
+
const entityProvider = {
|
|
10
|
+
name: "entity",
|
|
11
|
+
contextKinds: ["after_directive"],
|
|
12
|
+
getCompletions(ctx, workspace) {
|
|
13
|
+
const partial = ctx.partial.toLowerCase();
|
|
14
|
+
const directive = ctx.entry.directive;
|
|
15
|
+
const items = [];
|
|
16
|
+
if (directive && isInstanceDirective(directive)) for (const entityName of workspace.schemaRegistry.entityNames()) {
|
|
17
|
+
if (partial && !entityName.toLowerCase().startsWith(partial)) continue;
|
|
18
|
+
const schema = workspace.schemaRegistry.get(entityName);
|
|
19
|
+
items.push({
|
|
20
|
+
label: entityName,
|
|
21
|
+
kind: 7,
|
|
22
|
+
detail: "Entity type",
|
|
23
|
+
documentation: schema ? {
|
|
24
|
+
kind: "markdown",
|
|
25
|
+
value: `**${schema.description}**\n\nDefined at ${schema.definedAt}`
|
|
26
|
+
} : void 0,
|
|
27
|
+
insertText: `${entityName} `,
|
|
28
|
+
filterText: entityName
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (directive === "alter-entity") for (const entityName of workspace.schemaRegistry.entityNames()) {
|
|
32
|
+
if (partial && !entityName.toLowerCase().startsWith(partial)) continue;
|
|
33
|
+
const schema = workspace.schemaRegistry.get(entityName);
|
|
34
|
+
items.push({
|
|
35
|
+
label: entityName,
|
|
36
|
+
kind: 7,
|
|
37
|
+
detail: "Existing entity",
|
|
38
|
+
documentation: schema ? {
|
|
39
|
+
kind: "markdown",
|
|
40
|
+
value: `**${schema.description}**\n\nDefined at ${schema.definedAt}`
|
|
41
|
+
} : void 0,
|
|
42
|
+
insertText: `${entityName} `,
|
|
43
|
+
filterText: entityName
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return items;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { entityProvider };
|
|
52
|
+
//# sourceMappingURL=entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.js","names":["entityProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/entity.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport { isInstanceDirective } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Provider for entity type completion after directive.\n *\n * Entity types are defined via `define-entity` and stored in the SchemaRegistry.\n */\nexport const entityProvider: CompletionProvider = {\n name: \"entity\",\n contextKinds: [\"after_directive\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const directive = ctx.entry.directive;\n const items: CompletionItem[] = [];\n\n // For instance directives (create/update), suggest entities from schema registry\n if (directive && isInstanceDirective(directive)) {\n for (const entityName of workspace.schemaRegistry.entityNames()) {\n if (partial && !entityName.toLowerCase().startsWith(partial)) {\n continue;\n }\n const schema = workspace.schemaRegistry.get(entityName);\n items.push({\n label: entityName,\n kind: 7 as CompletionItemKind, // Class\n detail: \"Entity type\",\n documentation: schema\n ? {\n kind: \"markdown\",\n value: `**${schema.description}**\\n\\nDefined at ${schema.definedAt}`,\n }\n : undefined,\n insertText: `${entityName} `,\n filterText: entityName,\n });\n }\n }\n\n // For alter-entity, only suggest entities that exist in the schema registry\n if (directive === \"alter-entity\") {\n for (const entityName of workspace.schemaRegistry.entityNames()) {\n if (partial && !entityName.toLowerCase().startsWith(partial)) {\n continue;\n }\n const schema = workspace.schemaRegistry.get(entityName);\n items.push({\n label: entityName,\n kind: 7 as CompletionItemKind, // Class\n detail: \"Existing entity\",\n documentation: schema\n ? {\n kind: \"markdown\",\n value: `**${schema.description}**\\n\\nDefined at ${schema.definedAt}`,\n }\n : undefined,\n insertText: `${entityName} `,\n filterText: entityName,\n });\n }\n }\n\n // For define-entity, no suggestions (user creates new name)\n\n return items;\n },\n};\n"],"mappings":";;;;;;;;AAWA,MAAaA,iBAAqC;CAChD,MAAM;CACN,cAAc,CAAC,kBAAkB;CAEjC,eAAe,KAAwB,WAAwC;EAC7E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAM,YAAY,IAAI,MAAM;EAC5B,MAAMC,QAA0B,EAAE;AAGlC,MAAI,aAAa,oBAAoB,UAAU,CAC7C,MAAK,MAAM,cAAc,UAAU,eAAe,aAAa,EAAE;AAC/D,OAAI,WAAW,CAAC,WAAW,aAAa,CAAC,WAAW,QAAQ,CAC1D;GAEF,MAAM,SAAS,UAAU,eAAe,IAAI,WAAW;AACvD,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ;IACR,eAAe,SACX;KACE,MAAM;KACN,OAAO,KAAK,OAAO,YAAY,mBAAmB,OAAO;KAC1D,GACD;IACJ,YAAY,GAAG,WAAW;IAC1B,YAAY;IACb,CAAC;;AAKN,MAAI,cAAc,eAChB,MAAK,MAAM,cAAc,UAAU,eAAe,aAAa,EAAE;AAC/D,OAAI,WAAW,CAAC,WAAW,aAAa,CAAC,WAAW,QAAQ,CAC1D;GAEF,MAAM,SAAS,UAAU,eAAe,IAAI,WAAW;AACvD,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ;IACR,eAAe,SACX;KACE,MAAM;KACN,OAAO,KAAK,OAAO,YAAY,mBAAmB,OAAO;KAC1D,GACD;IACJ,YAAY,GAAG,WAAW;IAC1B,YAAY;IACb,CAAC;;AAMN,SAAO;;CAEV"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { isSyntaxError } from "@rejot-dev/thalo";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/completions/providers/link.ts
|
|
4
|
+
/**
|
|
5
|
+
* Format a timestamp to string
|
|
6
|
+
*/
|
|
7
|
+
function formatTimestamp(ts) {
|
|
8
|
+
return `${`${ts.date.year}-${String(ts.date.month).padStart(2, "0")}-${String(ts.date.day).padStart(2, "0")}`}T${`${String(ts.time.hour).padStart(2, "0")}:${String(ts.time.minute).padStart(2, "0")}`}${isSyntaxError(ts.timezone) ? "" : ts.timezone.value}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get sort text for an entry (prefer recent entries).
|
|
12
|
+
*/
|
|
13
|
+
function getSortText(entry) {
|
|
14
|
+
let ts;
|
|
15
|
+
switch (entry.type) {
|
|
16
|
+
case "instance_entry":
|
|
17
|
+
ts = entry.header.timestamp;
|
|
18
|
+
break;
|
|
19
|
+
case "schema_entry":
|
|
20
|
+
ts = entry.header.timestamp;
|
|
21
|
+
break;
|
|
22
|
+
case "synthesis_entry":
|
|
23
|
+
ts = entry.header.timestamp;
|
|
24
|
+
break;
|
|
25
|
+
case "actualize_entry":
|
|
26
|
+
ts = entry.header.timestamp;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
return formatTimestamp(ts).split("").map((c) => String.fromCharCode(126 - c.charCodeAt(0))).join("");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get title from an entry
|
|
33
|
+
*/
|
|
34
|
+
function getEntryTitle(entry) {
|
|
35
|
+
switch (entry.type) {
|
|
36
|
+
case "instance_entry": return entry.header.title?.value ?? "(no title)";
|
|
37
|
+
case "synthesis_entry": return entry.header.title?.value ?? "(no title)";
|
|
38
|
+
case "actualize_entry": return `actualize-synthesis ^${entry.header.target.id}`;
|
|
39
|
+
case "schema_entry": return entry.header.title?.value ?? "(no title)";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get entity description from an entry
|
|
44
|
+
*/
|
|
45
|
+
function getEntryEntity(entry) {
|
|
46
|
+
switch (entry.type) {
|
|
47
|
+
case "instance_entry": return entry.header.entity;
|
|
48
|
+
case "synthesis_entry": return "synthesis";
|
|
49
|
+
case "actualize_entry": return "actualize";
|
|
50
|
+
case "schema_entry": return entry.header.entityName.value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Format a link completion item.
|
|
55
|
+
*/
|
|
56
|
+
function formatLinkCompletion(linkId, definition) {
|
|
57
|
+
const entry = definition.entry;
|
|
58
|
+
const title = getEntryTitle(entry);
|
|
59
|
+
const entity = getEntryEntity(entry);
|
|
60
|
+
let ts;
|
|
61
|
+
switch (entry.type) {
|
|
62
|
+
case "instance_entry":
|
|
63
|
+
ts = entry.header.timestamp;
|
|
64
|
+
break;
|
|
65
|
+
case "schema_entry":
|
|
66
|
+
ts = entry.header.timestamp;
|
|
67
|
+
break;
|
|
68
|
+
case "synthesis_entry":
|
|
69
|
+
ts = entry.header.timestamp;
|
|
70
|
+
break;
|
|
71
|
+
case "actualize_entry":
|
|
72
|
+
ts = entry.header.timestamp;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
const timestamp = formatTimestamp(ts);
|
|
76
|
+
return {
|
|
77
|
+
label: `^${linkId}`,
|
|
78
|
+
kind: 18,
|
|
79
|
+
detail: title,
|
|
80
|
+
documentation: {
|
|
81
|
+
kind: "markdown",
|
|
82
|
+
value: `**${title}**\n\n${timestamp} • ${entity}\n\n*${definition.file}*`
|
|
83
|
+
},
|
|
84
|
+
insertText: linkId,
|
|
85
|
+
sortText: getSortText(entry),
|
|
86
|
+
filterText: linkId
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Provider for link completion (^link-id).
|
|
91
|
+
*/
|
|
92
|
+
const linkProvider = {
|
|
93
|
+
name: "link",
|
|
94
|
+
contextKinds: ["link", "header_suffix"],
|
|
95
|
+
getCompletions(ctx, workspace) {
|
|
96
|
+
if (ctx.kind === "header_suffix" && !ctx.partial.includes("^")) return [];
|
|
97
|
+
const partial = ctx.kind === "link" ? ctx.partial.toLowerCase() : ctx.partial.replace(/.*\^/, "").toLowerCase();
|
|
98
|
+
const items = [];
|
|
99
|
+
const linkIndex = workspace.linkIndex;
|
|
100
|
+
for (const [linkId, definition] of linkIndex.definitions) {
|
|
101
|
+
if (partial && !linkId.toLowerCase().includes(partial)) {
|
|
102
|
+
const entry = definition.entry;
|
|
103
|
+
if (!getEntryTitle(entry).toLowerCase().includes(partial)) continue;
|
|
104
|
+
}
|
|
105
|
+
items.push(formatLinkCompletion(linkId, definition));
|
|
106
|
+
}
|
|
107
|
+
return items;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
export { linkProvider };
|
|
113
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","names":["ts: Timestamp","linkProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/link.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace, Entry, Timestamp } from \"@rejot-dev/thalo\";\nimport { isSyntaxError } from \"@rejot-dev/thalo\";\nimport type { LinkDefinition } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Format a timestamp to string\n */\nfunction formatTimestamp(ts: Timestamp): string {\n const date = `${ts.date.year}-${String(ts.date.month).padStart(2, \"0\")}-${String(ts.date.day).padStart(2, \"0\")}`;\n const time = `${String(ts.time.hour).padStart(2, \"0\")}:${String(ts.time.minute).padStart(2, \"0\")}`;\n const tz = isSyntaxError(ts.timezone) ? \"\" : ts.timezone.value;\n return `${date}T${time}${tz}`;\n}\n\n/**\n * Get sort text for an entry (prefer recent entries).\n */\nfunction getSortText(entry: Entry): string {\n // Sort by timestamp descending (recent first)\n let ts: Timestamp;\n switch (entry.type) {\n case \"instance_entry\":\n ts = entry.header.timestamp;\n break;\n case \"schema_entry\":\n ts = entry.header.timestamp;\n break;\n case \"synthesis_entry\":\n ts = entry.header.timestamp;\n break;\n case \"actualize_entry\":\n ts = entry.header.timestamp;\n break;\n }\n const timestamp = formatTimestamp(ts);\n // Invert for sorting\n return timestamp\n .split(\"\")\n .map((c) => String.fromCharCode(126 - c.charCodeAt(0)))\n .join(\"\");\n}\n\n/**\n * Get title from an entry\n */\nfunction getEntryTitle(entry: Entry): string {\n switch (entry.type) {\n case \"instance_entry\":\n return entry.header.title?.value ?? \"(no title)\";\n case \"synthesis_entry\":\n return entry.header.title?.value ?? \"(no title)\";\n case \"actualize_entry\":\n return `actualize-synthesis ^${entry.header.target.id}`;\n case \"schema_entry\":\n return entry.header.title?.value ?? \"(no title)\";\n }\n}\n\n/**\n * Get entity description from an entry\n */\nfunction getEntryEntity(entry: Entry): string {\n switch (entry.type) {\n case \"instance_entry\":\n return entry.header.entity;\n case \"synthesis_entry\":\n return \"synthesis\";\n case \"actualize_entry\":\n return \"actualize\";\n case \"schema_entry\":\n return entry.header.entityName.value;\n }\n}\n\n/**\n * Format a link completion item.\n */\nfunction formatLinkCompletion(linkId: string, definition: LinkDefinition): CompletionItem {\n const entry = definition.entry;\n const title = getEntryTitle(entry);\n const entity = getEntryEntity(entry);\n let ts: Timestamp;\n switch (entry.type) {\n case \"instance_entry\":\n ts = entry.header.timestamp;\n break;\n case \"schema_entry\":\n ts = entry.header.timestamp;\n break;\n case \"synthesis_entry\":\n ts = entry.header.timestamp;\n break;\n case \"actualize_entry\":\n ts = entry.header.timestamp;\n break;\n }\n const timestamp = formatTimestamp(ts);\n\n return {\n label: `^${linkId}`,\n kind: 18 as CompletionItemKind, // Reference\n detail: title,\n documentation: {\n kind: \"markdown\",\n value: `**${title}**\\n\\n${timestamp} • ${entity}\\n\\n*${definition.file}*`,\n },\n insertText: linkId,\n sortText: getSortText(entry),\n filterText: linkId,\n };\n}\n\n/**\n * Provider for link completion (^link-id).\n */\nexport const linkProvider: CompletionProvider = {\n name: \"link\",\n contextKinds: [\"link\", \"header_suffix\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n // For header_suffix, only provide if we're at a ^ position\n if (ctx.kind === \"header_suffix\" && !ctx.partial.includes(\"^\")) {\n return [];\n }\n\n const partial =\n ctx.kind === \"link\"\n ? ctx.partial.toLowerCase()\n : ctx.partial.replace(/.*\\^/, \"\").toLowerCase();\n\n const items: CompletionItem[] = [];\n const linkIndex = workspace.linkIndex;\n\n for (const [linkId, definition] of linkIndex.definitions) {\n // Filter by partial text\n if (partial && !linkId.toLowerCase().includes(partial)) {\n // Also check title\n const entry = definition.entry;\n const title = getEntryTitle(entry);\n if (!title.toLowerCase().includes(partial)) {\n continue;\n }\n }\n\n items.push(formatLinkCompletion(linkId, definition));\n }\n\n return items;\n },\n};\n"],"mappings":";;;;;;AAUA,SAAS,gBAAgB,IAAuB;AAI9C,QAAO,GAHM,GAAG,GAAG,KAAK,KAAK,GAAG,OAAO,GAAG,KAAK,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,KAAK,IAAI,CAAC,SAAS,GAAG,IAAI,GAG/F,GAFF,GAAG,OAAO,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,KACrF,cAAc,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS;;;;;AAO3D,SAAS,YAAY,OAAsB;CAEzC,IAAIA;AACJ,SAAQ,MAAM,MAAd;EACE,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;;AAIJ,QAFkB,gBAAgB,GAAG,CAGlC,MAAM,GAAG,CACT,KAAK,MAAM,OAAO,aAAa,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CACtD,KAAK,GAAG;;;;;AAMb,SAAS,cAAc,OAAsB;AAC3C,SAAQ,MAAM,MAAd;EACE,KAAK,iBACH,QAAO,MAAM,OAAO,OAAO,SAAS;EACtC,KAAK,kBACH,QAAO,MAAM,OAAO,OAAO,SAAS;EACtC,KAAK,kBACH,QAAO,wBAAwB,MAAM,OAAO,OAAO;EACrD,KAAK,eACH,QAAO,MAAM,OAAO,OAAO,SAAS;;;;;;AAO1C,SAAS,eAAe,OAAsB;AAC5C,SAAQ,MAAM,MAAd;EACE,KAAK,iBACH,QAAO,MAAM,OAAO;EACtB,KAAK,kBACH,QAAO;EACT,KAAK,kBACH,QAAO;EACT,KAAK,eACH,QAAO,MAAM,OAAO,WAAW;;;;;;AAOrC,SAAS,qBAAqB,QAAgB,YAA4C;CACxF,MAAM,QAAQ,WAAW;CACzB,MAAM,QAAQ,cAAc,MAAM;CAClC,MAAM,SAAS,eAAe,MAAM;CACpC,IAAIA;AACJ,SAAQ,MAAM,MAAd;EACE,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;EACF,KAAK;AACH,QAAK,MAAM,OAAO;AAClB;;CAEJ,MAAM,YAAY,gBAAgB,GAAG;AAErC,QAAO;EACL,OAAO,IAAI;EACX,MAAM;EACN,QAAQ;EACR,eAAe;GACb,MAAM;GACN,OAAO,KAAK,MAAM,QAAQ,UAAU,KAAK,OAAO,OAAO,WAAW,KAAK;GACxE;EACD,YAAY;EACZ,UAAU,YAAY,MAAM;EAC5B,YAAY;EACb;;;;;AAMH,MAAaC,eAAmC;CAC9C,MAAM;CACN,cAAc,CAAC,QAAQ,gBAAgB;CAEvC,eAAe,KAAwB,WAAwC;AAE7E,MAAI,IAAI,SAAS,mBAAmB,CAAC,IAAI,QAAQ,SAAS,IAAI,CAC5D,QAAO,EAAE;EAGX,MAAM,UACJ,IAAI,SAAS,SACT,IAAI,QAAQ,aAAa,GACzB,IAAI,QAAQ,QAAQ,QAAQ,GAAG,CAAC,aAAa;EAEnD,MAAMC,QAA0B,EAAE;EAClC,MAAM,YAAY,UAAU;AAE5B,OAAK,MAAM,CAAC,QAAQ,eAAe,UAAU,aAAa;AAExD,OAAI,WAAW,CAAC,OAAO,aAAa,CAAC,SAAS,QAAQ,EAAE;IAEtD,MAAM,QAAQ,WAAW;AAEzB,QAAI,CADU,cAAc,MAAM,CACvB,aAAa,CAAC,SAAS,QAAQ,CACxC;;AAIJ,SAAM,KAAK,qBAAqB,QAAQ,WAAW,CAAC;;AAGtD,SAAO;;CAEV"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TypeExpr } from "@rejot-dev/thalo";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/completions/providers/metadata-key.ts
|
|
4
|
+
/**
|
|
5
|
+
* Provider for metadata key completion in instance entries.
|
|
6
|
+
*/
|
|
7
|
+
const metadataKeyProvider = {
|
|
8
|
+
name: "metadata-key",
|
|
9
|
+
contextKinds: ["metadata_key"],
|
|
10
|
+
getCompletions(ctx, workspace) {
|
|
11
|
+
const partial = ctx.partial.toLowerCase();
|
|
12
|
+
const entity = ctx.entry.entity;
|
|
13
|
+
const existingKeys = ctx.entry.existingMetadataKeys ?? [];
|
|
14
|
+
const items = [];
|
|
15
|
+
if (ctx.entry.isSchemaEntry) return items;
|
|
16
|
+
if (!entity) return items;
|
|
17
|
+
const schema = workspace.schemaRegistry.get(entity);
|
|
18
|
+
if (!schema) return items;
|
|
19
|
+
for (const [fieldName, field] of schema.fields) {
|
|
20
|
+
if (existingKeys.includes(fieldName)) continue;
|
|
21
|
+
if (partial && !fieldName.toLowerCase().startsWith(partial)) continue;
|
|
22
|
+
const typeStr = TypeExpr.toString(field.type);
|
|
23
|
+
const optionalSuffix = field.optional ? " (optional)" : " (required)";
|
|
24
|
+
items.push({
|
|
25
|
+
label: fieldName,
|
|
26
|
+
kind: 5,
|
|
27
|
+
detail: `${typeStr}${optionalSuffix}`,
|
|
28
|
+
documentation: field.description ? {
|
|
29
|
+
kind: "markdown",
|
|
30
|
+
value: field.description
|
|
31
|
+
} : void 0,
|
|
32
|
+
insertText: `${fieldName}: `,
|
|
33
|
+
filterText: fieldName,
|
|
34
|
+
sortText: field.optional ? `1${fieldName}` : `0${fieldName}`
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return items;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { metadataKeyProvider };
|
|
43
|
+
//# sourceMappingURL=metadata-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata-key.js","names":["metadataKeyProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/metadata-key.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport { TypeExpr } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Provider for metadata key completion in instance entries.\n */\nexport const metadataKeyProvider: CompletionProvider = {\n name: \"metadata-key\",\n contextKinds: [\"metadata_key\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const entity = ctx.entry.entity;\n const existingKeys = ctx.entry.existingMetadataKeys ?? [];\n const items: CompletionItem[] = [];\n\n // Skip if this is a schema entry (they use field_type context)\n if (ctx.entry.isSchemaEntry) {\n return items;\n }\n\n // Get schema for the entity\n if (!entity) {\n return items;\n }\n\n const schema = workspace.schemaRegistry.get(entity);\n if (!schema) {\n return items;\n }\n\n // Suggest fields from the schema\n for (const [fieldName, field] of schema.fields) {\n // Skip already-used keys\n if (existingKeys.includes(fieldName)) {\n continue;\n }\n\n // Filter by partial\n if (partial && !fieldName.toLowerCase().startsWith(partial)) {\n continue;\n }\n\n const typeStr = TypeExpr.toString(field.type);\n const optionalSuffix = field.optional ? \" (optional)\" : \" (required)\";\n\n items.push({\n label: fieldName,\n kind: 5 as CompletionItemKind, // Field\n detail: `${typeStr}${optionalSuffix}`,\n documentation: field.description\n ? {\n kind: \"markdown\",\n value: field.description,\n }\n : undefined,\n insertText: `${fieldName}: `,\n filterText: fieldName,\n // Sort required fields first\n sortText: field.optional ? `1${fieldName}` : `0${fieldName}`,\n });\n }\n\n return items;\n },\n};\n"],"mappings":";;;;;;AASA,MAAaA,sBAA0C;CACrD,MAAM;CACN,cAAc,CAAC,eAAe;CAE9B,eAAe,KAAwB,WAAwC;EAC7E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAM,SAAS,IAAI,MAAM;EACzB,MAAM,eAAe,IAAI,MAAM,wBAAwB,EAAE;EACzD,MAAMC,QAA0B,EAAE;AAGlC,MAAI,IAAI,MAAM,cACZ,QAAO;AAIT,MAAI,CAAC,OACH,QAAO;EAGT,MAAM,SAAS,UAAU,eAAe,IAAI,OAAO;AACnD,MAAI,CAAC,OACH,QAAO;AAIT,OAAK,MAAM,CAAC,WAAW,UAAU,OAAO,QAAQ;AAE9C,OAAI,aAAa,SAAS,UAAU,CAClC;AAIF,OAAI,WAAW,CAAC,UAAU,aAAa,CAAC,WAAW,QAAQ,CACzD;GAGF,MAAM,UAAU,SAAS,SAAS,MAAM,KAAK;GAC7C,MAAM,iBAAiB,MAAM,WAAW,gBAAgB;AAExD,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,GAAG,UAAU;IACrB,eAAe,MAAM,cACjB;KACE,MAAM;KACN,OAAO,MAAM;KACd,GACD;IACJ,YAAY,GAAG,UAAU;IACzB,YAAY;IAEZ,UAAU,MAAM,WAAW,IAAI,cAAc,IAAI;IAClD,CAAC;;AAGJ,SAAO;;CAEV"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region src/handlers/completions/providers/metadata-value.ts
|
|
2
|
+
/**
|
|
3
|
+
* Extract literal values from a type expression.
|
|
4
|
+
*/
|
|
5
|
+
function extractLiteralValues(type) {
|
|
6
|
+
switch (type.kind) {
|
|
7
|
+
case "literal": return [type.value];
|
|
8
|
+
case "union": return type.members.flatMap((m) => extractLiteralValues(m));
|
|
9
|
+
default: return [];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Check if a type accepts links.
|
|
14
|
+
*/
|
|
15
|
+
function acceptsLinks(type) {
|
|
16
|
+
switch (type.kind) {
|
|
17
|
+
case "primitive": return type.name === "link";
|
|
18
|
+
case "array": return acceptsLinks(type.elementType);
|
|
19
|
+
case "union": return type.members.some((m) => acceptsLinks(m));
|
|
20
|
+
default: return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Provider for metadata value completion.
|
|
25
|
+
*/
|
|
26
|
+
const metadataValueProvider = {
|
|
27
|
+
name: "metadata-value",
|
|
28
|
+
contextKinds: ["metadata_value"],
|
|
29
|
+
getCompletions(ctx, workspace) {
|
|
30
|
+
const partial = ctx.partial.toLowerCase();
|
|
31
|
+
const entity = ctx.entry.entity;
|
|
32
|
+
const metadataKey = ctx.metadataKey;
|
|
33
|
+
const items = [];
|
|
34
|
+
if (ctx.entry.isSchemaEntry) return items;
|
|
35
|
+
if (!entity || !metadataKey) return items;
|
|
36
|
+
const schema = workspace.schemaRegistry.get(entity);
|
|
37
|
+
if (!schema) return items;
|
|
38
|
+
const field = schema.fields.get(metadataKey);
|
|
39
|
+
if (!field) return items;
|
|
40
|
+
const literals = extractLiteralValues(field.type);
|
|
41
|
+
for (const value of literals) {
|
|
42
|
+
if (partial && !value.toLowerCase().startsWith(partial)) continue;
|
|
43
|
+
items.push({
|
|
44
|
+
label: value,
|
|
45
|
+
kind: 12,
|
|
46
|
+
detail: `Valid value for ${metadataKey}`,
|
|
47
|
+
insertText: value,
|
|
48
|
+
filterText: value
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (metadataKey === "subject" && acceptsLinks(field.type)) {
|
|
52
|
+
if (!partial || "self".startsWith(partial) || "^self".startsWith(partial)) items.push({
|
|
53
|
+
label: "^self",
|
|
54
|
+
kind: 18,
|
|
55
|
+
detail: "Reference to yourself",
|
|
56
|
+
documentation: {
|
|
57
|
+
kind: "markdown",
|
|
58
|
+
value: "Use `^self` for personal lore entries about yourself."
|
|
59
|
+
},
|
|
60
|
+
insertText: "^self",
|
|
61
|
+
filterText: "self",
|
|
62
|
+
sortText: "0"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return items;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { metadataValueProvider };
|
|
71
|
+
//# sourceMappingURL=metadata-value.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata-value.js","names":["metadataValueProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/metadata-value.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace, ModelTypeExpression } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Extract literal values from a type expression.\n */\nfunction extractLiteralValues(type: ModelTypeExpression): string[] {\n switch (type.kind) {\n case \"literal\":\n return [type.value];\n case \"union\":\n return type.members.flatMap((m) => extractLiteralValues(m));\n default:\n return [];\n }\n}\n\n/**\n * Check if a type accepts links.\n */\nfunction acceptsLinks(type: ModelTypeExpression): boolean {\n switch (type.kind) {\n case \"primitive\":\n return type.name === \"link\";\n case \"array\":\n return acceptsLinks(type.elementType);\n case \"union\":\n return type.members.some((m) => acceptsLinks(m));\n default:\n return false;\n }\n}\n\n/**\n * Provider for metadata value completion.\n */\nexport const metadataValueProvider: CompletionProvider = {\n name: \"metadata-value\",\n contextKinds: [\"metadata_value\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const entity = ctx.entry.entity;\n const metadataKey = ctx.metadataKey;\n const items: CompletionItem[] = [];\n\n // Skip if this is a schema entry\n if (ctx.entry.isSchemaEntry) {\n return items;\n }\n\n if (!entity || !metadataKey) {\n return items;\n }\n\n const schema = workspace.schemaRegistry.get(entity);\n if (!schema) {\n return items;\n }\n\n const field = schema.fields.get(metadataKey);\n if (!field) {\n return items;\n }\n\n // Get literal values from the type\n const literals = extractLiteralValues(field.type);\n for (const value of literals) {\n if (partial && !value.toLowerCase().startsWith(partial)) {\n continue;\n }\n items.push({\n label: value,\n kind: 12 as CompletionItemKind, // Value\n detail: `Valid value for ${metadataKey}`,\n insertText: value,\n filterText: value,\n });\n }\n\n // If the field accepts links and we have a partial that starts with ^, provide link completions\n // (but link completions are primarily handled by the link provider via \"link\" context)\n\n // Special case: subject field often uses ^self\n if (metadataKey === \"subject\" && acceptsLinks(field.type)) {\n if (!partial || \"self\".startsWith(partial) || \"^self\".startsWith(partial)) {\n items.push({\n label: \"^self\",\n kind: 18 as CompletionItemKind, // Reference\n detail: \"Reference to yourself\",\n documentation: {\n kind: \"markdown\",\n value: \"Use `^self` for personal lore entries about yourself.\",\n },\n insertText: \"^self\",\n filterText: \"self\",\n sortText: \"0\", // High priority\n });\n }\n }\n\n return items;\n },\n};\n"],"mappings":";;;;AAQA,SAAS,qBAAqB,MAAqC;AACjE,SAAQ,KAAK,MAAb;EACE,KAAK,UACH,QAAO,CAAC,KAAK,MAAM;EACrB,KAAK,QACH,QAAO,KAAK,QAAQ,SAAS,MAAM,qBAAqB,EAAE,CAAC;EAC7D,QACE,QAAO,EAAE;;;;;;AAOf,SAAS,aAAa,MAAoC;AACxD,SAAQ,KAAK,MAAb;EACE,KAAK,YACH,QAAO,KAAK,SAAS;EACvB,KAAK,QACH,QAAO,aAAa,KAAK,YAAY;EACvC,KAAK,QACH,QAAO,KAAK,QAAQ,MAAM,MAAM,aAAa,EAAE,CAAC;EAClD,QACE,QAAO;;;;;;AAOb,MAAaA,wBAA4C;CACvD,MAAM;CACN,cAAc,CAAC,iBAAiB;CAEhC,eAAe,KAAwB,WAAwC;EAC7E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAM,SAAS,IAAI,MAAM;EACzB,MAAM,cAAc,IAAI;EACxB,MAAMC,QAA0B,EAAE;AAGlC,MAAI,IAAI,MAAM,cACZ,QAAO;AAGT,MAAI,CAAC,UAAU,CAAC,YACd,QAAO;EAGT,MAAM,SAAS,UAAU,eAAe,IAAI,OAAO;AACnD,MAAI,CAAC,OACH,QAAO;EAGT,MAAM,QAAQ,OAAO,OAAO,IAAI,YAAY;AAC5C,MAAI,CAAC,MACH,QAAO;EAIT,MAAM,WAAW,qBAAqB,MAAM,KAAK;AACjD,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,WAAW,CAAC,MAAM,aAAa,CAAC,WAAW,QAAQ,CACrD;AAEF,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,mBAAmB;IAC3B,YAAY;IACZ,YAAY;IACb,CAAC;;AAOJ,MAAI,gBAAgB,aAAa,aAAa,MAAM,KAAK,EACvD;OAAI,CAAC,WAAW,OAAO,WAAW,QAAQ,IAAI,QAAQ,WAAW,QAAQ,CACvE,OAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ;IACR,eAAe;KACb,MAAM;KACN,OAAO;KACR;IACD,YAAY;IACZ,YAAY;IACZ,UAAU;IACX,CAAC;;AAIN,SAAO;;CAEV"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { timestampProvider } from "./timestamp.js";
|
|
2
|
+
import { directiveProvider } from "./directive.js";
|
|
3
|
+
import { entityProvider } from "./entity.js";
|
|
4
|
+
import { metadataKeyProvider } from "./metadata-key.js";
|
|
5
|
+
import { metadataValueProvider } from "./metadata-value.js";
|
|
6
|
+
import { linkProvider } from "./link.js";
|
|
7
|
+
import { tagProvider } from "./tag.js";
|
|
8
|
+
import { sectionProvider } from "./section.js";
|
|
9
|
+
import { schemaBlockProvider } from "./schema-block.js";
|
|
10
|
+
import { typeExprProvider } from "./type-expr.js";
|
|
11
|
+
|
|
12
|
+
//#region src/handlers/completions/providers/providers.ts
|
|
13
|
+
/**
|
|
14
|
+
* All completion providers, in order of priority.
|
|
15
|
+
*/
|
|
16
|
+
const allProviders = [
|
|
17
|
+
timestampProvider,
|
|
18
|
+
directiveProvider,
|
|
19
|
+
entityProvider,
|
|
20
|
+
metadataKeyProvider,
|
|
21
|
+
metadataValueProvider,
|
|
22
|
+
linkProvider,
|
|
23
|
+
tagProvider,
|
|
24
|
+
sectionProvider,
|
|
25
|
+
schemaBlockProvider,
|
|
26
|
+
typeExprProvider
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { allProviders };
|
|
31
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","names":["allProviders: readonly CompletionProvider[]"],"sources":["../../../../src/handlers/completions/providers/providers.ts"],"sourcesContent":["import type { CompletionProvider } from \"../completions.js\";\nimport { timestampProvider } from \"./timestamp.js\";\nimport { directiveProvider } from \"./directive.js\";\nimport { entityProvider } from \"./entity.js\";\nimport { metadataKeyProvider } from \"./metadata-key.js\";\nimport { metadataValueProvider } from \"./metadata-value.js\";\nimport { linkProvider } from \"./link.js\";\nimport { tagProvider } from \"./tag.js\";\nimport { sectionProvider } from \"./section.js\";\nimport { schemaBlockProvider } from \"./schema-block.js\";\nimport { typeExprProvider } from \"./type-expr.js\";\n\n/**\n * All completion providers, in order of priority.\n */\nexport const allProviders: readonly CompletionProvider[] = [\n timestampProvider,\n directiveProvider,\n entityProvider,\n metadataKeyProvider,\n metadataValueProvider,\n linkProvider,\n tagProvider,\n sectionProvider,\n schemaBlockProvider,\n typeExprProvider,\n];\n\n// Re-export individual providers for testing\nexport {\n timestampProvider,\n directiveProvider,\n entityProvider,\n metadataKeyProvider,\n metadataValueProvider,\n linkProvider,\n tagProvider,\n sectionProvider,\n schemaBlockProvider,\n typeExprProvider,\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,MAAaA,eAA8C;CACzD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SCHEMA_BLOCK_HEADERS } from "@rejot-dev/thalo";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/completions/providers/schema-block.ts
|
|
4
|
+
/**
|
|
5
|
+
* Get description for a schema block header.
|
|
6
|
+
*/
|
|
7
|
+
function getBlockDescription(header) {
|
|
8
|
+
switch (header) {
|
|
9
|
+
case "# Metadata": return "Define metadata fields for this entity";
|
|
10
|
+
case "# Sections": return "Define content sections for this entity";
|
|
11
|
+
case "# Remove Metadata": return "Remove metadata fields (alter-entity only)";
|
|
12
|
+
case "# Remove Sections": return "Remove sections (alter-entity only)";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Provider for schema block header completion (# Metadata, # Sections, etc.)
|
|
17
|
+
*/
|
|
18
|
+
const schemaBlockProvider = {
|
|
19
|
+
name: "schema-block",
|
|
20
|
+
contextKinds: ["schema_block_header"],
|
|
21
|
+
getCompletions(ctx, _workspace) {
|
|
22
|
+
const partial = ctx.partial.toLowerCase();
|
|
23
|
+
const directive = ctx.entry.directive;
|
|
24
|
+
const items = [];
|
|
25
|
+
for (const header of SCHEMA_BLOCK_HEADERS) {
|
|
26
|
+
if ((header === "# Remove Metadata" || header === "# Remove Sections") && directive !== "alter-entity") continue;
|
|
27
|
+
if (partial && !header.toLowerCase().startsWith(partial)) continue;
|
|
28
|
+
items.push({
|
|
29
|
+
label: header,
|
|
30
|
+
kind: 22,
|
|
31
|
+
detail: "Schema block",
|
|
32
|
+
documentation: {
|
|
33
|
+
kind: "markdown",
|
|
34
|
+
value: getBlockDescription(header)
|
|
35
|
+
},
|
|
36
|
+
insertText: header.slice(1),
|
|
37
|
+
filterText: header
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return items;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { schemaBlockProvider };
|
|
46
|
+
//# sourceMappingURL=schema-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-block.js","names":["schemaBlockProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/schema-block.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport { SCHEMA_BLOCK_HEADERS, type SchemaBlockHeader } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Get description for a schema block header.\n */\nfunction getBlockDescription(header: SchemaBlockHeader): string {\n switch (header) {\n case \"# Metadata\":\n return \"Define metadata fields for this entity\";\n case \"# Sections\":\n return \"Define content sections for this entity\";\n case \"# Remove Metadata\":\n return \"Remove metadata fields (alter-entity only)\";\n case \"# Remove Sections\":\n return \"Remove sections (alter-entity only)\";\n }\n}\n\n/**\n * Provider for schema block header completion (# Metadata, # Sections, etc.)\n */\nexport const schemaBlockProvider: CompletionProvider = {\n name: \"schema-block\",\n contextKinds: [\"schema_block_header\"],\n\n getCompletions(ctx: CompletionContext, _workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const directive = ctx.entry.directive;\n const items: CompletionItem[] = [];\n\n for (const header of SCHEMA_BLOCK_HEADERS) {\n // Remove Metadata/Sections only for alter-entity\n if (\n (header === \"# Remove Metadata\" || header === \"# Remove Sections\") &&\n directive !== \"alter-entity\"\n ) {\n continue;\n }\n\n // Filter by partial (partial includes the #)\n if (partial && !header.toLowerCase().startsWith(partial)) {\n continue;\n }\n\n items.push({\n label: header,\n kind: 22 as CompletionItemKind, // Struct\n detail: \"Schema block\",\n documentation: {\n kind: \"markdown\",\n value: getBlockDescription(header),\n },\n // Remove the # prefix since we're completing after #\n insertText: header.slice(1), // Remove leading #\n filterText: header,\n });\n }\n\n return items;\n },\n};\n"],"mappings":";;;;;;AASA,SAAS,oBAAoB,QAAmC;AAC9D,SAAQ,QAAR;EACE,KAAK,aACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,KAAK,oBACH,QAAO;EACT,KAAK,oBACH,QAAO;;;;;;AAOb,MAAaA,sBAA0C;CACrD,MAAM;CACN,cAAc,CAAC,sBAAsB;CAErC,eAAe,KAAwB,YAAyC;EAC9E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAM,YAAY,IAAI,MAAM;EAC5B,MAAMC,QAA0B,EAAE;AAElC,OAAK,MAAM,UAAU,sBAAsB;AAEzC,QACG,WAAW,uBAAuB,WAAW,wBAC9C,cAAc,eAEd;AAIF,OAAI,WAAW,CAAC,OAAO,aAAa,CAAC,WAAW,QAAQ,CACtD;AAGF,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ;IACR,eAAe;KACb,MAAM;KACN,OAAO,oBAAoB,OAAO;KACnC;IAED,YAAY,OAAO,MAAM,EAAE;IAC3B,YAAY;IACb,CAAC;;AAGJ,SAAO;;CAEV"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/handlers/completions/providers/section.ts
|
|
2
|
+
/**
|
|
3
|
+
* Provider for section header completion in content area (# Section).
|
|
4
|
+
*/
|
|
5
|
+
const sectionProvider = {
|
|
6
|
+
name: "section",
|
|
7
|
+
contextKinds: ["section_header"],
|
|
8
|
+
getCompletions(ctx, workspace) {
|
|
9
|
+
const partial = ctx.partial.toLowerCase();
|
|
10
|
+
const entity = ctx.entry.entity;
|
|
11
|
+
const items = [];
|
|
12
|
+
if (!entity) return items;
|
|
13
|
+
const schema = workspace.schemaRegistry.get(entity);
|
|
14
|
+
if (!schema) return items;
|
|
15
|
+
for (const [sectionName, section] of schema.sections) {
|
|
16
|
+
if (partial && !sectionName.toLowerCase().startsWith(partial)) continue;
|
|
17
|
+
const optionalSuffix = section.optional ? " (optional)" : " (required)";
|
|
18
|
+
items.push({
|
|
19
|
+
label: sectionName,
|
|
20
|
+
kind: 22,
|
|
21
|
+
detail: `Section${optionalSuffix}`,
|
|
22
|
+
documentation: section.description ? {
|
|
23
|
+
kind: "markdown",
|
|
24
|
+
value: section.description
|
|
25
|
+
} : void 0,
|
|
26
|
+
insertText: ` ${sectionName}`,
|
|
27
|
+
filterText: sectionName,
|
|
28
|
+
sortText: section.optional ? `1${sectionName}` : `0${sectionName}`
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return items;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { sectionProvider };
|
|
37
|
+
//# sourceMappingURL=section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.js","names":["sectionProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/section.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Provider for section header completion in content area (# Section).\n */\nexport const sectionProvider: CompletionProvider = {\n name: \"section\",\n contextKinds: [\"section_header\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const entity = ctx.entry.entity;\n const items: CompletionItem[] = [];\n\n if (!entity) {\n return items;\n }\n\n const schema = workspace.schemaRegistry.get(entity);\n if (!schema) {\n return items;\n }\n\n // Suggest sections from the schema\n for (const [sectionName, section] of schema.sections) {\n if (partial && !sectionName.toLowerCase().startsWith(partial)) {\n continue;\n }\n\n const optionalSuffix = section.optional ? \" (optional)\" : \" (required)\";\n\n items.push({\n label: sectionName,\n kind: 22 as CompletionItemKind, // Struct\n detail: `Section${optionalSuffix}`,\n documentation: section.description\n ? {\n kind: \"markdown\",\n value: section.description,\n }\n : undefined,\n // Insert the full markdown header format\n insertText: ` ${sectionName}`,\n filterText: sectionName,\n // Sort required sections first\n sortText: section.optional ? `1${sectionName}` : `0${sectionName}`,\n });\n }\n\n return items;\n },\n};\n"],"mappings":";;;;AAQA,MAAaA,kBAAsC;CACjD,MAAM;CACN,cAAc,CAAC,iBAAiB;CAEhC,eAAe,KAAwB,WAAwC;EAC7E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAM,SAAS,IAAI,MAAM;EACzB,MAAMC,QAA0B,EAAE;AAElC,MAAI,CAAC,OACH,QAAO;EAGT,MAAM,SAAS,UAAU,eAAe,IAAI,OAAO;AACnD,MAAI,CAAC,OACH,QAAO;AAIT,OAAK,MAAM,CAAC,aAAa,YAAY,OAAO,UAAU;AACpD,OAAI,WAAW,CAAC,YAAY,aAAa,CAAC,WAAW,QAAQ,CAC3D;GAGF,MAAM,iBAAiB,QAAQ,WAAW,gBAAgB;AAE1D,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ,UAAU;IAClB,eAAe,QAAQ,cACnB;KACE,MAAM;KACN,OAAO,QAAQ;KAChB,GACD;IAEJ,YAAY,IAAI;IAChB,YAAY;IAEZ,UAAU,QAAQ,WAAW,IAAI,gBAAgB,IAAI;IACtD,CAAC;;AAGJ,SAAO;;CAEV"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/handlers/completions/providers/tag.ts
|
|
2
|
+
/**
|
|
3
|
+
* Get tags from an entry
|
|
4
|
+
*/
|
|
5
|
+
function getEntryTags(entry) {
|
|
6
|
+
switch (entry.type) {
|
|
7
|
+
case "instance_entry": return entry.header.tags.map((t) => t.name);
|
|
8
|
+
case "schema_entry": return entry.header.tags.map((t) => t.name);
|
|
9
|
+
case "synthesis_entry": return entry.header.tags.map((t) => t.name);
|
|
10
|
+
case "actualize_entry": return [];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get all unique tags from the workspace with counts.
|
|
15
|
+
*/
|
|
16
|
+
function getTagsWithCounts(workspace) {
|
|
17
|
+
const tagCounts = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const model of workspace.allModels()) for (const entry of model.ast.entries) for (const tag of getEntryTags(entry)) tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);
|
|
19
|
+
return tagCounts;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Format a tag completion item.
|
|
23
|
+
*/
|
|
24
|
+
function formatTagCompletion(tag, count) {
|
|
25
|
+
return {
|
|
26
|
+
label: `#${tag}`,
|
|
27
|
+
kind: 20,
|
|
28
|
+
detail: `${count} ${count === 1 ? "entry" : "entries"}`,
|
|
29
|
+
insertText: tag,
|
|
30
|
+
filterText: tag,
|
|
31
|
+
sortText: String(1e6 - count).padStart(7, "0")
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Provider for tag completion (#tag).
|
|
36
|
+
*/
|
|
37
|
+
const tagProvider = {
|
|
38
|
+
name: "tag",
|
|
39
|
+
contextKinds: ["tag", "header_suffix"],
|
|
40
|
+
getCompletions(ctx, workspace) {
|
|
41
|
+
if (ctx.kind === "header_suffix" && !ctx.partial.includes("#")) return [];
|
|
42
|
+
const partial = ctx.kind === "tag" ? ctx.partial.toLowerCase() : ctx.partial.replace(/.*#/, "").toLowerCase();
|
|
43
|
+
const items = [];
|
|
44
|
+
const tagCounts = getTagsWithCounts(workspace);
|
|
45
|
+
for (const [tag, count] of tagCounts) {
|
|
46
|
+
if (partial && !tag.toLowerCase().includes(partial)) continue;
|
|
47
|
+
items.push(formatTagCompletion(tag, count));
|
|
48
|
+
}
|
|
49
|
+
return items;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
export { tagProvider };
|
|
55
|
+
//# sourceMappingURL=tag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tag.js","names":["tagProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/tag.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace, Entry } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Get tags from an entry\n */\nfunction getEntryTags(entry: Entry): string[] {\n switch (entry.type) {\n case \"instance_entry\":\n return entry.header.tags.map((t) => t.name);\n case \"schema_entry\":\n return entry.header.tags.map((t) => t.name);\n case \"synthesis_entry\":\n return entry.header.tags.map((t) => t.name);\n case \"actualize_entry\":\n // Actualize entries don't have tags\n return [];\n }\n}\n\n/**\n * Get all unique tags from the workspace with counts.\n */\nfunction getTagsWithCounts(workspace: Workspace): Map<string, number> {\n const tagCounts = new Map<string, number>();\n\n for (const model of workspace.allModels()) {\n for (const entry of model.ast.entries) {\n for (const tag of getEntryTags(entry)) {\n tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1);\n }\n }\n }\n\n return tagCounts;\n}\n\n/**\n * Format a tag completion item.\n */\nfunction formatTagCompletion(tag: string, count: number): CompletionItem {\n return {\n label: `#${tag}`,\n kind: 20 as CompletionItemKind, // Keyword\n detail: `${count} ${count === 1 ? \"entry\" : \"entries\"}`,\n insertText: tag,\n filterText: tag,\n // Sort by count (most used first)\n sortText: String(1000000 - count).padStart(7, \"0\"),\n };\n}\n\n/**\n * Provider for tag completion (#tag).\n */\nexport const tagProvider: CompletionProvider = {\n name: \"tag\",\n contextKinds: [\"tag\", \"header_suffix\"],\n\n getCompletions(ctx: CompletionContext, workspace: Workspace): CompletionItem[] {\n // For header_suffix, only provide if we're at a # position\n if (ctx.kind === \"header_suffix\" && !ctx.partial.includes(\"#\")) {\n return [];\n }\n\n const partial =\n ctx.kind === \"tag\" ? ctx.partial.toLowerCase() : ctx.partial.replace(/.*#/, \"\").toLowerCase();\n\n const items: CompletionItem[] = [];\n const tagCounts = getTagsWithCounts(workspace);\n\n for (const [tag, count] of tagCounts) {\n // Filter by partial text\n if (partial && !tag.toLowerCase().includes(partial)) {\n continue;\n }\n\n items.push(formatTagCompletion(tag, count));\n }\n\n return items;\n },\n};\n"],"mappings":";;;;AAQA,SAAS,aAAa,OAAwB;AAC5C,SAAQ,MAAM,MAAd;EACE,KAAK,iBACH,QAAO,MAAM,OAAO,KAAK,KAAK,MAAM,EAAE,KAAK;EAC7C,KAAK,eACH,QAAO,MAAM,OAAO,KAAK,KAAK,MAAM,EAAE,KAAK;EAC7C,KAAK,kBACH,QAAO,MAAM,OAAO,KAAK,KAAK,MAAM,EAAE,KAAK;EAC7C,KAAK,kBAEH,QAAO,EAAE;;;;;;AAOf,SAAS,kBAAkB,WAA2C;CACpE,MAAM,4BAAY,IAAI,KAAqB;AAE3C,MAAK,MAAM,SAAS,UAAU,WAAW,CACvC,MAAK,MAAM,SAAS,MAAM,IAAI,QAC5B,MAAK,MAAM,OAAO,aAAa,MAAM,CACnC,WAAU,IAAI,MAAM,UAAU,IAAI,IAAI,IAAI,KAAK,EAAE;AAKvD,QAAO;;;;;AAMT,SAAS,oBAAoB,KAAa,OAA+B;AACvE,QAAO;EACL,OAAO,IAAI;EACX,MAAM;EACN,QAAQ,GAAG,MAAM,GAAG,UAAU,IAAI,UAAU;EAC5C,YAAY;EACZ,YAAY;EAEZ,UAAU,OAAO,MAAU,MAAM,CAAC,SAAS,GAAG,IAAI;EACnD;;;;;AAMH,MAAaA,cAAkC;CAC7C,MAAM;CACN,cAAc,CAAC,OAAO,gBAAgB;CAEtC,eAAe,KAAwB,WAAwC;AAE7E,MAAI,IAAI,SAAS,mBAAmB,CAAC,IAAI,QAAQ,SAAS,IAAI,CAC5D,QAAO,EAAE;EAGX,MAAM,UACJ,IAAI,SAAS,QAAQ,IAAI,QAAQ,aAAa,GAAG,IAAI,QAAQ,QAAQ,OAAO,GAAG,CAAC,aAAa;EAE/F,MAAMC,QAA0B,EAAE;EAClC,MAAM,YAAY,kBAAkB,UAAU;AAE9C,OAAK,MAAM,CAAC,KAAK,UAAU,WAAW;AAEpC,OAAI,WAAW,CAAC,IAAI,aAAa,CAAC,SAAS,QAAQ,CACjD;AAGF,SAAM,KAAK,oBAAoB,KAAK,MAAM,CAAC;;AAG7C,SAAO;;CAEV"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/handlers/completions/providers/timestamp.ts
|
|
2
|
+
/**
|
|
3
|
+
* Format a date as a thalo timestamp (YYYY-MM-DDTHH:MMZ) in UTC.
|
|
4
|
+
*/
|
|
5
|
+
function formatTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
6
|
+
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}T${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(2, "0")}Z`;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Provider for timestamp completion at the start of a new entry.
|
|
10
|
+
*/
|
|
11
|
+
const timestampProvider = {
|
|
12
|
+
name: "timestamp",
|
|
13
|
+
contextKinds: ["line_start"],
|
|
14
|
+
getCompletions(_ctx, _workspace) {
|
|
15
|
+
const timestamp = formatTimestamp();
|
|
16
|
+
return [{
|
|
17
|
+
label: timestamp,
|
|
18
|
+
kind: 12,
|
|
19
|
+
detail: "Current timestamp",
|
|
20
|
+
documentation: {
|
|
21
|
+
kind: "markdown",
|
|
22
|
+
value: "Insert the current timestamp to start a new entry."
|
|
23
|
+
},
|
|
24
|
+
insertText: `${timestamp} `,
|
|
25
|
+
sortText: "0"
|
|
26
|
+
}];
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { timestampProvider };
|
|
32
|
+
//# sourceMappingURL=timestamp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timestamp.js","names":["timestampProvider: CompletionProvider"],"sources":["../../../../src/handlers/completions/providers/timestamp.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Format a date as a thalo timestamp (YYYY-MM-DDTHH:MMZ) in UTC.\n */\nfunction formatTimestamp(date: Date = new Date()): string {\n const year = date.getUTCFullYear();\n const month = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n const day = String(date.getUTCDate()).padStart(2, \"0\");\n const hours = String(date.getUTCHours()).padStart(2, \"0\");\n const minutes = String(date.getUTCMinutes()).padStart(2, \"0\");\n return `${year}-${month}-${day}T${hours}:${minutes}Z`;\n}\n\n/**\n * Provider for timestamp completion at the start of a new entry.\n */\nexport const timestampProvider: CompletionProvider = {\n name: \"timestamp\",\n contextKinds: [\"line_start\"],\n\n getCompletions(_ctx: CompletionContext, _workspace: Workspace): CompletionItem[] {\n const timestamp = formatTimestamp();\n\n return [\n {\n label: timestamp,\n kind: 12 as CompletionItemKind, // Value\n detail: \"Current timestamp\",\n documentation: {\n kind: \"markdown\",\n value: \"Insert the current timestamp to start a new entry.\",\n },\n insertText: `${timestamp} `,\n // High priority - show first\n sortText: \"0\",\n },\n ];\n },\n};\n"],"mappings":";;;;AAQA,SAAS,gBAAgB,uBAAa,IAAI,MAAM,EAAU;AAMxD,QAAO,GALM,KAAK,gBAAgB,CAKnB,GAJD,OAAO,KAAK,aAAa,GAAG,EAAE,CAAC,SAAS,GAAG,IAAI,CAIrC,GAHZ,OAAO,KAAK,YAAY,CAAC,CAAC,SAAS,GAAG,IAAI,CAGvB,GAFjB,OAAO,KAAK,aAAa,CAAC,CAAC,SAAS,GAAG,IAAI,CAEjB,GADxB,OAAO,KAAK,eAAe,CAAC,CAAC,SAAS,GAAG,IAAI,CACV;;;;;AAMrD,MAAaA,oBAAwC;CACnD,MAAM;CACN,cAAc,CAAC,aAAa;CAE5B,eAAe,MAAyB,YAAyC;EAC/E,MAAM,YAAY,iBAAiB;AAEnC,SAAO,CACL;GACE,OAAO;GACP,MAAM;GACN,QAAQ;GACR,eAAe;IACb,MAAM;IACN,OAAO;IACR;GACD,YAAY,GAAG,UAAU;GAEzB,UAAU;GACX,CACF;;CAEJ"}
|