@nowline/lsp 0.4.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 (53) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +49 -0
  3. package/dist/index.d.ts +9 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/nowline-lsp-module.d.ts +29 -0
  8. package/dist/nowline-lsp-module.d.ts.map +1 -0
  9. package/dist/nowline-lsp-module.js +49 -0
  10. package/dist/nowline-lsp-module.js.map +1 -0
  11. package/dist/providers/completion.d.ts +28 -0
  12. package/dist/providers/completion.d.ts.map +1 -0
  13. package/dist/providers/completion.js +175 -0
  14. package/dist/providers/completion.js.map +1 -0
  15. package/dist/providers/definition.d.ts +18 -0
  16. package/dist/providers/definition.d.ts.map +1 -0
  17. package/dist/providers/definition.js +39 -0
  18. package/dist/providers/definition.js.map +1 -0
  19. package/dist/providers/document-symbols.d.ts +24 -0
  20. package/dist/providers/document-symbols.d.ts.map +1 -0
  21. package/dist/providers/document-symbols.js +126 -0
  22. package/dist/providers/document-symbols.js.map +1 -0
  23. package/dist/providers/hover.d.ts +18 -0
  24. package/dist/providers/hover.d.ts.map +1 -0
  25. package/dist/providers/hover.js +78 -0
  26. package/dist/providers/hover.js.map +1 -0
  27. package/dist/providers/references.d.ts +19 -0
  28. package/dist/providers/references.d.ts.map +1 -0
  29. package/dist/providers/references.js +70 -0
  30. package/dist/providers/references.js.map +1 -0
  31. package/dist/providers/rename.d.ts +16 -0
  32. package/dist/providers/rename.d.ts.map +1 -0
  33. package/dist/providers/rename.js +72 -0
  34. package/dist/providers/rename.js.map +1 -0
  35. package/dist/references/ast-utils.d.ts +126 -0
  36. package/dist/references/ast-utils.d.ts.map +1 -0
  37. package/dist/references/ast-utils.js +313 -0
  38. package/dist/references/ast-utils.js.map +1 -0
  39. package/dist/server.d.ts +9 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +35 -0
  42. package/dist/server.js.map +1 -0
  43. package/package.json +47 -0
  44. package/src/index.ts +26 -0
  45. package/src/nowline-lsp-module.ts +102 -0
  46. package/src/providers/completion.ts +202 -0
  47. package/src/providers/definition.ts +58 -0
  48. package/src/providers/document-symbols.ts +152 -0
  49. package/src/providers/hover.ts +98 -0
  50. package/src/providers/references.ts +86 -0
  51. package/src/providers/rename.ts +109 -0
  52. package/src/references/ast-utils.ts +378 -0
  53. package/src/server.ts +33 -0
