treelsp 0.0.1

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 (46) hide show
  1. package/LICENSE +21 -0
  2. package/dist/codegen/ast-types.d.ts +9 -0
  3. package/dist/codegen/grammar.d.ts +9 -0
  4. package/dist/codegen/highlights.d.ts +9 -0
  5. package/dist/codegen/index.d.ts +9 -0
  6. package/dist/codegen/index.js +983 -0
  7. package/dist/codegen/locals.d.ts +9 -0
  8. package/dist/codegen/server.d.ts +31 -0
  9. package/dist/defaults/completion.d.ts +18 -0
  10. package/dist/defaults/hover.d.ts +11 -0
  11. package/dist/defaults/index.d.ts +4 -0
  12. package/dist/defaults/symbols.d.ts +10 -0
  13. package/dist/defaults/validation.d.ts +19 -0
  14. package/dist/define.d.ts +25 -0
  15. package/dist/definition/grammar.d.ts +36 -0
  16. package/dist/definition/index.d.ts +33 -0
  17. package/dist/definition/lsp.d.ts +93 -0
  18. package/dist/definition/semantic.d.ts +80 -0
  19. package/dist/definition/validation.d.ts +62 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.js +148 -0
  22. package/dist/runtime/index.d.ts +19 -0
  23. package/dist/runtime/index.js +3 -0
  24. package/dist/runtime/lsp/completion.d.ts +24 -0
  25. package/dist/runtime/lsp/context.d.ts +59 -0
  26. package/dist/runtime/lsp/definition.d.ts +23 -0
  27. package/dist/runtime/lsp/diagnostics.d.ts +33 -0
  28. package/dist/runtime/lsp/documents.d.ts +55 -0
  29. package/dist/runtime/lsp/hover.d.ts +30 -0
  30. package/dist/runtime/lsp/references.d.ts +28 -0
  31. package/dist/runtime/lsp/rename.d.ts +50 -0
  32. package/dist/runtime/lsp/semantic-tokens.d.ts +32 -0
  33. package/dist/runtime/lsp/server.d.ts +52 -0
  34. package/dist/runtime/lsp/symbols.d.ts +36 -0
  35. package/dist/runtime/parser/index.d.ts +7 -0
  36. package/dist/runtime/parser/node.d.ts +185 -0
  37. package/dist/runtime/parser/tree.d.ts +153 -0
  38. package/dist/runtime/parser/wasm.d.ts +32 -0
  39. package/dist/runtime/scope/index.d.ts +7 -0
  40. package/dist/runtime/scope/resolver.d.ts +49 -0
  41. package/dist/runtime/scope/scope.d.ts +127 -0
  42. package/dist/runtime/scope/workspace.d.ts +104 -0
  43. package/dist/runtime-CnCx2QS4.js +1914 -0
  44. package/dist/server/index.d.ts +25 -0
  45. package/dist/server/index.js +230 -0
  46. package/package.json +64 -0
