@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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/capabilities.d.ts +12 -0
  4. package/dist/capabilities.d.ts.map +1 -0
  5. package/dist/capabilities.js +54 -0
  6. package/dist/capabilities.js.map +1 -0
  7. package/dist/handlers/completions/completions.js +33 -0
  8. package/dist/handlers/completions/completions.js.map +1 -0
  9. package/dist/handlers/completions/context.js +228 -0
  10. package/dist/handlers/completions/context.js.map +1 -0
  11. package/dist/handlers/completions/providers/directive.js +50 -0
  12. package/dist/handlers/completions/providers/directive.js.map +1 -0
  13. package/dist/handlers/completions/providers/entity.js +52 -0
  14. package/dist/handlers/completions/providers/entity.js.map +1 -0
  15. package/dist/handlers/completions/providers/link.js +113 -0
  16. package/dist/handlers/completions/providers/link.js.map +1 -0
  17. package/dist/handlers/completions/providers/metadata-key.js +43 -0
  18. package/dist/handlers/completions/providers/metadata-key.js.map +1 -0
  19. package/dist/handlers/completions/providers/metadata-value.js +71 -0
  20. package/dist/handlers/completions/providers/metadata-value.js.map +1 -0
  21. package/dist/handlers/completions/providers/providers.js +31 -0
  22. package/dist/handlers/completions/providers/providers.js.map +1 -0
  23. package/dist/handlers/completions/providers/schema-block.js +46 -0
  24. package/dist/handlers/completions/providers/schema-block.js.map +1 -0
  25. package/dist/handlers/completions/providers/section.js +37 -0
  26. package/dist/handlers/completions/providers/section.js.map +1 -0
  27. package/dist/handlers/completions/providers/tag.js +55 -0
  28. package/dist/handlers/completions/providers/tag.js.map +1 -0
  29. package/dist/handlers/completions/providers/timestamp.js +32 -0
  30. package/dist/handlers/completions/providers/timestamp.js.map +1 -0
  31. package/dist/handlers/completions/providers/type-expr.js +56 -0
  32. package/dist/handlers/completions/providers/type-expr.js.map +1 -0
  33. package/dist/handlers/definition.js +166 -0
  34. package/dist/handlers/definition.js.map +1 -0
  35. package/dist/handlers/diagnostics.js +77 -0
  36. package/dist/handlers/diagnostics.js.map +1 -0
  37. package/dist/handlers/hover.js +73 -0
  38. package/dist/handlers/hover.js.map +1 -0
  39. package/dist/handlers/references.js +233 -0
  40. package/dist/handlers/references.js.map +1 -0
  41. package/dist/handlers/semantic-tokens.js +36 -0
  42. package/dist/handlers/semantic-tokens.js.map +1 -0
  43. package/dist/mod.d.ts +2 -0
  44. package/dist/mod.js +3 -0
  45. package/dist/server.bundled.js +1764 -0
  46. package/dist/server.d.ts +18 -0
  47. package/dist/server.d.ts.map +1 -0
  48. package/dist/server.js +367 -0
  49. package/dist/server.js.map +1 -0
  50. package/package.json +45 -0