@@ -0,0 +1,313 @@
1
+ import { isAnchorDeclaration, isFootnoteDeclaration, isGroupBlock, isItemDeclaration, isLabelDeclaration, isMilestoneDeclaration, isNowlineFile, isParallelBlock, isPersonDeclaration, isPersonMemberRef, isSizeDeclaration, isStatusDeclaration, isStyleDeclaration, isSwimlaneDeclaration, isSymbolDeclaration, isTeamDeclaration, } from '@nowline/core';
2
+ import { CstUtils } from 'langium';
3
+ /**
4
+ * Property keys whose value(s) name another entity in the same file. Used by
5
+ * definition / references / rename / completion to decide whether the cursor
6
+ * sits on an id reference.
7
+ *
8
+ * Wider than the validator's `checkReferenceResolution` (which is strict only
9
+ * for sequencing — `after` / `before` / `on`). Categorisation references
10
+ * (`size` / `status` / `style` / `labels`), decoration references
11
+ * (`icon` / `capacity-icon`), and ownership references (`owner` / `team`) are
12
+ * permissive in the validator (an undeclared id is not an error) but still
13
+ * navigable when a declaration exists.
14
+ *
15
+ * `footnote:` is intentionally absent — the legacy `item … footnote:foo`
16
+ * form was removed in favor of the spec-mandated reverse direction.
17
+ * Footnote association flows only from the footnote declaration's `on:`,
18
+ * and the validator now rejects `footnote:` as a property on host entities.
19
+ */
20
+ export const REFERENCE_PROP_KEYS = new Set([
21
+ 'after',
22
+ 'before',
23
+ 'depends',
24
+ 'on',
25
+ 'owner',
26
+ 'team',
27
+ 'size',
28
+ 'status',
29
+ 'style',
30
+ 'labels',
31
+ 'icon',
32
+ 'capacity-icon',
33
+ ]);
34
+ /** Status value-completion source: built-in statuses ship with the renderer. */
35
+ export const BUILTIN_STATUSES = [
36
+ 'planned',
37
+ 'in-progress',
38
+ 'done',
39
+ 'at-risk',
40
+ 'blocked',
41
+ ];
42
+ /**
43
+ * Built-in `capacity-icon:` vocabulary. Mirrors `BUILTIN_CAPACITY_ICONS` in
44
+ * the validator — kept as a literal here so the LSP doesn't have to import a
45
+ * non-public symbol from `@nowline/core`.
46
+ */
47
+ export const BUILTIN_CAPACITY_ICONS = [
48
+ 'none',
49
+ 'multiplier',
50
+ 'person',
51
+ 'people',
52
+ 'points',
53
+ 'time',
54
+ ];
55
+ /**
56
+ * Built-in `icon:` vocabulary. Superset of `BUILTIN_CAPACITY_ICONS` plus the
57
+ * entity-decoration glyphs the renderer ships.
58
+ */
59
+ export const BUILTIN_ICON_NAMES = [
60
+ ...BUILTIN_CAPACITY_ICONS,
61
+ 'shield',
62
+ 'warning',
63
+ 'lock',
64
+ ];
65
+ /** Strip the trailing `:` an `EntityProperty.key` carries from the grammar. */
66
+ export function propKey(prop) {
67
+ return prop.key.endsWith(':') ? prop.key.slice(0, -1) : prop.key;
68
+ }
69
+ /**
70
+ * Walk the `NowlineFile` and collect every entity that owns an `id`. Covers
71
+ * roadmap-section entities (mirrors the validator's `collectReferenceableIds`)
72
+ * plus the named config-section declarations (`style`, `symbol`) so style and
73
+ * symbol references navigate correctly. Anonymous config blocks (`scale`,
74
+ * `calendar`, `default <entity>`) are skipped.
75
+ */
76
+ export function collectNamedEntities(file) {
77
+ const out = [];
78
+ for (const entry of file.configEntries) {
79
+ if (isStyleDeclaration(entry) && entry.name)
80
+ out.push(entry);
81
+ else if (isSymbolDeclaration(entry) && entry.name)
82
+ out.push(entry);
83
+ }
84
+ const visitTeam = (team) => {
85
+ if (team.name)
86
+ out.push(team);
87
+ for (const c of team.content) {
88
+ if (isTeamDeclaration(c))
89
+ visitTeam(c);
90
+ // `PersonMemberRef` and `DescriptionDirective` are references / prose,
91
+ // not declarations — skip.
92
+ else if (isPersonMemberRef(c)) {
93
+ /* skip */
94
+ }
95
+ }
96
+ };
97
+ const visitTrackChild = (child) => {
98
+ if (isItemDeclaration(child) && child.name)
99
+ out.push(child);
100
+ else if (isParallelBlock(child)) {
101
+ if (child.name)
102
+ out.push(child);
103
+ for (const sub of child.content)
104
+ visitTrackChild(sub);
105
+ }
106
+ else if (isGroupBlock(child)) {
107
+ if (child.name)
108
+ out.push(child);
109
+ for (const sub of child.content)
110
+ visitTrackChild(sub);
111
+ }
112
+ };
113
+ for (const entry of file.roadmapEntries) {
114
+ if (isSwimlaneDeclaration(entry)) {
115
+ if (entry.name)
116
+ out.push(entry);
117
+ for (const c of entry.content)
118
+ visitTrackChild(c);
119
+ }
120
+ else if (isTeamDeclaration(entry))
121
+ visitTeam(entry);
122
+ else if (isPersonDeclaration(entry) ||
123
+ isAnchorDeclaration(entry) ||
124
+ isMilestoneDeclaration(entry) ||
125
+ isFootnoteDeclaration(entry) ||
126
+ isLabelDeclaration(entry) ||
127
+ isSizeDeclaration(entry) ||
128
+ isStatusDeclaration(entry)) {
129
+ if (entry.name)
130
+ out.push(entry);
131
+ }
132
+ }
133
+ return out;
134
+ }
135
+ /**
136
+ * Build a map from id → declaring entity. When a name appears multiple times
137
+ * the validator already flags it; we keep the first occurrence so navigation
138
+ * is deterministic.
139
+ */
140
+ export function buildEntityIndex(file) {
141
+ const index = new Map();
142
+ for (const ent of collectNamedEntities(file)) {
143
+ const id = ent.name;
144
+ if (id && !index.has(id))
145
+ index.set(id, ent);
146
+ }
147
+ return index;
148
+ }
149
+ /** Visit every `EntityProperty` reachable from a roadmap entry, depth-first. */
150
+ export function visitProperties(node, visit) {
151
+ const walk = (n) => {
152
+ const props = n.properties;
153
+ if (Array.isArray(props)) {
154
+ for (const p of props)
155
+ visit(p, n);
156
+ }
157
+ if (isSwimlaneDeclaration(n)) {
158
+ for (const c of n.content)
159
+ walk(c);
160
+ }
161
+ else if (isParallelBlock(n) || isGroupBlock(n)) {
162
+ for (const c of n.content)
163
+ walk(c);
164
+ }
165
+ else if (isTeamDeclaration(n)) {
166
+ for (const c of n.content) {
167
+ if (isTeamDeclaration(c) || isPersonDeclaration(c))
168
+ walk(c);
169
+ }
170
+ }
171
+ };
172
+ walk(node);
173
+ }
174
+ /**
175
+ * Iterate every `EntityProperty` in the file, including those on the roadmap
176
+ * declaration itself, plus the properties on top-level config entries (style /
177
+ * default / symbol blocks). Useful for find-references and completion when the
178
+ * cursor isn't necessarily inside a roadmap entry.
179
+ */
180
+ export function visitAllProperties(file, visit) {
181
+ if (file.roadmapDecl) {
182
+ for (const p of file.roadmapDecl.properties)
183
+ visit(p, file.roadmapDecl);
184
+ }
185
+ for (const entry of file.roadmapEntries)
186
+ visitProperties(entry, visit);
187
+ }
188
+ /**
189
+ * Pull the `NowlineFile` AST root from a Langium document.
190
+ */
191
+ export function fileFromDocument(document) {
192
+ const root = document.parseResult?.value;
193
+ return root && isNowlineFile(root) ? root : undefined;
194
+ }
195
+ /**
196
+ * Find the leaf CST node directly under the given offset. Wraps
197
+ * `CstUtils.findLeafNodeAtOffset` to make the call sites read clearly.
198
+ */
199
+ export function leafAt(document, offset) {
200
+ const root = document.parseResult?.value?.$cstNode;
201
+ if (!root)
202
+ return undefined;
203
+ return CstUtils.findLeafNodeAtOffset(root, offset);
204
+ }
205
+ /**
206
+ * When `leaf` sits inside the value(s) of an `EntityProperty`, return the
207
+ * containing property along with the matched value text. Returns `undefined`
208
+ * when the leaf is the property key, surrounding punctuation, or otherwise
209
+ * outside a property value.
210
+ *
211
+ * Used by definition / hover / references to detect "cursor on a reference".
212
+ */
213
+ export function propertyValueAt(leaf) {
214
+ if (!leaf)
215
+ return undefined;
216
+ let node = leaf;
217
+ while (node && node.astNode.$type !== 'EntityProperty')
218
+ node = node.container;
219
+ if (!node)
220
+ return undefined;
221
+ const prop = node.astNode;
222
+ const text = leaf.text;
223
+ if (prop.value === text) {
224
+ return { prop, value: prop.value, valueNode: leaf };
225
+ }
226
+ if (prop.values?.includes(text)) {
227
+ return { prop, value: text, valueNode: leaf };
228
+ }
229
+ return undefined;
230
+ }
231
+ /**
232
+ * When `leaf` is the `name=ID` token of a named entity declaration, return the
233
+ * declaring entity. Returns `undefined` for any other position.
234
+ */
235
+ export function declarationAt(leaf) {
236
+ if (!leaf)
237
+ return undefined;
238
+ const owner = leaf.astNode;
239
+ if (!owner || typeof owner.name !== 'string')
240
+ return undefined;
241
+ if (owner.name !== leaf.text)
242
+ return undefined;
243
+ if (isSwimlaneDeclaration(owner) ||
244
+ isItemDeclaration(owner) ||
245
+ isParallelBlock(owner) ||
246
+ isGroupBlock(owner) ||
247
+ isAnchorDeclaration(owner) ||
248
+ isMilestoneDeclaration(owner) ||
249
+ isFootnoteDeclaration(owner) ||
250
+ isPersonDeclaration(owner) ||
251
+ isTeamDeclaration(owner) ||
252
+ isLabelDeclaration(owner) ||
253
+ isSizeDeclaration(owner) ||
254
+ isStatusDeclaration(owner) ||
255
+ isStyleDeclaration(owner) ||
256
+ isSymbolDeclaration(owner)) {
257
+ return owner;
258
+ }
259
+ return undefined;
260
+ }
261
+ /**
262
+ * Look up the `name=ID` token range of the entity that declares `id`. Walks
263
+ * `collectNamedEntities` so config-section declarations (style / symbol)
264
+ * participate. Returns `undefined` when no declaration matches.
265
+ *
266
+ * Replaces the per-provider `findDeclarationRange` helpers in
267
+ * references.ts and rename.ts so all providers agree on what "declared"
268
+ * means.
269
+ */
270
+ export function findDeclarationRange(file, id) {
271
+ for (const ent of collectNamedEntities(file)) {
272
+ if (ent.name === id) {
273
+ const range = nameRangeOf(ent);
274
+ if (range)
275
+ return range;
276
+ }
277
+ }
278
+ return undefined;
279
+ }
280
+ /**
281
+ * Render a one-line "type id" label used by hover and document-symbols.
282
+ */
283
+ export function entityKind(entity) {
284
+ return entity.$type
285
+ .replace(/Declaration$/, '')
286
+ .replace(/Block$/, '')
287
+ .toLowerCase();
288
+ }
289
+ /**
290
+ * Find the CST node that holds the given entity's `name=ID` token. Walks the
291
+ * entity's CST subtree and returns the first leaf whose text equals the
292
+ * entity's resolved name. Returns `undefined` when the entity has no `name`
293
+ * (anonymous declaration), no CST node, or no matching leaf.
294
+ */
295
+ export function nameRangeOf(entity) {
296
+ const name = entity.name;
297
+ if (!name || !entity.$cstNode)
298
+ return undefined;
299
+ for (const leaf of CstUtils.flattenCst(entity.$cstNode)) {
300
+ if (leaf.text === name)
301
+ return leaf.range;
302
+ }
303
+ return undefined;
304
+ }
305
+ /**
306
+ * Iterate every leaf CST node inside the given root. Convenience wrapper over
307
+ * `CstUtils.flattenCst` so call sites don't need to import the namespace.
308
+ */
309
+ export function* flattenLeaves(root) {
310
+ for (const leaf of CstUtils.flattenCst(root))
311
+ yield leaf;
312
+ }
313
+ //# sourceMappingURL=ast-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-utils.js","sourceRoot":"","sources":["../../src/references/ast-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Boot the Nowline language server over stdio. Used both by the bundled VS
4
+ * Code / Cursor extension (spawned as a Node module via `LanguageClient`) and
5
+ * by the standalone `nowline-lsp` binary editors like Neovim and JetBrains
6
+ * point at via the LSP config.
7
+ */
8
+ export declare function startNowlineServer(): void;
9
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAMA;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAOzC"}
package/dist/server.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { startLanguageServer } from 'langium/lsp';
3
+ import { NodeFileSystem } from 'langium/node';
4
+ import { createConnection, ProposedFeatures } from 'vscode-languageserver/node.js';
5
+ import { createNowlineLspServices } from './nowline-lsp-module.js';
6
+ /**
7
+ * Boot the Nowline language server over stdio. Used both by the bundled VS
8
+ * Code / Cursor extension (spawned as a Node module via `LanguageClient`) and
9
+ * by the standalone `nowline-lsp` binary editors like Neovim and JetBrains
10
+ * point at via the LSP config.
11
+ */
12
+ export function startNowlineServer() {
13
+ const connection = createConnection(ProposedFeatures.all);
14
+ const { shared } = createNowlineLspServices({
15
+ connection,
16
+ fileSystemProvider: NodeFileSystem.fileSystemProvider,
17
+ });
18
+ startLanguageServer(shared);
19
+ }
20
+ // Only auto-start when invoked directly (the binary entrypoint). Importers
21
+ // (e.g. tests or in-process embeddings) call `startNowlineServer()` themselves.
22
+ const isDirect = (() => {
23
+ if (typeof process === 'undefined' || !process.argv?.[1])
24
+ return false;
25
+ try {
26
+ const url = new URL(import.meta.url);
27
+ return url.protocol === 'file:' && url.pathname === process.argv[1];
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ })();
33
+ if (isDirect)
34
+ startNowlineServer();
35
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACnF,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAC9B,MAAM,UAAU,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,EAAE,MAAM,EAAE,GAAG,wBAAwB,CAAC;QACxC,UAAU;QACV,kBAAkB,EAAE,cAAc,CAAC,kBAAkB;KACxD,CAAC,CAAC;IACH,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,2EAA2E;AAC3E,gFAAgF;AAChF,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE;IACnB,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACvE,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC,CAAC,EAAE,CAAC;AACL,IAAI,QAAQ;IAAE,kBAAkB,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@nowline/lsp",
3
+ "version": "0.4.1",
4
+ "description": "Nowline language-server — Langium-based LSP that powers VS Code, Cursor, and any other LSP-capable editor",
5
+ "license": "Apache-2.0",
6
+ "engines": {
7
+ "node": ">=22",
8
+ "pnpm": ">=11"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "bin": {
14
+ "nowline-lsp": "./dist/server.js"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./server": {
22
+ "types": "./dist/server.d.ts",
23
+ "import": "./dist/server.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist/",
28
+ "src/"
29
+ ],
30
+ "dependencies": {
31
+ "langium": "~4.2.4",
32
+ "vscode-languageserver": "~9.0.1",
33
+ "vscode-languageserver-textdocument": "~1.0.12",
34
+ "@nowline/core": "0.4.1"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^25.9.1",
38
+ "typescript": "^6.0.3",
39
+ "vitest": "^4.1.7"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -b tsconfig.json",
43
+ "watch": "tsc -b tsconfig.json --watch",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest"
46
+ }
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ export {
2
+ type CreateNowlineLspServicesContext,
3
+ createNowlineLspServices,
4
+ type NowlineLspServices,
5
+ } from './nowline-lsp-module.js';
6
+ export { NowlineCompletionProvider } from './providers/completion.js';
7
+ export { NowlineDefinitionProvider } from './providers/definition.js';
8
+ export { NowlineDocumentSymbolProvider } from './providers/document-symbols.js';
9
+ export { NowlineHoverProvider } from './providers/hover.js';
10
+ export { NowlineReferencesProvider } from './providers/references.js';
11
+ export { NowlineRenameProvider } from './providers/rename.js';
12
+ export {
13
+ BUILTIN_STATUSES,
14
+ buildEntityIndex,
15
+ collectNamedEntities,
16
+ declarationAt,
17
+ entityKind,
18
+ fileFromDocument,
19
+ leafAt,
20
+ type NamedEntity,
21
+ propertyValueAt,
22
+ propKey,
23
+ REFERENCE_PROP_KEYS,
24
+ visitAllProperties,
25
+ visitProperties,
26
+ } from './references/ast-utils.js';
@@ -0,0 +1,102 @@
1
+ import {
2
+ type NowlineAddedServices,
3
+ NowlineGeneratedModule,
4
+ NowlineGeneratedSharedModule,
5
+ NowlineModule,
6
+ registerValidationChecks,
7
+ } from '@nowline/core';
8
+ import {
9
+ type FileSystemProvider,
10
+ inject,
11
+ type LangiumSharedCoreServices,
12
+ type Module,
13
+ } from 'langium';
14
+ import {
15
+ createDefaultModule,
16
+ createDefaultSharedModule,
17
+ type DefaultSharedModuleContext,
18
+ type LangiumServices,
19
+ type LangiumSharedServices,
20
+ type PartialLangiumServices,
21
+ } from 'langium/lsp';
22
+ import { NowlineCompletionProvider } from './providers/completion.js';
23
+ import { NowlineDefinitionProvider } from './providers/definition.js';
24
+ import { NowlineDocumentSymbolProvider } from './providers/document-symbols.js';
25
+ import { NowlineHoverProvider } from './providers/hover.js';
26
+ import { NowlineReferencesProvider } from './providers/references.js';
27
+ import { NowlineRenameProvider } from './providers/rename.js';
28
+
29
+ /**
30
+ * Combined Langium LSP services + Nowline-specific additions.
31
+ *
32
+ * `LangiumServices` already covers parser, validation, and the LSP service
33
+ * slots (`lsp.DefinitionProvider`, `lsp.HoverProvider`, …). We layer the
34
+ * Nowline validator on top via `NowlineAddedServices`.
35
+ */
36
+ export type NowlineLspServices = LangiumServices & NowlineAddedServices;
37
+
38
+ /**
39
+ * Language-specific module. Carries the Nowline LSP provider overrides on top
40
+ * of whatever the cross-package `NowlineModule` (parser config + validator
41
+ * factory, lifted from `@nowline/core`) already contributes.
42
+ *
43
+ * Keeping the parser/validation slots on the core-side `NowlineModule` means
44
+ * the LSP and the headless `@nowline/cli` parse path stay byte-identical.
45
+ */
46
+ const NowlineLspProviderModule: Module<NowlineLspServices, PartialLangiumServices> = {
47
+ lsp: {
48
+ CompletionProvider: (services) => new NowlineCompletionProvider(services),
49
+ DefinitionProvider: (services) => new NowlineDefinitionProvider(services),
50
+ DocumentSymbolProvider: (services) => new NowlineDocumentSymbolProvider(services),
51
+ HoverProvider: (services) => new NowlineHoverProvider(services),
52
+ ReferencesProvider: (services) => new NowlineReferencesProvider(services),
53
+ RenameProvider: (services) => new NowlineRenameProvider(services),
54
+ },
55
+ };
56
+
57
+ export interface CreateNowlineLspServicesContext {
58
+ /** Optional shared services container (e.g. when reusing across languages). */
59
+ shared?: LangiumSharedServices;
60
+ /** Either a `NodeFileSystem` (real disk) or `EmptyFileSystem` (tests). */
61
+ fileSystemProvider: (services: LangiumSharedCoreServices) => FileSystemProvider;
62
+ /** Optional LSP connection (omit for headless / in-memory usage). */
63
+ connection?: DefaultSharedModuleContext['connection'];
64
+ }
65
+
66
+ /**
67
+ * Build the full Langium LSP service container plus the Nowline language
68
+ * services. Returns the shared container (used to start the language server)
69
+ * and the language-specific services (used by tests + provider call sites).
70
+ */
71
+ export function createNowlineLspServices(context: CreateNowlineLspServicesContext): {
72
+ shared: LangiumSharedServices;
73
+ Nowline: NowlineLspServices;
74
+ } {
75
+ const shared =
76
+ context.shared ??
77
+ inject(
78
+ createDefaultSharedModule({
79
+ connection: context.connection,
80
+ fileSystemProvider: context.fileSystemProvider,
81
+ }),
82
+ NowlineGeneratedSharedModule,
83
+ );
84
+
85
+ const Nowline = inject(
86
+ createDefaultModule({ shared }),
87
+ NowlineGeneratedModule,
88
+ // `NowlineModule` is typed against `NowlineServices` (core only); cast
89
+ // through `unknown` because the LSP injector is strictly broader and
90
+ // Langium's `inject()` accepts that at runtime. See
91
+ // `packages/core/src/language/nowline-module.ts` for the source.
92
+ NowlineModule as unknown as Module<
93
+ NowlineLspServices,
94
+ PartialLangiumServices & NowlineAddedServices
95
+ >,
96
+ NowlineLspProviderModule,
97
+ );
98
+
99
+ shared.ServiceRegistry.register(Nowline);
100
+ registerValidationChecks(Nowline);
101
+ return { shared, Nowline };
102
+ }