@@ -0,0 +1,25 @@
1
+ /**
2
+ * LSP server transport for Node.js stdio
3
+ *
4
+ * Provides startStdioServer() that wires a LanguageService to a
5
+ * vscode-languageserver connection over stdio. Separate from treelsp/runtime
6
+ * to keep the runtime browser-compatible.
7
+ */
8
+ import type { LanguageDefinition } from '../definition/index.js';
9
+ /**
10
+ * Options for starting a stdio LSP server
11
+ */
12
+ export interface StdioServerOptions {
13
+ /** Language definition from defineLanguage() */
14
+ definition: LanguageDefinition;
15
+ /** Absolute path to grammar.wasm file */
16
+ wasmPath: string;
17
+ }
18
+ /**
19
+ * Start an LSP server over stdio transport
20
+ *
21
+ * Creates a vscode-languageserver connection, wires all LSP protocol handlers
22
+ * to a LanguageService, and starts listening. This is the main entry point
23
+ * for generated LSP servers.
24
+ */
25
+ export declare function startStdioServer(options: StdioServerOptions): void;
@@ -0,0 +1,230 @@
1
+ import { COMPLETION_KIND_MAP, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES, createDocumentState, createServer } from "../runtime-CnCx2QS4.js";
2
+ import { DiagnosticSeverity, ProposedFeatures, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/lib/node/main.js";
3
+ import { TextDocument } from "vscode-languageserver-textdocument";
4
+
5
+ //#region src/server/index.ts
6
+ /**
7
+ * Convert internal CompletionItem to LSP protocol CompletionItem
8
+ */
9
+ function toLspCompletionItem(item) {
10
+ const lspItem = { label: item.label };
11
+ if (item.kind) lspItem.kind = COMPLETION_KIND_MAP[item.kind];
12
+ if (item.detail) lspItem.detail = item.detail;
13
+ if (item.documentation) lspItem.documentation = item.documentation;
14
+ if (item.insertText) lspItem.insertText = item.insertText;
15
+ return lspItem;
16
+ }
17
+ function getRangeStr(start, end) {
18
+ return `${start.line}:${start.character}-${end.line}:${end.character}`;
19
+ }
20
+ /**
21
+ * Start an LSP server over stdio transport
22
+ *
23
+ * Creates a vscode-languageserver connection, wires all LSP protocol handlers
24
+ * to a LanguageService, and starts listening. This is the main entry point
25
+ * for generated LSP servers.
26
+ */
27
+ function startStdioServer(options) {
28
+ const { definition, wasmPath } = options;
29
+ const langId = definition.name.toLowerCase();
30
+ const connection = createConnection(ProposedFeatures.all);
31
+ const textDocuments = new TextDocuments(TextDocument);
32
+ const service = createServer(definition);
33
+ const documentStates = /* @__PURE__ */ new Map();
34
+ const pendingInits = /* @__PURE__ */ new Map();
35
+ async function getDocumentState(textDoc) {
36
+ const existing = documentStates.get(textDoc.uri);
37
+ if (existing) return existing;
38
+ let promise = pendingInits.get(textDoc.uri);
39
+ if (!promise) {
40
+ promise = createDocumentState(wasmPath, {
41
+ uri: textDoc.uri,
42
+ version: textDoc.version,
43
+ languageId: langId
44
+ }, textDoc.getText());
45
+ pendingInits.set(textDoc.uri, promise);
46
+ }
47
+ const state = await promise;
48
+ documentStates.set(textDoc.uri, state);
49
+ pendingInits.delete(textDoc.uri);
50
+ return state;
51
+ }
52
+ const severityMap = {
53
+ error: DiagnosticSeverity.Error,
54
+ warning: DiagnosticSeverity.Warning,
55
+ info: DiagnosticSeverity.Information,
56
+ hint: DiagnosticSeverity.Hint
57
+ };
58
+ async function validateDocument(textDoc) {
59
+ const state = await getDocumentState(textDoc);
60
+ const diagnostics = service.computeDiagnostics(state);
61
+ connection.console.log(`[validation] ${diagnostics.map((d) => `range=${getRangeStr(d.range.start, d.range.end)} message=${d.message}`).join(", ")}`);
62
+ connection.sendDiagnostics({
63
+ uri: textDoc.uri,
64
+ version: textDoc.version,
65
+ diagnostics: diagnostics.map((d) => {
66
+ const lspDiag = {
67
+ range: d.range,
68
+ severity: severityMap[d.severity],
69
+ message: d.message,
70
+ source: d.source ?? langId
71
+ };
72
+ if (d.code) lspDiag.code = d.code;
73
+ return lspDiag;
74
+ })
75
+ });
76
+ }
77
+ connection.onInitialize(() => ({ capabilities: {
78
+ textDocumentSync: TextDocumentSyncKind.Full,
79
+ hoverProvider: true,
80
+ definitionProvider: true,
81
+ referencesProvider: true,
82
+ completionProvider: { resolveProvider: false },
83
+ renameProvider: { prepareProvider: true },
84
+ documentSymbolProvider: true,
85
+ semanticTokensProvider: {
86
+ legend: {
87
+ tokenTypes: [...SEMANTIC_TOKEN_TYPES],
88
+ tokenModifiers: [...SEMANTIC_TOKEN_MODIFIERS]
89
+ },
90
+ full: true
91
+ }
92
+ } }));
93
+ textDocuments.onDidOpen(async (event) => {
94
+ connection.console.log(`[open] ${event.document.uri} v${event.document.version}`);
95
+ await validateDocument(event.document);
96
+ connection.console.log(`[open] done ${event.document.uri}`);
97
+ });
98
+ textDocuments.onDidChangeContent(async (event) => {
99
+ connection.console.log(`[change] ${event.document.uri} v${event.document.version}`);
100
+ const state = await getDocumentState(event.document);
101
+ state.update(event.document.getText(), event.document.version);
102
+ service.documents.change(state);
103
+ await validateDocument(event.document);
104
+ });
105
+ textDocuments.onDidClose((event) => {
106
+ const state = documentStates.get(event.document.uri);
107
+ if (state) {
108
+ service.documents.close(event.document.uri);
109
+ state.dispose();
110
+ documentStates.delete(event.document.uri);
111
+ }
112
+ connection.sendDiagnostics({
113
+ uri: event.document.uri,
114
+ diagnostics: []
115
+ });
116
+ });
117
+ connection.onHover(async (params) => {
118
+ const textDoc = textDocuments.get(params.textDocument.uri);
119
+ if (!textDoc) return null;
120
+ const state = await getDocumentState(textDoc);
121
+ const result = service.provideHover(state, params.position);
122
+ if (!result) return null;
123
+ return {
124
+ contents: {
125
+ kind: "markdown",
126
+ value: result.contents
127
+ },
128
+ range: result.range
129
+ };
130
+ });
131
+ connection.onDefinition(async (params) => {
132
+ const textDoc = textDocuments.get(params.textDocument.uri);
133
+ if (!textDoc) {
134
+ connection.console.log(`[definition] no textDoc for ${params.textDocument.uri}`);
135
+ return null;
136
+ }
137
+ try {
138
+ const state = await getDocumentState(textDoc);
139
+ const pos = params.position;
140
+ const node = state.root.descendantForPosition(pos);
141
+ connection.console.log(`[definition] pos=${pos.line}:${pos.character} node=${node.type} "${node.text}" range=${getRangeStr(node.startPosition, node.endPosition)}`);
142
+ const result = service.provideDefinition(state, params.position);
143
+ connection.console.log(`[definition] result=${result ? `${result.uri} ${result.range.start.line}:${result.range.start.character}` : "null"}`);
144
+ if (!result) return null;
145
+ return {
146
+ uri: result.uri,
147
+ range: result.range
148
+ };
149
+ } catch (e) {
150
+ connection.console.error(`[definition] error: ${String(e)}`);
151
+ return null;
152
+ }
153
+ });
154
+ connection.onReferences(async (params) => {
155
+ const textDoc = textDocuments.get(params.textDocument.uri);
156
+ if (!textDoc) {
157
+ connection.console.log(`[references] no textDoc for ${params.textDocument.uri}`);
158
+ return [];
159
+ }
160
+ try {
161
+ const state = await getDocumentState(textDoc);
162
+ const pos = params.position;
163
+ const node = state.root.descendantForPosition(pos);
164
+ connection.console.log(`[references] pos=${pos.line}:${pos.character} node=${node.type} "${node.text}" range=${node.startPosition.line}:${node.startPosition.character}-${node.endPosition.line}:${node.endPosition.character}`);
165
+ const results = service.provideReferences(state, params.position);
166
+ connection.console.log(`[references] found ${results.length} references`);
167
+ return results.map((r) => ({
168
+ uri: r.uri,
169
+ range: r.range
170
+ }));
171
+ } catch (e) {
172
+ connection.console.error(`[references] error: ${String(e)}`);
173
+ return [];
174
+ }
175
+ });
176
+ connection.onCompletion(async (params) => {
177
+ const textDoc = textDocuments.get(params.textDocument.uri);
178
+ if (!textDoc) return [];
179
+ const state = await getDocumentState(textDoc);
180
+ const items = service.provideCompletion(state, params.position);
181
+ return items.map(toLspCompletionItem);
182
+ });
183
+ connection.onPrepareRename(async (params) => {
184
+ const textDoc = textDocuments.get(params.textDocument.uri);
185
+ if (!textDoc) return null;
186
+ const state = await getDocumentState(textDoc);
187
+ return service.prepareRename(state, params.position);
188
+ });
189
+ connection.onRenameRequest(async (params) => {
190
+ const textDoc = textDocuments.get(params.textDocument.uri);
191
+ if (!textDoc) return null;
192
+ const state = await getDocumentState(textDoc);
193
+ const result = service.provideRename(state, params.position, params.newName);
194
+ if (!result) return null;
195
+ const changes = {};
196
+ for (const [uri, edits] of Object.entries(result.changes)) changes[uri] = edits.map((e) => ({
197
+ range: e.range,
198
+ newText: e.newText
199
+ }));
200
+ return { changes };
201
+ });
202
+ connection.onDocumentSymbol(async (params) => {
203
+ const textDoc = textDocuments.get(params.textDocument.uri);
204
+ if (!textDoc) return [];
205
+ const state = await getDocumentState(textDoc);
206
+ const symbols = service.provideSymbols(state);
207
+ return symbols.map((s) => {
208
+ const sym = {
209
+ name: s.name,
210
+ kind: s.kindNumber,
211
+ range: s.range,
212
+ selectionRange: s.selectionRange,
213
+ children: []
214
+ };
215
+ if (s.detail) sym.detail = s.detail;
216
+ return sym;
217
+ });
218
+ });
219
+ connection.languages.semanticTokens.on(async (params) => {
220
+ const textDoc = textDocuments.get(params.textDocument.uri);
221
+ if (!textDoc) return { data: [] };
222
+ const state = await getDocumentState(textDoc);
223
+ return service.provideSemanticTokensFull(state);
224
+ });
225
+ textDocuments.listen(connection);
226
+ connection.listen();
227
+ }
228
+
229
+ //#endregion
230
+ export { startStdioServer };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "treelsp",
3
+ "version": "0.0.1",
4
+ "description": "A Langium-style LSP generator using Tree-sitter as the parsing backend",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./codegen": {
14
+ "types": "./dist/codegen/index.d.ts",
15
+ "import": "./dist/codegen/index.js"
16
+ },
17
+ "./runtime": {
18
+ "types": "./dist/runtime/index.d.ts",
19
+ "import": "./dist/runtime/index.js"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/server/index.d.ts",
23
+ "import": "./dist/server/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md"
29
+ ],
30
+ "keywords": [
31
+ "lsp",
32
+ "language-server",
33
+ "tree-sitter",
34
+ "parser",
35
+ "typescript"
36
+ ],
37
+ "author": "Dhrubomoy Das Gupta",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/dhrubomoy/treelsp.git",
42
+ "directory": "packages/treelsp"
43
+ },
44
+ "homepage": "https://github.com/dhrubomoy/treelsp#readme",
45
+ "bugs": "https://github.com/dhrubomoy/treelsp/issues",
46
+ "dependencies": {
47
+ "vscode-languageserver": "^9.0.1",
48
+ "vscode-languageserver-textdocument": "^1.0.12",
49
+ "web-tree-sitter": "^0.24.4"
50
+ },
51
+ "devDependencies": {
52
+ "tsdown": "^0.2.17",
53
+ "typescript": "^5.7.3",
54
+ "vitest": "^2.1.8"
55
+ },
56
+ "scripts": {
57
+ "build": "tsdown",
58
+ "dev": "tsdown --watch",
59
+ "test": "vitest run",
60
+ "test:watch": "vitest",
61
+ "lint": "eslint src",
62
+ "clean": "rm -rf dist *.tsbuildinfo"
63
+ }
64
+ }