@@ -0,0 +1,56 @@
1
+ import { PRIMITIVE_TYPES } from "@rejot-dev/thalo";
2
+
3
+ //#region src/handlers/completions/providers/type-expr.ts
4
+ /**
5
+ * Get description for a primitive type.
6
+ */
7
+ function getPrimitiveTypeDescription(type) {
8
+ switch (type) {
9
+ case "string": return "Any text value";
10
+ case "datetime": return "Date value (YYYY-MM-DD)";
11
+ case "daterange": return "Date range (YYYY ~ YYYY, YYYY-MM, YYYY Q1, etc.)";
12
+ case "link": return "Reference to another entry (^link-id)";
13
+ case "number": return "Numeric value (integer or float)";
14
+ }
15
+ }
16
+ /**
17
+ * Provider for type expression completion in schema field definitions.
18
+ */
19
+ const typeExprProvider = {
20
+ name: "type-expr",
21
+ contextKinds: ["field_type"],
22
+ getCompletions(ctx, _workspace) {
23
+ const partial = ctx.partial.toLowerCase();
24
+ const items = [];
25
+ for (const type of PRIMITIVE_TYPES) {
26
+ if (partial && !type.toLowerCase().startsWith(partial)) continue;
27
+ items.push({
28
+ label: type,
29
+ kind: 25,
30
+ detail: "Primitive type",
31
+ documentation: {
32
+ kind: "markdown",
33
+ value: getPrimitiveTypeDescription(type)
34
+ },
35
+ insertText: type,
36
+ filterText: type
37
+ });
38
+ }
39
+ if (!partial || "\"".startsWith(partial)) items.push({
40
+ label: "\"...\"",
41
+ kind: 12,
42
+ detail: "Literal type",
43
+ documentation: {
44
+ kind: "markdown",
45
+ value: "Define a literal value type (e.g., \"article\" | \"video\")"
46
+ },
47
+ insertText: "\"",
48
+ filterText: "\""
49
+ });
50
+ return items;
51
+ }
52
+ };
53
+
54
+ //#endregion
55
+ export { typeExprProvider };
56
+ //# sourceMappingURL=type-expr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-expr.js","names":["typeExprProvider: CompletionProvider","items: CompletionItem[]"],"sources":["../../../../src/handlers/completions/providers/type-expr.ts"],"sourcesContent":["import type { CompletionItem, CompletionItemKind } from \"vscode-languageserver\";\nimport type { Workspace } from \"@rejot-dev/thalo\";\nimport { PRIMITIVE_TYPES, type PrimitiveType } from \"@rejot-dev/thalo\";\nimport type { CompletionContext } from \"../context.js\";\nimport type { CompletionProvider } from \"../completions.js\";\n\n/**\n * Get description for a primitive type.\n */\nfunction getPrimitiveTypeDescription(type: PrimitiveType): string {\n switch (type) {\n case \"string\":\n return \"Any text value\";\n case \"datetime\":\n return \"Date value (YYYY-MM-DD)\";\n case \"daterange\":\n return \"Date range (YYYY ~ YYYY, YYYY-MM, YYYY Q1, etc.)\";\n case \"link\":\n return \"Reference to another entry (^link-id)\";\n case \"number\":\n return \"Numeric value (integer or float)\";\n }\n}\n\n/**\n * Provider for type expression completion in schema field definitions.\n */\nexport const typeExprProvider: CompletionProvider = {\n name: \"type-expr\",\n contextKinds: [\"field_type\"],\n\n getCompletions(ctx: CompletionContext, _workspace: Workspace): CompletionItem[] {\n const partial = ctx.partial.toLowerCase();\n const items: CompletionItem[] = [];\n\n // Suggest primitive types\n for (const type of PRIMITIVE_TYPES) {\n if (partial && !type.toLowerCase().startsWith(partial)) {\n continue;\n }\n\n items.push({\n label: type,\n kind: 25 as CompletionItemKind, // TypeParameter\n detail: \"Primitive type\",\n documentation: {\n kind: \"markdown\",\n value: getPrimitiveTypeDescription(type),\n },\n insertText: type,\n filterText: type,\n });\n }\n\n // Suggest starting a literal type with quote\n if (!partial || '\"'.startsWith(partial)) {\n items.push({\n label: '\"...\"',\n kind: 12 as CompletionItemKind, // Value\n detail: \"Literal type\",\n documentation: {\n kind: \"markdown\",\n value: 'Define a literal value type (e.g., \"article\" | \"video\")',\n },\n insertText: '\"',\n filterText: '\"',\n });\n }\n\n return items;\n },\n};\n"],"mappings":";;;;;;AASA,SAAS,4BAA4B,MAA6B;AAChE,SAAQ,MAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,SACH,QAAO;;;;;;AAOb,MAAaA,mBAAuC;CAClD,MAAM;CACN,cAAc,CAAC,aAAa;CAE5B,eAAe,KAAwB,YAAyC;EAC9E,MAAM,UAAU,IAAI,QAAQ,aAAa;EACzC,MAAMC,QAA0B,EAAE;AAGlC,OAAK,MAAM,QAAQ,iBAAiB;AAClC,OAAI,WAAW,CAAC,KAAK,aAAa,CAAC,WAAW,QAAQ,CACpD;AAGF,SAAM,KAAK;IACT,OAAO;IACP,MAAM;IACN,QAAQ;IACR,eAAe;KACb,MAAM;KACN,OAAO,4BAA4B,KAAK;KACzC;IACD,YAAY;IACZ,YAAY;IACb,CAAC;;AAIJ,MAAI,CAAC,WAAW,KAAI,WAAW,QAAQ,CACrC,OAAM,KAAK;GACT,OAAO;GACP,MAAM;GACN,QAAQ;GACR,eAAe;IACb,MAAM;IACN,OAAO;IACR;GACD,YAAY;GACZ,YAAY;GACb,CAAC;AAGJ,SAAO;;CAEV"}
@@ -0,0 +1,166 @@
1
+ import { parseDocument } from "@rejot-dev/thalo/native";
2
+ import { findDefinition, findEntityDefinition, findFieldDefinition, findNodeAtPosition, findSectionDefinition } from "@rejot-dev/thalo";
3
+
4
+ //#region src/handlers/definition.ts
5
+ /**
6
+ * Convert a file path to a URI
7
+ */
8
+ function pathToUri(path) {
9
+ if (!path.startsWith("file://")) return `file://${encodeURIComponent(path).replace(/%2F/g, "/")}`;
10
+ return path;
11
+ }
12
+ /**
13
+ * Get the file type from a URI
14
+ */
15
+ function getFileType(uri) {
16
+ if (uri.endsWith(".thalo")) return "thalo";
17
+ if (uri.endsWith(".md")) return "markdown";
18
+ return "thalo";
19
+ }
20
+ /**
21
+ * Convert LSP Position to thalo Position (both are 0-based).
22
+ */
23
+ function lspToThaloPosition(pos) {
24
+ return {
25
+ line: pos.line,
26
+ column: pos.character
27
+ };
28
+ }
29
+ /**
30
+ * Handle textDocument/definition request
31
+ *
32
+ * Finds the definition location for various syntax elements at the given position:
33
+ * - ^link-id → Entry definition
34
+ * - Entity name in instance/alter-entity → define-entity
35
+ * - Metadata key → Field definition in schema
36
+ * - Section header → Section definition in schema
37
+ *
38
+ * Supports both standalone .thalo files and thalo blocks embedded in markdown.
39
+ *
40
+ * @param workspace - The thalo workspace
41
+ * @param document - The text document
42
+ * @param position - The position in the document (line/character)
43
+ * @returns The definition location, or null if not found
44
+ */
45
+ function handleDefinition(workspace, document, position) {
46
+ const fileType = getFileType(document.uri);
47
+ try {
48
+ const context = findNodeAtPosition(parseDocument(document.getText(), {
49
+ fileType,
50
+ filename: document.uri
51
+ }), lspToThaloPosition(position));
52
+ switch (context.kind) {
53
+ case "link": {
54
+ const result = findDefinition(workspace, context.linkId);
55
+ if (result) return {
56
+ uri: pathToUri(result.file),
57
+ range: {
58
+ start: {
59
+ line: result.location.startPosition.row,
60
+ character: result.location.startPosition.column
61
+ },
62
+ end: {
63
+ line: result.location.endPosition.row,
64
+ character: result.location.endPosition.column
65
+ }
66
+ }
67
+ };
68
+ return null;
69
+ }
70
+ case "entity":
71
+ case "schema_entity": {
72
+ const result = findEntityDefinition(workspace, context.kind === "entity" ? context.entityName : context.entityName);
73
+ if (result) return {
74
+ uri: pathToUri(result.file),
75
+ range: {
76
+ start: {
77
+ line: result.location.startPosition.row,
78
+ character: result.location.startPosition.column
79
+ },
80
+ end: {
81
+ line: result.location.endPosition.row,
82
+ character: result.location.endPosition.column
83
+ }
84
+ }
85
+ };
86
+ return null;
87
+ }
88
+ case "metadata_key": {
89
+ const result = findFieldDefinition(workspace, context.key, context.entityContext);
90
+ if (result) return {
91
+ uri: pathToUri(result.file),
92
+ range: {
93
+ start: {
94
+ line: result.location.startPosition.row,
95
+ character: result.location.startPosition.column
96
+ },
97
+ end: {
98
+ line: result.location.endPosition.row,
99
+ character: result.location.endPosition.column
100
+ }
101
+ }
102
+ };
103
+ return null;
104
+ }
105
+ case "section_header": {
106
+ const result = findSectionDefinition(workspace, context.sectionName, context.entityContext);
107
+ if (result) return {
108
+ uri: pathToUri(result.file),
109
+ range: {
110
+ start: {
111
+ line: result.location.startPosition.row,
112
+ character: result.location.startPosition.column
113
+ },
114
+ end: {
115
+ line: result.location.endPosition.row,
116
+ character: result.location.endPosition.column
117
+ }
118
+ }
119
+ };
120
+ return null;
121
+ }
122
+ case "field_name": {
123
+ const result = findFieldDefinition(workspace, context.fieldName, context.entityContext);
124
+ if (result) return {
125
+ uri: pathToUri(result.file),
126
+ range: {
127
+ start: {
128
+ line: result.location.startPosition.row,
129
+ character: result.location.startPosition.column
130
+ },
131
+ end: {
132
+ line: result.location.endPosition.row,
133
+ character: result.location.endPosition.column
134
+ }
135
+ }
136
+ };
137
+ return null;
138
+ }
139
+ case "section_name": {
140
+ const result = findSectionDefinition(workspace, context.sectionName, context.entityContext);
141
+ if (result) return {
142
+ uri: pathToUri(result.file),
143
+ range: {
144
+ start: {
145
+ line: result.location.startPosition.row,
146
+ character: result.location.startPosition.column
147
+ },
148
+ end: {
149
+ line: result.location.endPosition.row,
150
+ character: result.location.endPosition.column
151
+ }
152
+ }
153
+ };
154
+ return null;
155
+ }
156
+ default: return null;
157
+ }
158
+ } catch (error) {
159
+ console.error(`[thalo-lsp] Error in definition handler:`, error);
160
+ return null;
161
+ }
162
+ }
163
+
164
+ //#endregion
165
+ export { handleDefinition };
166
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","names":[],"sources":["../../src/handlers/definition.ts"],"sourcesContent":["import type { Position, Location } from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\nimport {\n findNodeAtPosition,\n findDefinition,\n findEntityDefinition,\n findFieldDefinition,\n findSectionDefinition,\n type Workspace,\n type Position as ThaloPosition,\n} from \"@rejot-dev/thalo\";\nimport { parseDocument } from \"@rejot-dev/thalo/native\";\n\n/**\n * Convert a file path to a URI\n */\nfunction pathToUri(path: string): string {\n // Add file:// prefix if not present\n if (!path.startsWith(\"file://\")) {\n return `file://${encodeURIComponent(path).replace(/%2F/g, \"/\")}`;\n }\n return path;\n}\n\n/**\n * Get the file type from a URI\n */\nfunction getFileType(uri: string): \"thalo\" | \"markdown\" {\n if (uri.endsWith(\".thalo\")) {\n return \"thalo\";\n }\n if (uri.endsWith(\".md\")) {\n return \"markdown\";\n }\n return \"thalo\";\n}\n\n/**\n * Convert LSP Position to thalo Position (both are 0-based).\n */\nfunction lspToThaloPosition(pos: Position): ThaloPosition {\n return { line: pos.line, column: pos.character };\n}\n\n/**\n * Handle textDocument/definition request\n *\n * Finds the definition location for various syntax elements at the given position:\n * - ^link-id → Entry definition\n * - Entity name in instance/alter-entity → define-entity\n * - Metadata key → Field definition in schema\n * - Section header → Section definition in schema\n *\n * Supports both standalone .thalo files and thalo blocks embedded in markdown.\n *\n * @param workspace - The thalo workspace\n * @param document - The text document\n * @param position - The position in the document (line/character)\n * @returns The definition location, or null if not found\n */\nexport function handleDefinition(\n workspace: Workspace,\n document: TextDocument,\n position: Position,\n): Location | null {\n const fileType = getFileType(document.uri);\n\n try {\n // Parse the document\n const parsed = parseDocument(document.getText(), {\n fileType,\n filename: document.uri,\n });\n\n // Find what element is at the position using the AST\n const context = findNodeAtPosition(parsed, lspToThaloPosition(position));\n\n switch (context.kind) {\n case \"link\": {\n // Find the definition for this link\n const result = findDefinition(workspace, context.linkId);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n case \"entity\":\n case \"schema_entity\": {\n // Find the define-entity for this entity\n const entityName = context.kind === \"entity\" ? context.entityName : context.entityName;\n const result = findEntityDefinition(workspace, entityName);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n case \"metadata_key\": {\n // Find the field definition in the schema\n const result = findFieldDefinition(workspace, context.key, context.entityContext);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n case \"section_header\": {\n // Find the section definition in the schema\n const result = findSectionDefinition(workspace, context.sectionName, context.entityContext);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n case \"field_name\": {\n // In schema definitions, go to the field definition itself\n const result = findFieldDefinition(workspace, context.fieldName, context.entityContext);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n case \"section_name\": {\n // In schema definitions, go to the section definition itself\n const result = findSectionDefinition(workspace, context.sectionName, context.entityContext);\n if (result) {\n return {\n uri: pathToUri(result.file),\n range: {\n start: {\n line: result.location.startPosition.row,\n character: result.location.startPosition.column,\n },\n end: {\n line: result.location.endPosition.row,\n character: result.location.endPosition.column,\n },\n },\n };\n }\n return null;\n }\n\n default:\n return null;\n }\n } catch (error) {\n console.error(`[thalo-lsp] Error in definition handler:`, error);\n return null;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { checkDocument } from "@rejot-dev/thalo";
2
+
3
+ //#region src/handlers/diagnostics.ts
4
+ /**
5
+ * Map thalo severity to LSP DiagnosticSeverity
6
+ */
7
+ function mapSeverity(severity) {
8
+ switch (severity) {
9
+ case "error": return 1;
10
+ case "warning": return 2;
11
+ case "info": return 3;
12
+ default: return 3;
13
+ }
14
+ }
15
+ /**
16
+ * Convert a URI to a file path
17
+ */
18
+ function uriToPath(uri) {
19
+ if (uri.startsWith("file://")) return decodeURIComponent(uri.slice(7));
20
+ return uri;
21
+ }
22
+ /**
23
+ * Convert a thalo Diagnostic to an LSP Diagnostic
24
+ */
25
+ function convertDiagnostic(diagnostic) {
26
+ return {
27
+ range: {
28
+ start: {
29
+ line: diagnostic.location.startPosition.row,
30
+ character: diagnostic.location.startPosition.column
31
+ },
32
+ end: {
33
+ line: diagnostic.location.endPosition.row,
34
+ character: diagnostic.location.endPosition.column
35
+ }
36
+ },
37
+ severity: mapSeverity(diagnostic.severity),
38
+ code: diagnostic.code,
39
+ source: "thalo",
40
+ message: diagnostic.message
41
+ };
42
+ }
43
+ /**
44
+ * Get diagnostics for a document
45
+ *
46
+ * @param workspace - The thalo workspace
47
+ * @param textDocument - The LSP text document
48
+ * @returns Array of LSP diagnostics
49
+ */
50
+ function getDiagnostics(workspace, textDocument) {
51
+ const path = uriToPath(textDocument.uri);
52
+ if (!workspace.getModel(path)) return [];
53
+ try {
54
+ return checkDocument(path, workspace).map(convertDiagnostic);
55
+ } catch (error) {
56
+ console.error(`[thalo-lsp] Error checking ${path}:`, error);
57
+ return [{
58
+ range: {
59
+ start: {
60
+ line: 0,
61
+ character: 0
62
+ },
63
+ end: {
64
+ line: 0,
65
+ character: 0
66
+ }
67
+ },
68
+ severity: 1,
69
+ source: "thalo",
70
+ message: `Parse error: ${error instanceof Error ? error.message : String(error)}`
71
+ }];
72
+ }
73
+ }
74
+
75
+ //#endregion
76
+ export { getDiagnostics };
77
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","names":[],"sources":["../../src/handlers/diagnostics.ts"],"sourcesContent":["import type { Diagnostic as LspDiagnostic, DiagnosticSeverity } from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\nimport {\n checkDocument,\n type Workspace,\n type Diagnostic as ThaloDiagnostic,\n type Severity,\n} from \"@rejot-dev/thalo\";\n\n/**\n * Map thalo severity to LSP DiagnosticSeverity\n */\nfunction mapSeverity(severity: Severity): DiagnosticSeverity {\n switch (severity) {\n case \"error\":\n return 1; // DiagnosticSeverity.Error\n case \"warning\":\n return 2; // DiagnosticSeverity.Warning\n case \"info\":\n return 3; // DiagnosticSeverity.Information\n default:\n return 3;\n }\n}\n\n/**\n * Convert a URI to a file path\n */\nfunction uriToPath(uri: string): string {\n if (uri.startsWith(\"file://\")) {\n return decodeURIComponent(uri.slice(7));\n }\n return uri;\n}\n\n/**\n * Convert a thalo Diagnostic to an LSP Diagnostic\n */\nfunction convertDiagnostic(diagnostic: ThaloDiagnostic): LspDiagnostic {\n return {\n range: {\n start: {\n line: diagnostic.location.startPosition.row,\n character: diagnostic.location.startPosition.column,\n },\n end: {\n line: diagnostic.location.endPosition.row,\n character: diagnostic.location.endPosition.column,\n },\n },\n severity: mapSeverity(diagnostic.severity),\n code: diagnostic.code,\n source: \"thalo\",\n message: diagnostic.message,\n };\n}\n\n/**\n * Get diagnostics for a document\n *\n * @param workspace - The thalo workspace\n * @param textDocument - The LSP text document\n * @returns Array of LSP diagnostics\n */\nexport function getDiagnostics(workspace: Workspace, textDocument: TextDocument): LspDiagnostic[] {\n const path = uriToPath(textDocument.uri);\n const model = workspace.getModel(path);\n\n if (!model) {\n return [];\n }\n\n try {\n const thaloDiagnostics = checkDocument(path, workspace);\n return thaloDiagnostics.map(convertDiagnostic);\n } catch (error) {\n // Return a diagnostic for parse errors\n console.error(`[thalo-lsp] Error checking ${path}:`, error);\n return [\n {\n range: {\n start: { line: 0, character: 0 },\n end: { line: 0, character: 0 },\n },\n severity: 1, // Error\n source: \"thalo\",\n message: `Parse error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ];\n }\n}\n"],"mappings":";;;;;;AAYA,SAAS,YAAY,UAAwC;AAC3D,SAAQ,UAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;;;;AAOb,SAAS,UAAU,KAAqB;AACtC,KAAI,IAAI,WAAW,UAAU,CAC3B,QAAO,mBAAmB,IAAI,MAAM,EAAE,CAAC;AAEzC,QAAO;;;;;AAMT,SAAS,kBAAkB,YAA4C;AACrE,QAAO;EACL,OAAO;GACL,OAAO;IACL,MAAM,WAAW,SAAS,cAAc;IACxC,WAAW,WAAW,SAAS,cAAc;IAC9C;GACD,KAAK;IACH,MAAM,WAAW,SAAS,YAAY;IACtC,WAAW,WAAW,SAAS,YAAY;IAC5C;GACF;EACD,UAAU,YAAY,WAAW,SAAS;EAC1C,MAAM,WAAW;EACjB,QAAQ;EACR,SAAS,WAAW;EACrB;;;;;;;;;AAUH,SAAgB,eAAe,WAAsB,cAA6C;CAChG,MAAM,OAAO,UAAU,aAAa,IAAI;AAGxC,KAAI,CAFU,UAAU,SAAS,KAAK,CAGpC,QAAO,EAAE;AAGX,KAAI;AAEF,SADyB,cAAc,MAAM,UAAU,CAC/B,IAAI,kBAAkB;UACvC,OAAO;AAEd,UAAQ,MAAM,8BAA8B,KAAK,IAAI,MAAM;AAC3D,SAAO,CACL;GACE,OAAO;IACL,OAAO;KAAE,MAAM;KAAG,WAAW;KAAG;IAChC,KAAK;KAAE,MAAM;KAAG,WAAW;KAAG;IAC/B;GACD,UAAU;GACV,QAAQ;GACR,SAAS,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAChF,CACF"}
@@ -0,0 +1,73 @@
1
+ import { parseDocument } from "@rejot-dev/thalo/native";
2
+ import { findNodeAtPosition, getHoverInfo } from "@rejot-dev/thalo";
3
+
4
+ //#region src/handlers/hover.ts
5
+ /**
6
+ * Get the file type from a URI
7
+ */
8
+ function getFileType(uri) {
9
+ if (uri.endsWith(".thalo")) return "thalo";
10
+ if (uri.endsWith(".md")) return "markdown";
11
+ return "thalo";
12
+ }
13
+ /**
14
+ * Convert LSP Position to thalo Position (both are 0-based).
15
+ */
16
+ function lspToThaloPosition(pos) {
17
+ return {
18
+ line: pos.line,
19
+ column: pos.character
20
+ };
21
+ }
22
+ /**
23
+ * Handle textDocument/hover request
24
+ *
25
+ * Provides hover information for various syntax elements:
26
+ * - ^link-id: Shows target entry details
27
+ * - #tag: Shows tag usage statistics
28
+ * - Directives: Shows documentation
29
+ * - Entity names: Shows schema with fields and sections
30
+ * - Metadata keys: Shows field type and description
31
+ * - Type expressions: Shows type documentation
32
+ * - Section headers: Shows section description
33
+ * - Timestamps: Shows entry info or link reference hint
34
+ *
35
+ * Supports both standalone .thalo files and thalo blocks embedded in markdown.
36
+ *
37
+ * @param workspace - The thalo workspace
38
+ * @param document - The text document
39
+ * @param position - The hover position
40
+ * @returns Hover information, or null if nothing to show
41
+ */
42
+ function handleHover(workspace, document, position) {
43
+ const fileType = getFileType(document.uri);
44
+ try {
45
+ const result = getHoverInfo(workspace, findNodeAtPosition(parseDocument(document.getText(), {
46
+ fileType,
47
+ filename: document.uri
48
+ }), lspToThaloPosition(position)));
49
+ if (!result) return null;
50
+ const hover = { contents: {
51
+ kind: "markdown",
52
+ value: result.content
53
+ } };
54
+ if (result.range) hover.range = {
55
+ start: {
56
+ line: result.range.startPosition.row,
57
+ character: result.range.startPosition.column
58
+ },
59
+ end: {
60
+ line: result.range.endPosition.row,
61
+ character: result.range.endPosition.column
62
+ }
63
+ };
64
+ return hover;
65
+ } catch (error) {
66
+ console.error(`[thalo-lsp] Error in hover handler:`, error);
67
+ return null;
68
+ }
69
+ }
70
+
71
+ //#endregion
72
+ export { handleHover };
73
+ //# sourceMappingURL=hover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hover.js","names":["hover: Hover"],"sources":["../../src/handlers/hover.ts"],"sourcesContent":["import type { Hover, Position } from \"vscode-languageserver\";\nimport type { TextDocument } from \"vscode-languageserver-textdocument\";\nimport {\n findNodeAtPosition,\n getHoverInfo,\n type Workspace,\n type Position as ThaloPosition,\n} from \"@rejot-dev/thalo\";\nimport { parseDocument } from \"@rejot-dev/thalo/native\";\n\n/**\n * Get the file type from a URI\n */\nfunction getFileType(uri: string): \"thalo\" | \"markdown\" {\n if (uri.endsWith(\".thalo\")) {\n return \"thalo\";\n }\n if (uri.endsWith(\".md\")) {\n return \"markdown\";\n }\n return \"thalo\";\n}\n\n/**\n * Convert LSP Position to thalo Position (both are 0-based).\n */\nfunction lspToThaloPosition(pos: Position): ThaloPosition {\n return { line: pos.line, column: pos.character };\n}\n\n/**\n * Handle textDocument/hover request\n *\n * Provides hover information for various syntax elements:\n * - ^link-id: Shows target entry details\n * - #tag: Shows tag usage statistics\n * - Directives: Shows documentation\n * - Entity names: Shows schema with fields and sections\n * - Metadata keys: Shows field type and description\n * - Type expressions: Shows type documentation\n * - Section headers: Shows section description\n * - Timestamps: Shows entry info or link reference hint\n *\n * Supports both standalone .thalo files and thalo blocks embedded in markdown.\n *\n * @param workspace - The thalo workspace\n * @param document - The text document\n * @param position - The hover position\n * @returns Hover information, or null if nothing to show\n */\nexport function handleHover(\n workspace: Workspace,\n document: TextDocument,\n position: Position,\n): Hover | null {\n const fileType = getFileType(document.uri);\n\n try {\n // Parse the document\n const parsed = parseDocument(document.getText(), {\n fileType,\n filename: document.uri,\n });\n\n // Find what element is at the position using the AST\n const context = findNodeAtPosition(parsed, lspToThaloPosition(position));\n\n // Get hover info from the service\n const result = getHoverInfo(workspace, context);\n\n if (!result) {\n return null;\n }\n\n // Convert to LSP Hover format\n const hover: Hover = {\n contents: {\n kind: \"markdown\",\n value: result.content,\n },\n };\n\n // Add range if available\n if (result.range) {\n hover.range = {\n start: {\n line: result.range.startPosition.row,\n character: result.range.startPosition.column,\n },\n end: {\n line: result.range.endPosition.row,\n character: result.range.endPosition.column,\n },\n };\n }\n\n return hover;\n } catch (error) {\n console.error(`[thalo-lsp] Error in hover handler:`, error);\n return null;\n }\n}\n"],"mappings":";;;;;;;AAaA,SAAS,YAAY,KAAmC;AACtD,KAAI,IAAI,SAAS,SAAS,CACxB,QAAO;AAET,KAAI,IAAI,SAAS,MAAM,CACrB,QAAO;AAET,QAAO;;;;;AAMT,SAAS,mBAAmB,KAA8B;AACxD,QAAO;EAAE,MAAM,IAAI;EAAM,QAAQ,IAAI;EAAW;;;;;;;;;;;;;;;;;;;;;;AAuBlD,SAAgB,YACd,WACA,UACA,UACc;CACd,MAAM,WAAW,YAAY,SAAS,IAAI;AAE1C,KAAI;EAWF,MAAM,SAAS,aAAa,WAHZ,mBAND,cAAc,SAAS,SAAS,EAAE;GAC/C;GACA,UAAU,SAAS;GACpB,CAAC,EAGyC,mBAAmB,SAAS,CAAC,CAGzB;AAE/C,MAAI,CAAC,OACH,QAAO;EAIT,MAAMA,QAAe,EACnB,UAAU;GACR,MAAM;GACN,OAAO,OAAO;GACf,EACF;AAGD,MAAI,OAAO,MACT,OAAM,QAAQ;GACZ,OAAO;IACL,MAAM,OAAO,MAAM,cAAc;IACjC,WAAW,OAAO,MAAM,cAAc;IACvC;GACD,KAAK;IACH,MAAM,OAAO,MAAM,YAAY;IAC/B,WAAW,OAAO,MAAM,YAAY;IACrC;GACF;AAGH,SAAO;UACA,OAAO;AACd,UAAQ,MAAM,uCAAuC,MAAM;AAC3D,SAAO"}
@@ -0,0 +1,233 @@
1
+ import { parseDocument } from "@rejot-dev/thalo/native";
2
+ import { findEntityReferences, findFieldReferences, findNodeAtPosition, findReferences, findSectionReferences, findTagReferences } from "@rejot-dev/thalo";
3
+
4
+ //#region src/handlers/references.ts
5
+ /**
6
+ * Convert a file path to a URI
7
+ */
8
+ function pathToUri(path) {
9
+ if (!path.startsWith("file://")) return `file://${encodeURIComponent(path).replace(/%2F/g, "/")}`;
10
+ return path;
11
+ }
12
+ /**
13
+ * Get the file type from a URI
14
+ */
15
+ function getFileType(uri) {
16
+ if (uri.endsWith(".thalo")) return "thalo";
17
+ if (uri.endsWith(".md")) return "markdown";
18
+ return "thalo";
19
+ }
20
+ /**
21
+ * Convert LSP Position to thalo Position (both are 0-based).
22
+ */
23
+ function lspToThaloPosition(pos) {
24
+ return {
25
+ line: pos.line,
26
+ column: pos.character
27
+ };
28
+ }
29
+ /**
30
+ * Handle textDocument/references request
31
+ *
32
+ * Finds all references to various syntax elements at the given position:
33
+ * - ^link-id → All usages of that link
34
+ * - #tag → All entries with that tag
35
+ * - Entity name → All entries using that entity type
36
+ * - Metadata key → All entries using that field
37
+ * - Section header → All entries with that section
38
+ *
39
+ * Supports both standalone .thalo files and thalo blocks embedded in markdown.
40
+ *
41
+ * @param workspace - The thalo workspace
42
+ * @param document - The text document
43
+ * @param position - The position in the document (line/character)
44
+ * @param context - Reference context (includeDeclaration)
45
+ * @returns Array of reference locations, or null if not found
46
+ */
47
+ function handleReferences(workspace, document, position, context) {
48
+ const fileType = getFileType(document.uri);
49
+ try {
50
+ const nodeContext = findNodeAtPosition(parseDocument(document.getText(), {
51
+ fileType,
52
+ filename: document.uri
53
+ }), lspToThaloPosition(position));
54
+ switch (nodeContext.kind) {
55
+ case "link": {
56
+ const result = findReferences(workspace, nodeContext.linkId, context.includeDeclaration);
57
+ if (result) return result.locations.map((loc) => ({
58
+ uri: pathToUri(loc.file),
59
+ range: {
60
+ start: {
61
+ line: loc.location.startPosition.row,
62
+ character: loc.location.startPosition.column
63
+ },
64
+ end: {
65
+ line: loc.location.endPosition.row,
66
+ character: loc.location.endPosition.column
67
+ }
68
+ }
69
+ }));
70
+ return null;
71
+ }
72
+ case "tag": return findTagReferences(workspace, nodeContext.tagName).references.map((ref) => ({
73
+ uri: pathToUri(ref.file),
74
+ range: {
75
+ start: {
76
+ line: ref.location.startPosition.row,
77
+ character: ref.location.startPosition.column
78
+ },
79
+ end: {
80
+ line: ref.location.endPosition.row,
81
+ character: ref.location.endPosition.column
82
+ }
83
+ }
84
+ }));
85
+ case "entity":
86
+ case "schema_entity": return findEntityReferences(workspace, nodeContext.kind === "entity" ? nodeContext.entityName : nodeContext.entityName, context.includeDeclaration).locations.map((loc) => ({
87
+ uri: pathToUri(loc.file),
88
+ range: {
89
+ start: {
90
+ line: loc.location.startPosition.row,
91
+ character: loc.location.startPosition.column
92
+ },
93
+ end: {
94
+ line: loc.location.endPosition.row,
95
+ character: loc.location.endPosition.column
96
+ }
97
+ }
98
+ }));
99
+ case "metadata_key": {
100
+ const result = findFieldReferences(workspace, nodeContext.key, nodeContext.entityContext);
101
+ const locations = [];
102
+ if (context.includeDeclaration && result.definition) locations.push({
103
+ uri: pathToUri(result.definition.file),
104
+ range: {
105
+ start: {
106
+ line: result.definition.location.startPosition.row,
107
+ character: result.definition.location.startPosition.column
108
+ },
109
+ end: {
110
+ line: result.definition.location.endPosition.row,
111
+ character: result.definition.location.endPosition.column
112
+ }
113
+ }
114
+ });
115
+ for (const ref of result.references) locations.push({
116
+ uri: pathToUri(ref.file),
117
+ range: {
118
+ start: {
119
+ line: ref.location.startPosition.row,
120
+ character: ref.location.startPosition.column
121
+ },
122
+ end: {
123
+ line: ref.location.endPosition.row,
124
+ character: ref.location.endPosition.column
125
+ }
126
+ }
127
+ });
128
+ return locations;
129
+ }
130
+ case "section_header": {
131
+ const result = findSectionReferences(workspace, nodeContext.sectionName, nodeContext.entityContext);
132
+ const locations = [];
133
+ if (context.includeDeclaration && result.definition) locations.push({
134
+ uri: pathToUri(result.definition.file),
135
+ range: {
136
+ start: {
137
+ line: result.definition.location.startPosition.row,
138
+ character: result.definition.location.startPosition.column
139
+ },
140
+ end: {
141
+ line: result.definition.location.endPosition.row,
142
+ character: result.definition.location.endPosition.column
143
+ }
144
+ }
145
+ });
146
+ for (const ref of result.references) locations.push({
147
+ uri: pathToUri(ref.file),
148
+ range: {
149
+ start: {
150
+ line: ref.location.startPosition.row,
151
+ character: ref.location.startPosition.column
152
+ },
153
+ end: {
154
+ line: ref.location.endPosition.row,
155
+ character: ref.location.endPosition.column
156
+ }
157
+ }
158
+ });
159
+ return locations;
160
+ }
161
+ case "field_name": {
162
+ const result = findFieldReferences(workspace, nodeContext.fieldName, nodeContext.entityContext);
163
+ const locations = [];
164
+ if (context.includeDeclaration && result.definition) locations.push({
165
+ uri: pathToUri(result.definition.file),
166
+ range: {
167
+ start: {
168
+ line: result.definition.location.startPosition.row,
169
+ character: result.definition.location.startPosition.column
170
+ },
171
+ end: {
172
+ line: result.definition.location.endPosition.row,
173
+ character: result.definition.location.endPosition.column
174
+ }
175
+ }
176
+ });
177
+ for (const ref of result.references) locations.push({
178
+ uri: pathToUri(ref.file),
179
+ range: {
180
+ start: {
181
+ line: ref.location.startPosition.row,
182
+ character: ref.location.startPosition.column
183
+ },
184
+ end: {
185
+ line: ref.location.endPosition.row,
186
+ character: ref.location.endPosition.column
187
+ }
188
+ }
189
+ });
190
+ return locations;
191
+ }
192
+ case "section_name": {
193
+ const result = findSectionReferences(workspace, nodeContext.sectionName, nodeContext.entityContext);
194
+ const locations = [];
195
+ if (context.includeDeclaration && result.definition) locations.push({
196
+ uri: pathToUri(result.definition.file),
197
+ range: {
198
+ start: {
199
+ line: result.definition.location.startPosition.row,
200
+ character: result.definition.location.startPosition.column
201
+ },
202
+ end: {
203
+ line: result.definition.location.endPosition.row,
204
+ character: result.definition.location.endPosition.column
205
+ }
206
+ }
207
+ });
208
+ for (const ref of result.references) locations.push({
209
+ uri: pathToUri(ref.file),
210
+ range: {
211
+ start: {
212
+ line: ref.location.startPosition.row,
213
+ character: ref.location.startPosition.column
214
+ },
215
+ end: {
216
+ line: ref.location.endPosition.row,
217
+ character: ref.location.endPosition.column
218
+ }
219
+ }
220
+ });
221
+ return locations;
222
+ }
223
+ default: return null;
224
+ }
225
+ } catch (error) {
226
+ console.error(`[thalo-lsp] Error in references handler:`, error);
227
+ return null;
228
+ }
229
+ }
230
+
231
+ //#endregion
232
+ export { handleReferences };
233
+ //# sourceMappingURL=references.js.map