luaut-language-server 1.0.0 → 1.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.
- package/README.md +100 -100
- package/dist/{chunk-RHII344O.js → chunk-FMWNSXUC.js} +31 -13
- package/dist/chunk-FMWNSXUC.js.map +1 -0
- package/dist/cli.cjs +36 -19
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +36 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +53 -49
- package/dist/chunk-RHII344O.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
# luaut-language-server
|
|
2
|
-
|
|
3
|
-
Language server (LSP) for **luaut** — the TypeScript-flavoured language that
|
|
4
|
-
compiles to Luau. It is a thin layer over [`luaut-parser`][parser]: the parser
|
|
5
|
-
does the parsing, scope analysis and flow-sensitive type analysis, and this
|
|
6
|
-
package answers editor questions from the tables it produces.
|
|
7
|
-
|
|
8
|
-
[parser]: https://www.npmjs.com/package/luaut-parser
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install luaut-language-server
|
|
12
|
-
luaut-language-server --stdio
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## What it does
|
|
16
|
-
|
|
17
|
-
| request | notes |
|
|
18
|
-
|---|---|
|
|
19
|
-
| `publishDiagnostics` | syntax, scope (redeclare, assign-to-`const`) and type errors, on open and on every keystroke |
|
|
20
|
-
| `hover` | the type as luaut writes it — the **narrowed** type at a reference, so a guarded `v` reads `string`, not `string \| nil` |
|
|
21
|
-
| `definition` | the binding's declaration |
|
|
22
|
-
| `references`, `documentHighlight` | every use of the binding |
|
|
23
|
-
| `rename`, `prepareRename` | refuses names that are not identifiers, and builtins from the definitions files |
|
|
24
|
-
| `completion` | members after `.` / `:`, names in scope, type names in a type position |
|
|
25
|
-
| `signatureHelp` | every overload, with the active parameter — `:` calls count `self` for you |
|
|
26
|
-
| `documentSymbol` | functions, type aliases, top-level bindings |
|
|
27
|
-
|
|
28
|
-
Single file, for now: `import` resolves to `any`, so cross-file navigation is
|
|
29
|
-
not there yet. See [Not yet](#not-yet).
|
|
30
|
-
|
|
31
|
-
## How it is put together
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
src/
|
|
35
|
-
server.ts LSP wiring, and nothing else
|
|
36
|
-
analysis.ts parse -> scopes -> types, cached per document version
|
|
37
|
-
ast-utils.ts 1-based spans <-> 0-based LSP positions, position -> node
|
|
38
|
-
features/ one file per feature; plain functions, no LSP plumbing
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
A feature is `(analysis, position) -> answer`. Nothing in `features/` opens a
|
|
42
|
-
connection or knows about documents, which is why `scripts/test.ts` can drive
|
|
43
|
-
all of them in-process without spawning a server, and why an editor extension
|
|
44
|
-
can call them directly:
|
|
45
|
-
|
|
46
|
-
```ts
|
|
47
|
-
import { Analyzer, hover, diagnostics } from "luaut-language-server"
|
|
48
|
-
|
|
49
|
-
const analyzer = new Analyzer() // or { libs: [...] } for your own definitions
|
|
50
|
-
const analysis = analyzer.get(document) // a vscode-languageserver TextDocument
|
|
51
|
-
hover(analysis, { line: 3, character: 12 })
|
|
52
|
-
diagnostics(analysis)
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Speculative parsing
|
|
56
|
-
|
|
57
|
-
`x.` and `add(1, ` are syntax errors — the text you are in the middle of
|
|
58
|
-
typing usually is. Completion and signature help therefore analyze a
|
|
59
|
-
*repaired copy* of the document: a placeholder identifier at the cursor for
|
|
60
|
-
completion, and the shortest of `nil`, `nil)`, `)` that parses for signature
|
|
61
|
-
help. The user's document is never touched and the repaired copy is never
|
|
62
|
-
cached.
|
|
63
|
-
|
|
64
|
-
### Globals
|
|
65
|
-
|
|
66
|
-
The names a file may use undeclared are not hard-coded: they are read out of
|
|
67
|
-
the `declare` statements in the definitions passed to `Analyzer`. Adding a
|
|
68
|
-
global to a `.d.luaut` is all it takes for the editor to stop calling it
|
|
69
|
-
undefined.
|
|
70
|
-
|
|
71
|
-
## Not yet
|
|
72
|
-
|
|
73
|
-
- **One file at a time.** No workspace indexing, so no cross-file
|
|
74
|
-
go-to-definition, `workspace/symbol`, or diagnostics for files you have not
|
|
75
|
-
opened.
|
|
76
|
-
- **No formatting** — there is no luaut printer yet (the compiler owns
|
|
77
|
-
emitting Luau, and it emits *Luau*, not luaut).
|
|
78
|
-
- No code actions, inlay hints, semantic tokens, or folding ranges.
|
|
79
|
-
- Everything `luaut-parser` does not check is invisible here too: unknown
|
|
80
|
-
properties, writes to `readonly`, generic constraints at call sites,
|
|
81
|
-
metatables.
|
|
82
|
-
|
|
83
|
-
## Editors
|
|
84
|
-
|
|
85
|
-
VS Code: [`luaut-vscode`](../luaut-vscode) — a separate project next door. It
|
|
86
|
-
bundles this server into the extension, so its `.vsix` is self-contained.
|
|
87
|
-
|
|
88
|
-
Anything else that speaks LSP: launch `luaut-language-server --stdio` (or
|
|
89
|
-
`--node-ipc`) and attach it to the `luaut` language / `.luaut` files.
|
|
90
|
-
|
|
91
|
-
## Development
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
npm run typecheck
|
|
95
|
-
npm test # features in-process, then the built binary over stdio
|
|
96
|
-
npm run build
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
`scripts/test.ts` marks the cursor with `‸` in each fixture (not `|` — that is
|
|
100
|
-
the union operator). `scripts/e2e.ts` speaks real LSP to `dist/cli.js`.
|
|
1
|
+
# luaut-language-server
|
|
2
|
+
|
|
3
|
+
Language server (LSP) for **luaut** — the TypeScript-flavoured language that
|
|
4
|
+
compiles to Luau. It is a thin layer over [`luaut-parser`][parser]: the parser
|
|
5
|
+
does the parsing, scope analysis and flow-sensitive type analysis, and this
|
|
6
|
+
package answers editor questions from the tables it produces.
|
|
7
|
+
|
|
8
|
+
[parser]: https://www.npmjs.com/package/luaut-parser
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install luaut-language-server
|
|
12
|
+
luaut-language-server --stdio
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## What it does
|
|
16
|
+
|
|
17
|
+
| request | notes |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `publishDiagnostics` | syntax, scope (redeclare, assign-to-`const`) and type errors, on open and on every keystroke |
|
|
20
|
+
| `hover` | the type as luaut writes it — the **narrowed** type at a reference, so a guarded `v` reads `string`, not `string \| nil` |
|
|
21
|
+
| `definition` | the binding's declaration |
|
|
22
|
+
| `references`, `documentHighlight` | every use of the binding |
|
|
23
|
+
| `rename`, `prepareRename` | refuses names that are not identifiers, and builtins from the definitions files |
|
|
24
|
+
| `completion` | members after `.` / `:`, names in scope, type names in a type position |
|
|
25
|
+
| `signatureHelp` | every overload, with the active parameter — `:` calls count `self` for you |
|
|
26
|
+
| `documentSymbol` | functions, type aliases, top-level bindings |
|
|
27
|
+
|
|
28
|
+
Single file, for now: `import` resolves to `any`, so cross-file navigation is
|
|
29
|
+
not there yet. See [Not yet](#not-yet).
|
|
30
|
+
|
|
31
|
+
## How it is put together
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/
|
|
35
|
+
server.ts LSP wiring, and nothing else
|
|
36
|
+
analysis.ts parse -> scopes -> types, cached per document version
|
|
37
|
+
ast-utils.ts 1-based spans <-> 0-based LSP positions, position -> node
|
|
38
|
+
features/ one file per feature; plain functions, no LSP plumbing
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
A feature is `(analysis, position) -> answer`. Nothing in `features/` opens a
|
|
42
|
+
connection or knows about documents, which is why `scripts/test.ts` can drive
|
|
43
|
+
all of them in-process without spawning a server, and why an editor extension
|
|
44
|
+
can call them directly:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Analyzer, hover, diagnostics } from "luaut-language-server"
|
|
48
|
+
|
|
49
|
+
const analyzer = new Analyzer() // or { libs: [...] } for your own definitions
|
|
50
|
+
const analysis = analyzer.get(document) // a vscode-languageserver TextDocument
|
|
51
|
+
hover(analysis, { line: 3, character: 12 })
|
|
52
|
+
diagnostics(analysis)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Speculative parsing
|
|
56
|
+
|
|
57
|
+
`x.` and `add(1, ` are syntax errors — the text you are in the middle of
|
|
58
|
+
typing usually is. Completion and signature help therefore analyze a
|
|
59
|
+
*repaired copy* of the document: a placeholder identifier at the cursor for
|
|
60
|
+
completion, and the shortest of `nil`, `nil)`, `)` that parses for signature
|
|
61
|
+
help. The user's document is never touched and the repaired copy is never
|
|
62
|
+
cached.
|
|
63
|
+
|
|
64
|
+
### Globals
|
|
65
|
+
|
|
66
|
+
The names a file may use undeclared are not hard-coded: they are read out of
|
|
67
|
+
the `declare` statements in the definitions passed to `Analyzer`. Adding a
|
|
68
|
+
global to a `.d.luaut` is all it takes for the editor to stop calling it
|
|
69
|
+
undefined.
|
|
70
|
+
|
|
71
|
+
## Not yet
|
|
72
|
+
|
|
73
|
+
- **One file at a time.** No workspace indexing, so no cross-file
|
|
74
|
+
go-to-definition, `workspace/symbol`, or diagnostics for files you have not
|
|
75
|
+
opened.
|
|
76
|
+
- **No formatting** — there is no luaut printer yet (the compiler owns
|
|
77
|
+
emitting Luau, and it emits *Luau*, not luaut).
|
|
78
|
+
- No code actions, inlay hints, semantic tokens, or folding ranges.
|
|
79
|
+
- Everything `luaut-parser` does not check is invisible here too: unknown
|
|
80
|
+
properties, writes to `readonly`, generic constraints at call sites,
|
|
81
|
+
metatables.
|
|
82
|
+
|
|
83
|
+
## Editors
|
|
84
|
+
|
|
85
|
+
VS Code: [`luaut-vscode`](../luaut-vscode) — a separate project next door. It
|
|
86
|
+
bundles this server into the extension, so its `.vsix` is self-contained.
|
|
87
|
+
|
|
88
|
+
Anything else that speaks LSP: launch `luaut-language-server --stdio` (or
|
|
89
|
+
`--node-ipc`) and attach it to the `luaut` language / `.luaut` files.
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm run typecheck
|
|
95
|
+
npm test # features in-process, then the built binary over stdio
|
|
96
|
+
npm run build
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`scripts/test.ts` marks the cursor with `‸` in each fixture (not `|` — that is
|
|
100
|
+
the union operator). `scripts/e2e.ts` speaks real LSP to `dist/cli.js`.
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
parseWithRecovery,
|
|
4
4
|
analyzeScopes,
|
|
5
5
|
analyzeTypes,
|
|
6
|
-
defaultLibs
|
|
6
|
+
defaultLibs,
|
|
7
|
+
getBinding
|
|
7
8
|
} from "luaut-parser";
|
|
8
9
|
function globalsOf(libs) {
|
|
9
10
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -15,6 +16,23 @@ function collect(statements, into) {
|
|
|
15
16
|
if (statement.type === "DeclareStatement") into.add(statement.name);
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
function bindingOfNode(analysis, node) {
|
|
20
|
+
const used = getBinding(analysis.scopes, node);
|
|
21
|
+
if (used) return used;
|
|
22
|
+
return declarationIndex(analysis).get(node);
|
|
23
|
+
}
|
|
24
|
+
var declarationIndexes = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
function declarationIndex(analysis) {
|
|
26
|
+
let index = declarationIndexes.get(analysis);
|
|
27
|
+
if (!index) {
|
|
28
|
+
index = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const binding of analysis.scopes.bindings.values()) {
|
|
30
|
+
if (binding.declarationNode) index.set(binding.declarationNode, binding);
|
|
31
|
+
}
|
|
32
|
+
declarationIndexes.set(analysis, index);
|
|
33
|
+
}
|
|
34
|
+
return index;
|
|
35
|
+
}
|
|
18
36
|
var Analyzer = class {
|
|
19
37
|
libs;
|
|
20
38
|
builtinGlobals;
|
|
@@ -156,7 +174,7 @@ function diagnostics(analysis) {
|
|
|
156
174
|
}
|
|
157
175
|
|
|
158
176
|
// src/features/hover.ts
|
|
159
|
-
import { formatType
|
|
177
|
+
import { formatType } from "luaut-parser";
|
|
160
178
|
function hover(analysis, position) {
|
|
161
179
|
const path = pathAt(analysis.program, position, true);
|
|
162
180
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
@@ -167,7 +185,7 @@ function hover(analysis, position) {
|
|
|
167
185
|
return null;
|
|
168
186
|
}
|
|
169
187
|
function describe(analysis, node, parent) {
|
|
170
|
-
const { types
|
|
188
|
+
const { types } = analysis;
|
|
171
189
|
if (node.type === "TypeAliasStatement" || node.type === "ExportTypeAliasStatement") {
|
|
172
190
|
const name = node.name;
|
|
173
191
|
const alias = types.aliases.get(name);
|
|
@@ -177,7 +195,7 @@ function describe(analysis, node, parent) {
|
|
|
177
195
|
const identifier = node;
|
|
178
196
|
const narrowed = types.narrowedTypeOf.get(identifier);
|
|
179
197
|
if (narrowed) return `${identifier.name}: ${formatType(narrowed)}`;
|
|
180
|
-
const binding =
|
|
198
|
+
const binding = bindingOfNode(analysis, identifier);
|
|
181
199
|
if (binding) {
|
|
182
200
|
const type2 = types.bindingType.get(binding.id);
|
|
183
201
|
if (type2) return `${keyword(binding)} ${binding.name}: ${formatType(type2)}`;
|
|
@@ -187,8 +205,8 @@ function describe(analysis, node, parent) {
|
|
|
187
205
|
if (type2) return `${identifier.name}: ${formatType(type2)}`;
|
|
188
206
|
}
|
|
189
207
|
}
|
|
190
|
-
if (node.type === "IdentifierPattern") {
|
|
191
|
-
const binding =
|
|
208
|
+
if (node.type === "IdentifierPattern" || node.type === "FunctionParameter" || node.type === "TypedIdentifier") {
|
|
209
|
+
const binding = bindingOfNode(analysis, node);
|
|
192
210
|
if (binding) {
|
|
193
211
|
const type2 = types.bindingType.get(binding.id);
|
|
194
212
|
if (type2) return `${keyword(binding)} ${binding.name}: ${formatType(type2)}`;
|
|
@@ -211,13 +229,13 @@ function code(text) {
|
|
|
211
229
|
import {
|
|
212
230
|
DocumentHighlightKind
|
|
213
231
|
} from "vscode-languageserver";
|
|
214
|
-
|
|
232
|
+
var NAMING = /* @__PURE__ */ new Set(["Identifier", "IdentifierPattern", "FunctionParameter", "TypedIdentifier"]);
|
|
215
233
|
function bindingAt(analysis, position) {
|
|
216
234
|
const path = pathAt(analysis.program, position, true);
|
|
217
235
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
218
236
|
const node = path[i];
|
|
219
|
-
if (node.type
|
|
220
|
-
const binding =
|
|
237
|
+
if (!node.type || !NAMING.has(node.type)) continue;
|
|
238
|
+
const binding = bindingOfNode(analysis, node);
|
|
221
239
|
if (binding) return binding;
|
|
222
240
|
}
|
|
223
241
|
return void 0;
|
|
@@ -252,7 +270,7 @@ function prepareRename(analysis, position) {
|
|
|
252
270
|
if (!binding) return null;
|
|
253
271
|
if (binding.isBuiltin || !binding.declarationNode) return null;
|
|
254
272
|
const path = pathAt(analysis.program, position, true);
|
|
255
|
-
const identifier = [...path].reverse().find((n) => n.type
|
|
273
|
+
const identifier = [...path].reverse().find((n) => !!n.type && NAMING.has(n.type));
|
|
256
274
|
if (!identifier) return null;
|
|
257
275
|
return { range: toRange(identifier), placeholder: binding.name };
|
|
258
276
|
}
|
|
@@ -544,7 +562,7 @@ function activeArgument(call, position) {
|
|
|
544
562
|
|
|
545
563
|
// src/features/symbols.ts
|
|
546
564
|
import { SymbolKind } from "vscode-languageserver";
|
|
547
|
-
import { formatType as formatType4
|
|
565
|
+
import { formatType as formatType4 } from "luaut-parser";
|
|
548
566
|
function documentSymbols(analysis) {
|
|
549
567
|
const out = [];
|
|
550
568
|
walk(analysis.program, (node) => {
|
|
@@ -590,7 +608,7 @@ function functionName(node) {
|
|
|
590
608
|
function detailOf(analysis, node) {
|
|
591
609
|
const name = node.name;
|
|
592
610
|
if (name && typeof name === "object") {
|
|
593
|
-
const binding =
|
|
611
|
+
const binding = bindingOfNode(analysis, name);
|
|
594
612
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
595
613
|
if (type) return formatType4(type);
|
|
596
614
|
}
|
|
@@ -730,4 +748,4 @@ export {
|
|
|
730
748
|
createServer,
|
|
731
749
|
startServer
|
|
732
750
|
};
|
|
733
|
-
//# sourceMappingURL=chunk-
|
|
751
|
+
//# sourceMappingURL=chunk-FMWNSXUC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/analysis.ts","../src/ast-utils.ts","../src/features/diagnostics.ts","../src/features/hover.ts","../src/features/navigation.ts","../src/features/members.ts","../src/features/completion.ts","../src/features/signatureHelp.ts","../src/features/symbols.ts","../src/server.ts"],"sourcesContent":["/**\n * Analysis cache.\n *\n * The three parser passes are cheap (single-digit milliseconds for a normal\n * file) but not free, and every LSP request wants the same result for the same\n * document version — so each document is analyzed once per version and the\n * result is reused by hover, definition, completion and the rest.\n */\nimport {\n parseWithRecovery, analyzeScopes, analyzeTypes, defaultLibs, getBinding,\n type Program, type ScopeAnalysis, type TypeAnalysis, type ParseError,\n type DeclareStatement, type Statement, type Binding, type Identifier,\n} from \"luaut-parser\"\nimport type { TextDocument } from \"vscode-languageserver-textdocument\"\n\nexport interface Analysis {\n readonly uri: string\n readonly version: number\n readonly source: string\n readonly program: Program\n readonly parseErrors: readonly ParseError[]\n readonly scopes: ScopeAnalysis\n readonly types: TypeAnalysis\n}\n\nexport interface AnalyzerOptions {\n /** Definitions to analyze against. Defaults to core Luau + Roblox. */\n libs?: readonly Program[]\n}\n\n/** Names every file may use undeclared: whatever the definitions declare.\n * Derived rather than hard-coded, so adding a `declare` to a `.d.luaut` is\n * all it takes for the name to stop looking undefined. */\nfunction globalsOf(libs: readonly Program[]): string[] {\n const names = new Set<string>()\n for (const lib of libs) collect(lib.body.statements, names)\n return [...names]\n}\n\nfunction collect(statements: readonly Statement[], into: Set<string>): void {\n for (const statement of statements) {\n if (statement.type === \"DeclareStatement\") into.add((statement as DeclareStatement).name)\n }\n}\n\n/** The binding a node names, whether it *uses* the binding or *declares* it.\n *\n * Scope analysis indexes the two differently: every use is in `bindingOf`,\n * but a declaration only appears as its binding's `declarationNode`. Asking\n * `bindingOf` alone is why hovering `const x` — as opposed to a later `x` —\n * used to show nothing. */\nexport function bindingOfNode(analysis: Analysis, node: object): Binding | undefined {\n const used = getBinding(analysis.scopes, node as Identifier)\n if (used) return used\n return declarationIndex(analysis).get(node)\n}\n\nconst declarationIndexes = new WeakMap<Analysis, Map<object, Binding>>()\n\nfunction declarationIndex(analysis: Analysis): Map<object, Binding> {\n let index = declarationIndexes.get(analysis)\n if (!index) {\n index = new Map()\n for (const binding of analysis.scopes.bindings.values()) {\n if (binding.declarationNode) index.set(binding.declarationNode, binding)\n }\n declarationIndexes.set(analysis, index)\n }\n return index\n}\n\nexport class Analyzer {\n private readonly libs: readonly Program[]\n private readonly builtinGlobals: string[]\n private readonly cache = new Map<string, Analysis>()\n\n constructor(options: AnalyzerOptions = {}) {\n this.libs = options.libs ?? defaultLibs\n this.builtinGlobals = globalsOf(this.libs)\n }\n\n /** Analyze `document`, reusing the previous result if its version is\n * unchanged. */\n get(document: TextDocument): Analysis {\n const cached = this.cache.get(document.uri)\n const source = document.getText()\n // The version alone would do for a real editor, where it only ever\n // increases — comparing the text too costs nothing next to an\n // analysis and makes the cache safe for any caller.\n if (cached && cached.version === document.version && cached.source === source) return cached\n const analysis = this.analyze(document.uri, document.version, source)\n this.cache.set(document.uri, analysis)\n return analysis\n }\n\n /** Analyze source text that is not a tracked document — used by\n * completion, which analyzes a speculatively edited copy of the file. */\n analyze(uri: string, version: number, source: string): Analysis {\n const { program, errors } = parseWithRecovery(source)\n const scopes = analyzeScopes(program, { builtinGlobals: this.builtinGlobals })\n const types = analyzeTypes(program, scopes, { libs: this.libs })\n return { uri, version, source, program, parseErrors: errors, scopes, types }\n }\n\n forget(uri: string): void {\n this.cache.delete(uri)\n }\n}\n","/**\n * Position mapping and AST lookup.\n *\n * luaut spans are 1-based with an exclusive end column; LSP positions are\n * 0-based. Every conversion between the two lives here so the features never\n * do the arithmetic themselves.\n */\nimport type { Position, Range } from \"vscode-languageserver\"\n\n/** The shape every luaut AST node shares. */\nexport interface Spanned {\n type?: string\n line: { start: number; end: number }\n column: { start: number; end: number }\n}\n\nexport function isSpanned(v: unknown): v is Spanned {\n if (!v || typeof v !== \"object\") return false\n const n = v as Record<string, unknown>\n return typeof n.line === \"object\" && n.line !== null && typeof n.column === \"object\" && n.column !== null\n}\n\nexport function toRange(node: Spanned): Range {\n return {\n start: { line: node.line.start - 1, character: node.column.start - 1 },\n end: { line: node.line.end - 1, character: node.column.end - 1 },\n }\n}\n\n/** A one-character range, for a diagnostic on a node with a collapsed span. */\nexport function toPosition(line: number, column: number): Position {\n return { line: line - 1, character: column - 1 }\n}\n\n/** Is `pos` inside `node`'s span? The end is exclusive, except that `inclusive`\n * admits a cursor sitting immediately after the node — which is where it is\n * while you are still typing the identifier under it. */\nexport function containsPosition(node: Spanned, pos: Position, inclusive = false): boolean {\n const startLine = node.line.start - 1\n const endLine = node.line.end - 1\n if (pos.line < startLine || pos.line > endLine) return false\n if (pos.line === startLine && pos.character < node.column.start - 1) return false\n if (pos.line === endLine) {\n const end = node.column.end - 1\n if (inclusive ? pos.character > end : pos.character >= end) return false\n }\n return true\n}\n\n/** Every child node of `node`, in source order-ish (declaration order of the\n * fields). Generic on purpose: it walks the object graph rather than knowing\n * the node types, so a new node kind in the parser needs no change here. */\nexport function children(node: Spanned): Spanned[] {\n const out: Spanned[] = []\n for (const key of Object.keys(node)) {\n if (key === \"line\" || key === \"column\") continue\n const value = (node as unknown as Record<string, unknown>)[key]\n if (Array.isArray(value)) {\n for (const item of value) if (isSpanned(item)) out.push(item)\n } else if (isSpanned(value)) {\n out.push(value)\n }\n }\n return out\n}\n\n/** The chain of nodes containing `pos`, outermost first — the last entry is\n * the innermost node at the cursor and the ones before it are its ancestors.\n *\n * It descends through every child rather than only children that contain\n * `pos`, because a parent's span does not always cover its child's: a\n * binding's span is the name alone, while its type annotation sits after it.\n * So an ancestor in this path is a real ancestor, but not necessarily one\n * whose own span contains the cursor. */\nexport function pathAt(root: Spanned, pos: Position, inclusive = false): Spanned[] {\n let best: Spanned[] | undefined\n\n const descend = (node: Spanned, ancestors: Spanned[]): void => {\n const here = [...ancestors, node]\n if (containsPosition(node, pos, inclusive)) {\n // Prefer the narrowest hit, and among equals the deepest — that is\n // the node the cursor is really \"on\".\n const incumbent = best?.[best.length - 1]\n if (!incumbent\n || spanLength(node) < spanLength(incumbent)\n || (spanLength(node) === spanLength(incumbent) && here.length > best!.length)) {\n best = here\n }\n }\n for (const child of children(node)) descend(child, here)\n }\n\n descend(root, [])\n return best ?? []\n}\n\n/** The innermost node containing `pos`. */\nexport function nodeAt(root: Spanned, pos: Position, inclusive = false): Spanned | undefined {\n const path = pathAt(root, pos, inclusive)\n return path[path.length - 1]\n}\n\n/** The innermost node of one of `types` containing `pos`. */\nexport function enclosing<T extends Spanned>(\n root: Spanned,\n pos: Position,\n types: readonly string[],\n inclusive = false,\n): T | undefined {\n const path = pathAt(root, pos, inclusive)\n for (let i = path.length - 1; i >= 0; i--) {\n if (path[i].type && types.includes(path[i].type as string)) return path[i] as T\n }\n return undefined\n}\n\nfunction spanLength(node: Spanned): number {\n // Line count dominates: a node spanning fewer lines is nested deeper.\n return (node.line.end - node.line.start) * 10000 + (node.column.end - node.column.start)\n}\n\n/** Walk every node under `root`, depth first. */\nexport function walk(root: Spanned, visit: (node: Spanned, parent?: Spanned) => void, parent?: Spanned): void {\n visit(root, parent)\n for (const child of children(root)) walk(child, visit, root)\n}\n","/** Syntax errors, scope errors and type errors, as one list. */\nimport { DiagnosticSeverity, type Diagnostic } from \"vscode-languageserver\"\nimport type { Analysis } from \"../analysis.js\"\nimport { toRange, toPosition } from \"../ast-utils.js\"\n\nexport function diagnostics(analysis: Analysis): Diagnostic[] {\n const out: Diagnostic[] = []\n\n for (const error of analysis.parseErrors) {\n // A parse error points at a token, not a span; highlight to the end of\n // the word under it so the squiggle is visible.\n const start = toPosition(error.line, error.column)\n out.push({\n range: { start, end: { line: start.line, character: start.character + 1 } },\n severity: DiagnosticSeverity.Error,\n source: \"luaut\",\n code: \"syntax\",\n // The parser appends `(line:column)`; the range already says that.\n message: error.message.replace(/\\s*\\(\\d+:\\d+\\)$/, \"\"),\n })\n }\n\n for (const d of analysis.scopes.diagnostics) {\n out.push({\n range: toRange(d.node),\n severity: DiagnosticSeverity.Error,\n source: \"luaut\",\n code: d.kind,\n message: d.message,\n })\n }\n\n for (const d of analysis.types.diagnostics) {\n out.push({\n range: toRange(d.node),\n severity: DiagnosticSeverity.Error,\n source: \"luaut\",\n code: \"type\",\n message: d.message,\n })\n }\n\n return out\n}\n","/** Hover: the type of the thing under the cursor, as luaut would write it. */\nimport type { Hover, Position } from \"vscode-languageserver\"\nimport { formatType, type Identifier, type Expression, type Type } from \"luaut-parser\"\nimport { bindingOfNode, type Analysis } from \"../analysis.js\"\nimport { pathAt, toRange, type Spanned } from \"../ast-utils.js\"\n\nexport function hover(analysis: Analysis, position: Position): Hover | null {\n const path = pathAt(analysis.program, position, true)\n for (let i = path.length - 1; i >= 0; i--) {\n const node = path[i]\n const found = describe(analysis, node, path[i - 1])\n if (found) return { contents: { kind: \"markdown\", value: code(found) }, range: toRange(node) }\n }\n return null\n}\n\nfunction describe(analysis: Analysis, node: Spanned, parent?: Spanned): string | undefined {\n const { types } = analysis\n\n // A type alias reads as its definition rather than as a value.\n if (node.type === \"TypeAliasStatement\" || node.type === \"ExportTypeAliasStatement\") {\n const name = (node as unknown as { name: string }).name\n const alias = types.aliases.get(name)\n if (alias) return `type ${name} = ${formatType(alias)}`\n }\n\n if (node.type === \"Identifier\") {\n const identifier = node as unknown as Identifier\n // A reference: prefer the narrowed type — that is what the code sees\n // at this point, and the difference is the whole reason for narrowing.\n const narrowed = types.narrowedTypeOf.get(identifier)\n if (narrowed) return `${identifier.name}: ${formatType(narrowed)}`\n const binding = bindingOfNode(analysis, identifier)\n if (binding) {\n const type = types.bindingType.get(binding.id)\n if (type) return `${keyword(binding)} ${binding.name}: ${formatType(type)}`\n }\n // A property name: `x.foo` has no binding, but the member expression\n // it belongs to has a type.\n if (parent && (parent.type === \"MemberExpression\" || parent.type === \"MethodCallExpression\")) {\n const type = types.typeOf.get(parent as unknown as Expression)\n if (type) return `${identifier.name}: ${formatType(type)}`\n }\n }\n\n // Declarations: `const x`, a parameter, `const function f`.\n if (node.type === \"IdentifierPattern\" || node.type === \"FunctionParameter\"\n || node.type === \"TypedIdentifier\") {\n const binding = bindingOfNode(analysis, node)\n if (binding) {\n const type = types.bindingType.get(binding.id)\n if (type) return `${keyword(binding)} ${binding.name}: ${formatType(type)}`\n }\n }\n\n const type: Type | undefined = types.typeOf.get(node as unknown as Expression)\n return type ? formatType(type) : undefined\n}\n\nfunction keyword(binding: { kind: string; isConst?: boolean }): string {\n if (binding.kind === \"param\" || binding.kind === \"self\") return \"(parameter)\"\n if (binding.kind === \"global\") return \"(global)\"\n if (binding.kind.startsWith(\"for-\")) return \"(loop variable)\"\n return binding.isConst ? \"const\" : \"let\"\n}\n\nfunction code(text: string): string {\n return \"```luaut\\n\" + text + \"\\n```\"\n}\n","/**\n * Go-to-definition, find-references, rename and document highlight — all four\n * are the same question (\"which binding is this, and where else does it\n * appear?\") asked with different answers.\n */\nimport {\n DocumentHighlightKind,\n type DocumentHighlight, type Location, type Position, type Range,\n type TextEdit, type WorkspaceEdit,\n} from \"vscode-languageserver\"\nimport type { Binding } from \"luaut-parser\"\nimport { bindingOfNode, type Analysis } from \"../analysis.js\"\nimport { pathAt, toRange, type Spanned } from \"../ast-utils.js\"\n\n/** Nodes that can name a binding — as a use or as its declaration. */\nconst NAMING = new Set([\"Identifier\", \"IdentifierPattern\", \"FunctionParameter\", \"TypedIdentifier\"])\n\n/** The binding referred to at `position`, if the cursor is on a variable —\n * a use of it or its declaration. */\nexport function bindingAt(analysis: Analysis, position: Position): Binding | undefined {\n const path = pathAt(analysis.program, position, true)\n for (let i = path.length - 1; i >= 0; i--) {\n const node = path[i]\n if (!node.type || !NAMING.has(node.type)) continue\n const binding = bindingOfNode(analysis, node)\n if (binding) return binding\n }\n return undefined\n}\n\n/** Every place the binding appears: its declaration plus every reference. */\nfunction sites(binding: Binding): Spanned[] {\n const out: Spanned[] = []\n if (binding.declarationNode) out.push(binding.declarationNode as unknown as Spanned)\n out.push(...(binding.references as unknown as Spanned[]))\n return out\n}\n\nexport function definition(analysis: Analysis, position: Position): Location | null {\n const binding = bindingAt(analysis, position)\n if (!binding?.declarationNode) return null\n return { uri: analysis.uri, range: toRange(binding.declarationNode as unknown as Spanned) }\n}\n\nexport function references(\n analysis: Analysis,\n position: Position,\n includeDeclaration: boolean,\n): Location[] {\n const binding = bindingAt(analysis, position)\n if (!binding) return []\n const nodes = includeDeclaration ? sites(binding) : (binding.references as unknown as Spanned[])\n return nodes.map(node => ({ uri: analysis.uri, range: toRange(node) }))\n}\n\nexport function highlights(analysis: Analysis, position: Position): DocumentHighlight[] {\n const binding = bindingAt(analysis, position)\n if (!binding) return []\n return sites(binding).map(node => ({\n range: toRange(node),\n kind: node === binding.declarationNode\n ? DocumentHighlightKind.Write\n : DocumentHighlightKind.Read,\n }))\n}\n\n/** The range rename would replace, and the current name — so the editor can\n * refuse before it asks for a new one. */\nexport function prepareRename(\n analysis: Analysis,\n position: Position,\n): { range: Range; placeholder: string } | null {\n const binding = bindingAt(analysis, position)\n if (!binding) return null\n // A builtin lives in a definitions file; renaming it here would rename the\n // uses and leave the declaration behind.\n if (binding.isBuiltin || !binding.declarationNode) return null\n const path = pathAt(analysis.program, position, true)\n const identifier = [...path].reverse().find(n => !!n.type && NAMING.has(n.type))\n if (!identifier) return null\n return { range: toRange(identifier), placeholder: binding.name }\n}\n\nexport function rename(analysis: Analysis, position: Position, newName: string): WorkspaceEdit | null {\n if (!isIdentifier(newName)) return null\n const binding = bindingAt(analysis, position)\n if (!binding || binding.isBuiltin || !binding.declarationNode) return null\n const edits: TextEdit[] = sites(binding).map(node => ({ range: toRange(node), newText: newName }))\n return { changes: { [analysis.uri]: edits } }\n}\n\nconst IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/\nfunction isIdentifier(name: string): boolean {\n return IDENTIFIER.test(name)\n}\n","/** What members a type has — shared by completion and signature help. */\nimport { formatType, type FunctionType, type ObjectProperty, type Type } from \"luaut-parser\"\n\nexport interface Member {\n name: string\n property: ObjectProperty\n /** True when the member is a function whose first parameter is `self` —\n * i.e. it is meant to be called with `:`. */\n isMethod: boolean\n}\n\n/** The members of `type`, following aliases, merging intersections and keeping\n * only what every member of a union has (you can only reach a property that\n * is there whichever way the union went). */\nexport function membersOf(\n type: Type | undefined,\n aliases: ReadonlyMap<string, Type>,\n seen = new Set<Type>(),\n): Member[] {\n if (!type || seen.has(type)) return []\n seen.add(type)\n\n switch (type.kind) {\n case \"object\": {\n const out: Member[] = []\n for (const [name, property] of type.properties) {\n out.push({ name, property, isMethod: takesSelf(property.type) })\n }\n return out\n }\n case \"intersection\": {\n // Overload sets are intersections of functions and have no\n // members of their own; a `A & B` object contributes both sides.\n const merged = new Map<string, Member>()\n for (const part of type.types) {\n for (const member of membersOf(part, aliases, seen)) merged.set(member.name, member)\n }\n return [...merged.values()]\n }\n case \"union\": {\n const perBranch = type.types.map(part => membersOf(part, aliases, seen))\n if (!perBranch.length) return []\n const [first, ...rest] = perBranch\n return first.filter(member => rest.every(other => other.some(m => m.name === member.name)))\n }\n case \"genericRef\": {\n const alias = aliases.get(type.name)\n return alias ? membersOf(alias, aliases, seen) : []\n }\n case \"typeParam\":\n return membersOf(type.constraint, aliases, seen)\n default:\n return []\n }\n}\n\nexport function takesSelf(type: Type): boolean {\n for (const signature of signaturesOf(type)) {\n if (signature.params[0]?.name === \"self\") return true\n }\n return false\n}\n\n/** Every call signature of `type` — one for a function, several for an\n * overload set (which is an intersection of function types). */\nexport function signaturesOf(type: Type | undefined, aliases?: ReadonlyMap<string, Type>): FunctionType[] {\n if (!type) return []\n if (type.kind === \"function\") return [type]\n if (type.kind === \"intersection\") return type.types.flatMap(t => signaturesOf(t, aliases))\n if (type.kind === \"genericRef\" && aliases) {\n const alias = aliases.get(type.name)\n return alias ? signaturesOf(alias, aliases) : []\n }\n return []\n}\n\n/** `(a: number, b?: string) -> boolean`, and the pieces of it, for signature\n * help — which needs each parameter's own label to highlight the active one. */\nexport function signatureLabel(signature: FunctionType): { label: string; parameters: string[] } {\n const parameters = signature.params.map((p, i) => {\n const name = p.name ?? `arg${i + 1}`\n return `${name}${p.optional ? \"?\" : \"\"}: ${formatType(p.type)}`\n })\n const generics = signature.typeParams?.length ? `<${signature.typeParams.join(\", \")}>` : \"\"\n const varargs = signature.varargs ? [`...: ${formatType(signature.varargs)}`] : []\n const label = `${generics}(${[...parameters, ...varargs].join(\", \")}) -> ${formatType(signature.returns)}`\n return { label, parameters }\n}\n","/**\n * Completion.\n *\n * `x.` and `x:` are syntax errors, so the file as typed cannot answer \"what\n * are `x`'s members?\". The trick every language server of this shape uses:\n * substitute a placeholder identifier at the cursor, analyze *that* text, and\n * read the answer off the AST it produces. The user's document is untouched —\n * only the speculative copy is analyzed, and it is never cached.\n */\nimport {\n CompletionItemKind, InsertTextFormat,\n type CompletionItem, type Position,\n} from \"vscode-languageserver\"\nimport type { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { formatType, type Expression, type Type } from \"luaut-parser\"\nimport type { Analyzer } from \"../analysis.js\"\nimport { pathAt, type Spanned } from \"../ast-utils.js\"\nimport { membersOf, signaturesOf, signatureLabel } from \"./members.js\"\n\nconst PLACEHOLDER = \"__luautCompletion__\"\nconst IDENTIFIER_CHAR = /[A-Za-z0-9_]/\n\nexport function completion(\n analyzer: Analyzer,\n document: TextDocument,\n position: Position,\n): CompletionItem[] {\n const source = document.getText()\n const offset = document.offsetAt(position)\n\n // The word being typed, if any — replaced wholesale so a half-written\n // name cannot break the speculative parse.\n let start = offset\n while (start > 0 && IDENTIFIER_CHAR.test(source[start - 1])) start--\n let end = offset\n while (end < source.length && IDENTIFIER_CHAR.test(source[end])) end++\n\n // A method name has to be called to parse (`part:foo` alone is not a\n // statement), so the placeholder brings its own argument list unless the\n // source already has one.\n const afterColon = source[start - 1] === \":\"\n const alreadyCalled = /^\\s*\\(/.test(source.slice(end))\n const stand_in = afterColon && !alreadyCalled ? `${PLACEHOLDER}()` : PLACEHOLDER\n const patched = source.slice(0, start) + stand_in + source.slice(end)\n const analysis = analyzer.analyze(document.uri, -1, patched)\n\n // Where the placeholder sits, in the patched document's coordinates —\n // the same line, since the patch never spans one.\n const at: Position = { line: position.line, character: position.character - (offset - start) }\n const path = pathAt(analysis.program, at, true)\n const placeholder = [...path].reverse().find(\n n => n.type === \"Identifier\" && (n as unknown as { name: string }).name === PLACEHOLDER,\n )\n const parent = placeholder ? path[path.indexOf(placeholder) - 1] : path[path.length - 1]\n\n // Member access: `x.foo` / `x:foo`.\n if (parent && (parent.type === \"MemberExpression\" || parent.type === \"MethodCallExpression\")) {\n const object = (parent as unknown as { object: Expression }).object\n const type = analysis.types.typeOf.get(object)\n const wantMethods = parent.type === \"MethodCallExpression\"\n return membersOf(type, analysis.types.aliases)\n .filter(member => (wantMethods ? member.isMethod : true))\n .map(member => memberItem(member.name, member.property.type, member.property.readonly))\n }\n\n // A type position wants type names, not values.\n if (inTypePosition(path)) {\n const named: CompletionItem[] = [...analysis.types.aliases.keys()].map(name => ({\n label: name,\n kind: CompletionItemKind.Interface,\n detail: \"type\",\n }))\n const primitives: CompletionItem[] = PRIMITIVES.map(name => ({\n label: name,\n kind: CompletionItemKind.Keyword,\n detail: \"type\",\n }))\n return [...named, ...primitives]\n }\n\n return valueItems(analysis, at)\n}\n\n/** Names in scope at `at`. Scope analysis records where each binding is\n * declared but not the extent of its scope, so this approximates: everything\n * declared earlier in the file, plus the globals, which are visible\n * everywhere. Over-offering is the right failure — a name the editor lists\n * and the file rejects is a diagnostic away from being obvious. */\nfunction valueItems(analysis: ReturnType<Analyzer[\"analyze\"]>, at: Position): CompletionItem[] {\n const items: CompletionItem[] = []\n const seen = new Set<string>()\n for (const binding of analysis.scopes.bindings.values()) {\n if (binding.name === PLACEHOLDER || seen.has(binding.name)) continue\n const declaration = binding.declarationNode as unknown as Spanned | undefined\n if (declaration && declaration.line.start - 1 > at.line) continue\n seen.add(binding.name)\n const type = analysis.types.bindingType.get(binding.id)\n items.push({\n label: binding.name,\n kind: kindOf(type, binding.kind),\n detail: type ? formatType(type) : undefined,\n // Locals before globals, and globals before library names.\n sortText: `${binding.isBuiltin ? 2 : binding.kind === \"global\" ? 1 : 0}${binding.name}`,\n })\n }\n for (const keyword of KEYWORDS) {\n items.push({ label: keyword, kind: CompletionItemKind.Keyword, sortText: `3${keyword}` })\n }\n return items\n}\n\nfunction memberItem(name: string, type: Type, readonly?: boolean): CompletionItem {\n const signatures = signaturesOf(type)\n if (signatures.length) {\n return {\n label: name,\n kind: CompletionItemKind.Method,\n detail: signatureLabel(signatures[0]).label,\n insertText: `${name}($0)`,\n insertTextFormat: InsertTextFormat.Snippet,\n }\n }\n return {\n label: name,\n kind: CompletionItemKind.Field,\n detail: `${readonly ? \"readonly \" : \"\"}${formatType(type)}`,\n }\n}\n\nfunction kindOf(type: Type | undefined, bindingKind: string): CompletionItemKind {\n if (type && signaturesOf(type).length) return CompletionItemKind.Function\n if (bindingKind === \"param\" || bindingKind === \"self\") return CompletionItemKind.Variable\n return CompletionItemKind.Variable\n}\n\n/** Is the cursor inside a type annotation? Every type node's name ends in\n * `TypeNode`, plus the couple that do not. */\nfunction inTypePosition(path: readonly Spanned[]): boolean {\n return path.some(n =>\n !!n.type && (n.type.endsWith(\"TypeNode\") || n.type === \"TypeReference\"\n || n.type === \"TypeAliasStatement\" || n.type === \"ExportTypeAliasStatement\"),\n )\n}\n\nconst PRIMITIVES = [\n \"any\", \"unknown\", \"never\", \"nil\", \"boolean\", \"number\", \"string\", \"thread\", \"buffer\",\n]\n\nconst KEYWORDS = [\n \"const\", \"let\", \"function\", \"return\", \"if\", \"then\", \"elseif\", \"else\", \"end\",\n \"for\", \"in\", \"while\", \"do\", \"repeat\", \"until\", \"break\", \"continue\",\n \"type\", \"declare\", \"export\", \"import\", \"and\", \"or\", \"not\", \"true\", \"false\", \"nil\",\n]\n","/**\n * Signature help: the parameter list of the call the cursor sits inside.\n *\n * A call being typed is usually not yet a call — `add(1, ` has no argument\n * after the comma and no closing paren, and the parser drops the statement.\n * So, like completion, this analyzes a repaired copy of the text: the fewest\n * characters that make the call parse, tried in order.\n */\nimport type { Position, SignatureHelp, SignatureInformation } from \"vscode-languageserver\"\nimport type { TextDocument } from \"vscode-languageserver-textdocument\"\nimport type { Expression } from \"luaut-parser\"\nimport type { Analyzer, Analysis } from \"../analysis.js\"\nimport { containsPosition, pathAt, type Spanned } from \"../ast-utils.js\"\nimport { signaturesOf, signatureLabel } from \"./members.js\"\n\ninterface CallLike extends Spanned {\n type: \"CallExpression\" | \"MethodCallExpression\"\n arguments: Expression[]\n}\n\nexport function signatureHelp(\n analyzer: Analyzer,\n document: TextDocument,\n position: Position,\n): SignatureHelp | null {\n const source = document.getText()\n const offset = document.offsetAt(position)\n for (const repair of [\"\", \"nil\", \"nil)\", \")\"]) {\n const text = source.slice(0, offset) + repair + source.slice(offset)\n const analysis = analyzer.analyze(document.uri, -1, text)\n const found = helpAt(analysis, position)\n if (found) return found\n }\n return null\n}\n\nfunction helpAt(analysis: Analysis, position: Position): SignatureHelp | null {\n const path = pathAt(analysis.program, position, true)\n const call = [...path].reverse().find(\n n => n.type === \"CallExpression\" || n.type === \"MethodCallExpression\",\n ) as CallLike | undefined\n if (!call) return null\n\n const callee = call.type === \"CallExpression\"\n ? (call as unknown as { callee: Expression }).callee\n : (call as unknown as Expression)\n // For a method call the callee has no node of its own, so read the type of\n // the whole `obj:m` receiver path from the object plus the method name.\n const calleeType = call.type === \"CallExpression\"\n ? analysis.types.typeOf.get(callee)\n : methodType(analysis, call)\n\n const signatures = signaturesOf(calleeType, analysis.types.aliases)\n if (!signatures.length) return null\n\n // `:` supplies `self`, so the first written argument is the second param.\n const selfOffset = call.type === \"MethodCallExpression\" ? 1 : 0\n const written = activeArgument(call, position)\n\n const infos: SignatureInformation[] = signatures.map(signature => {\n const { label, parameters } = signatureLabel(signature)\n return { label, parameters: parameters.map(p => ({ label: p })) }\n })\n\n // Pick the overload that could still accept this many arguments.\n const wanted = written + selfOffset + 1\n let active = signatures.findIndex(s => s.params.length >= wanted || s.varargs)\n if (active < 0) active = 0\n\n return {\n signatures: infos,\n activeSignature: active,\n activeParameter: Math.min(\n written + selfOffset,\n Math.max(0, signatures[active].params.length - 1),\n ),\n }\n}\n\nfunction methodType(analysis: Analysis, call: CallLike): undefined | ReturnType<Analysis[\"types\"][\"typeOf\"][\"get\"]> {\n const object = (call as unknown as { object: Expression }).object\n const method = (call as unknown as { method: { name: string } }).method\n const objectType = analysis.types.typeOf.get(object)\n if (!objectType) return undefined\n return memberType(objectType, method.name, analysis)\n}\n\nfunction memberType(\n type: NonNullable<ReturnType<Analysis[\"types\"][\"typeOf\"][\"get\"]>>,\n name: string,\n analysis: Analysis,\n): ReturnType<Analysis[\"types\"][\"typeOf\"][\"get\"]> {\n if (type.kind === \"object\") return type.properties.get(name)?.type\n if (type.kind === \"intersection\") {\n for (const part of type.types) {\n const found = memberType(part, name, analysis)\n if (found) return found\n }\n }\n if (type.kind === \"genericRef\") {\n const alias = analysis.types.aliases.get(type.name)\n if (alias) return memberType(alias, name, analysis)\n }\n return undefined\n}\n\n/** Which argument the cursor is in — counted by which argument spans it, or\n * by how many end before it when the cursor is in the gap after a comma. */\nfunction activeArgument(call: CallLike, position: Position): number {\n const args = call.arguments\n for (let i = 0; i < args.length; i++) {\n if (containsPosition(args[i] as unknown as Spanned, position, true)) return i\n }\n let count = 0\n for (const arg of args as unknown as Spanned[]) {\n const before = arg.line.end - 1 < position.line\n || (arg.line.end - 1 === position.line && arg.column.end - 1 <= position.character)\n if (before) count++\n }\n return count\n}\n","/** Document symbols: the outline of a file. */\nimport { SymbolKind, type DocumentSymbol } from \"vscode-languageserver\"\nimport { formatType, type Identifier } from \"luaut-parser\"\nimport { bindingOfNode, type Analysis } from \"../analysis.js\"\nimport { toRange, walk, type Spanned } from \"../ast-utils.js\"\n\nexport function documentSymbols(analysis: Analysis): DocumentSymbol[] {\n const out: DocumentSymbol[] = []\n\n walk(analysis.program, node => {\n switch (node.type) {\n case \"FunctionDeclaration\":\n case \"FunctionDeclarationStatement\": {\n const name = functionName(node)\n if (name) out.push(symbol(name, SymbolKind.Function, node, detailOf(analysis, node)))\n break\n }\n case \"TypeAliasStatement\":\n case \"ExportTypeAliasStatement\": {\n // The alias name is an Identifier node here, a bare string on\n // a `declare` — take either.\n const named = (node as unknown as { name?: string | { name?: string } }).name\n const name = typeof named === \"string\" ? named : named?.name\n if (name) {\n const alias = analysis.types.aliases.get(name)\n out.push(symbol(name, SymbolKind.Interface, node, alias ? formatType(alias) : undefined))\n }\n break\n }\n case \"VariableDeclaration\": {\n for (const target of (node as unknown as { names?: Spanned[] }).names ?? []) {\n const name = (target as unknown as { name?: string }).name\n if (name) out.push(symbol(name, SymbolKind.Variable, target))\n }\n break\n }\n }\n })\n\n return out\n}\n\nfunction functionName(node: Spanned): string | undefined {\n const named = node as unknown as {\n name?: string | { name?: string }\n target?: { base?: { name?: string }; path?: { name?: string }[]; method?: { name: string } }\n }\n if (typeof named.name === \"string\") return named.name\n if (named.name && typeof named.name === \"object\") return named.name.name\n if (named.target?.base?.name) {\n const path = (named.target.path ?? []).map(p => p.name).filter(Boolean)\n const dotted = [named.target.base.name, ...path].join(\".\")\n return named.target.method ? `${dotted}:${named.target.method.name}` : dotted\n }\n return undefined\n}\n\n/** A function declaration is a statement, not an expression, so its type\n * comes from the binding it creates rather than from `typeOf`. */\nfunction detailOf(analysis: Analysis, node: Spanned): string | undefined {\n const name = (node as unknown as { name?: Identifier }).name\n if (name && typeof name === \"object\") {\n const binding = bindingOfNode(analysis, name)\n const type = binding && analysis.types.bindingType.get(binding.id)\n if (type) return formatType(type)\n }\n return undefined\n}\n\nfunction symbol(name: string, kind: SymbolKind, node: Spanned, detail?: string): DocumentSymbol {\n const range = toRange(node)\n return { name, kind, detail, range, selectionRange: range }\n}\n","/**\n * The language server: LSP wiring only.\n *\n * Every handler is the same three steps — get the cached analysis for the\n * document, ask one feature module a question, hand back the answer. The\n * thinking lives in `features/`; nothing here knows about luaut.\n */\nimport {\n createConnection, ProposedFeatures, TextDocuments, TextDocumentSyncKind,\n type Connection, type InitializeParams, type InitializeResult,\n} from \"vscode-languageserver/node\"\nimport { TextDocument } from \"vscode-languageserver-textdocument\"\nimport { Analyzer, type AnalyzerOptions } from \"./analysis.js\"\nimport { diagnostics } from \"./features/diagnostics.js\"\nimport { hover } from \"./features/hover.js\"\nimport { definition, references, highlights, prepareRename, rename } from \"./features/navigation.js\"\nimport { completion } from \"./features/completion.js\"\nimport { signatureHelp } from \"./features/signatureHelp.js\"\nimport { documentSymbols } from \"./features/symbols.js\"\n\nexport interface ServerOptions extends AnalyzerOptions {}\n\n/** Attach the luaut language server to a connection. Exported separately from\n * `startServer` so an editor extension can run it in-process over its own\n * transport, and so the tests can drive it without spawning anything. */\nexport function createServer(connection: Connection, options: ServerOptions = {}): void {\n const analyzer = new Analyzer(options)\n const documents = new TextDocuments(TextDocument)\n\n connection.onInitialize((_params: InitializeParams): InitializeResult => ({\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n hoverProvider: true,\n definitionProvider: true,\n referencesProvider: true,\n documentHighlightProvider: true,\n documentSymbolProvider: true,\n renameProvider: { prepareProvider: true },\n completionProvider: {\n // `.` and `:` open a member list; the rest of the time\n // completion is asked for as you type a word.\n triggerCharacters: [\".\", \":\"],\n resolveProvider: false,\n },\n signatureHelpProvider: { triggerCharacters: [\"(\", \",\"], retriggerCharacters: [\",\"] },\n },\n serverInfo: { name: \"luaut-language-server\" },\n }))\n\n // --- diagnostics -------------------------------------------------------\n const publish = (document: TextDocument): void => {\n void connection.sendDiagnostics({\n uri: document.uri,\n version: document.version,\n diagnostics: diagnostics(analyzer.get(document)),\n })\n }\n\n documents.onDidOpen(e => publish(e.document))\n documents.onDidChangeContent(e => publish(e.document))\n documents.onDidClose(e => {\n analyzer.forget(e.document.uri)\n void connection.sendDiagnostics({ uri: e.document.uri, diagnostics: [] })\n })\n\n // --- language features -------------------------------------------------\n const withDocument = <T>(uri: string, f: (document: TextDocument) => T, fallback: T): T => {\n const document = documents.get(uri)\n return document ? f(document) : fallback\n }\n\n connection.onHover(p => withDocument(\n p.textDocument.uri, d => hover(analyzer.get(d), p.position), null,\n ))\n\n connection.onDefinition(p => withDocument(\n p.textDocument.uri, d => definition(analyzer.get(d), p.position), null,\n ))\n\n connection.onReferences(p => withDocument(\n p.textDocument.uri,\n d => references(analyzer.get(d), p.position, p.context.includeDeclaration),\n [],\n ))\n\n connection.onDocumentHighlight(p => withDocument(\n p.textDocument.uri, d => highlights(analyzer.get(d), p.position), [],\n ))\n\n connection.onDocumentSymbol(p => withDocument(\n p.textDocument.uri, d => documentSymbols(analyzer.get(d)), [],\n ))\n\n connection.onPrepareRename(p => withDocument(\n p.textDocument.uri,\n d => {\n const prepared = prepareRename(analyzer.get(d), p.position)\n return prepared ? { range: prepared.range, placeholder: prepared.placeholder } : null\n },\n null,\n ))\n\n connection.onRenameRequest(p => withDocument(\n p.textDocument.uri, d => rename(analyzer.get(d), p.position, p.newName), null,\n ))\n\n connection.onCompletion(p => withDocument(\n p.textDocument.uri, d => completion(analyzer, d, p.position), [],\n ))\n\n connection.onSignatureHelp(p => withDocument(\n p.textDocument.uri, d => signatureHelp(analyzer, d, p.position), null,\n ))\n\n documents.listen(connection)\n connection.listen()\n}\n\n/** Run the server over stdio — the transport editors launch it with. */\nexport function startServer(options: ServerOptions = {}): void {\n createServer(createConnection(ProposedFeatures.all), options)\n}\n"],"mappings":"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","names":["type","formatType","formatType","formatType","keyword","formatType","formatType"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -17,6 +17,23 @@ function collect(statements, into) {
|
|
|
17
17
|
if (statement.type === "DeclareStatement") into.add(statement.name);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
function bindingOfNode(analysis, node) {
|
|
21
|
+
const used = (0, import_luaut_parser.getBinding)(analysis.scopes, node);
|
|
22
|
+
if (used) return used;
|
|
23
|
+
return declarationIndex(analysis).get(node);
|
|
24
|
+
}
|
|
25
|
+
var declarationIndexes = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
function declarationIndex(analysis) {
|
|
27
|
+
let index = declarationIndexes.get(analysis);
|
|
28
|
+
if (!index) {
|
|
29
|
+
index = /* @__PURE__ */ new Map();
|
|
30
|
+
for (const binding of analysis.scopes.bindings.values()) {
|
|
31
|
+
if (binding.declarationNode) index.set(binding.declarationNode, binding);
|
|
32
|
+
}
|
|
33
|
+
declarationIndexes.set(analysis, index);
|
|
34
|
+
}
|
|
35
|
+
return index;
|
|
36
|
+
}
|
|
20
37
|
var Analyzer = class {
|
|
21
38
|
libs;
|
|
22
39
|
builtinGlobals;
|
|
@@ -160,7 +177,7 @@ function hover(analysis, position) {
|
|
|
160
177
|
return null;
|
|
161
178
|
}
|
|
162
179
|
function describe(analysis, node, parent) {
|
|
163
|
-
const { types
|
|
180
|
+
const { types } = analysis;
|
|
164
181
|
if (node.type === "TypeAliasStatement" || node.type === "ExportTypeAliasStatement") {
|
|
165
182
|
const name = node.name;
|
|
166
183
|
const alias = types.aliases.get(name);
|
|
@@ -170,7 +187,7 @@ function describe(analysis, node, parent) {
|
|
|
170
187
|
const identifier = node;
|
|
171
188
|
const narrowed = types.narrowedTypeOf.get(identifier);
|
|
172
189
|
if (narrowed) return `${identifier.name}: ${(0, import_luaut_parser2.formatType)(narrowed)}`;
|
|
173
|
-
const binding = (
|
|
190
|
+
const binding = bindingOfNode(analysis, identifier);
|
|
174
191
|
if (binding) {
|
|
175
192
|
const type2 = types.bindingType.get(binding.id);
|
|
176
193
|
if (type2) return `${keyword(binding)} ${binding.name}: ${(0, import_luaut_parser2.formatType)(type2)}`;
|
|
@@ -180,8 +197,8 @@ function describe(analysis, node, parent) {
|
|
|
180
197
|
if (type2) return `${identifier.name}: ${(0, import_luaut_parser2.formatType)(type2)}`;
|
|
181
198
|
}
|
|
182
199
|
}
|
|
183
|
-
if (node.type === "IdentifierPattern") {
|
|
184
|
-
const binding = (
|
|
200
|
+
if (node.type === "IdentifierPattern" || node.type === "FunctionParameter" || node.type === "TypedIdentifier") {
|
|
201
|
+
const binding = bindingOfNode(analysis, node);
|
|
185
202
|
if (binding) {
|
|
186
203
|
const type2 = types.bindingType.get(binding.id);
|
|
187
204
|
if (type2) return `${keyword(binding)} ${binding.name}: ${(0, import_luaut_parser2.formatType)(type2)}`;
|
|
@@ -202,13 +219,13 @@ function code(text) {
|
|
|
202
219
|
|
|
203
220
|
// src/features/navigation.ts
|
|
204
221
|
var import_vscode_languageserver2 = require("vscode-languageserver");
|
|
205
|
-
var
|
|
222
|
+
var NAMING = /* @__PURE__ */ new Set(["Identifier", "IdentifierPattern", "FunctionParameter", "TypedIdentifier"]);
|
|
206
223
|
function bindingAt(analysis, position) {
|
|
207
224
|
const path = pathAt(analysis.program, position, true);
|
|
208
225
|
for (let i = path.length - 1; i >= 0; i--) {
|
|
209
226
|
const node = path[i];
|
|
210
|
-
if (node.type
|
|
211
|
-
const binding = (
|
|
227
|
+
if (!node.type || !NAMING.has(node.type)) continue;
|
|
228
|
+
const binding = bindingOfNode(analysis, node);
|
|
212
229
|
if (binding) return binding;
|
|
213
230
|
}
|
|
214
231
|
return void 0;
|
|
@@ -243,7 +260,7 @@ function prepareRename(analysis, position) {
|
|
|
243
260
|
if (!binding) return null;
|
|
244
261
|
if (binding.isBuiltin || !binding.declarationNode) return null;
|
|
245
262
|
const path = pathAt(analysis.program, position, true);
|
|
246
|
-
const identifier = [...path].reverse().find((n) => n.type
|
|
263
|
+
const identifier = [...path].reverse().find((n) => !!n.type && NAMING.has(n.type));
|
|
247
264
|
if (!identifier) return null;
|
|
248
265
|
return { range: toRange(identifier), placeholder: binding.name };
|
|
249
266
|
}
|
|
@@ -261,10 +278,10 @@ function isIdentifier(name) {
|
|
|
261
278
|
|
|
262
279
|
// src/features/completion.ts
|
|
263
280
|
var import_vscode_languageserver3 = require("vscode-languageserver");
|
|
264
|
-
var
|
|
281
|
+
var import_luaut_parser4 = require("luaut-parser");
|
|
265
282
|
|
|
266
283
|
// src/features/members.ts
|
|
267
|
-
var
|
|
284
|
+
var import_luaut_parser3 = require("luaut-parser");
|
|
268
285
|
function membersOf(type, aliases, seen = /* @__PURE__ */ new Set()) {
|
|
269
286
|
if (!type || seen.has(type)) return [];
|
|
270
287
|
seen.add(type);
|
|
@@ -318,11 +335,11 @@ function signaturesOf(type, aliases) {
|
|
|
318
335
|
function signatureLabel(signature) {
|
|
319
336
|
const parameters = signature.params.map((p, i) => {
|
|
320
337
|
const name = p.name ?? `arg${i + 1}`;
|
|
321
|
-
return `${name}${p.optional ? "?" : ""}: ${(0,
|
|
338
|
+
return `${name}${p.optional ? "?" : ""}: ${(0, import_luaut_parser3.formatType)(p.type)}`;
|
|
322
339
|
});
|
|
323
340
|
const generics = signature.typeParams?.length ? `<${signature.typeParams.join(", ")}>` : "";
|
|
324
|
-
const varargs = signature.varargs ? [`...: ${(0,
|
|
325
|
-
const label = `${generics}(${[...parameters, ...varargs].join(", ")}) -> ${(0,
|
|
341
|
+
const varargs = signature.varargs ? [`...: ${(0, import_luaut_parser3.formatType)(signature.varargs)}`] : [];
|
|
342
|
+
const label = `${generics}(${[...parameters, ...varargs].join(", ")}) -> ${(0, import_luaut_parser3.formatType)(signature.returns)}`;
|
|
326
343
|
return { label, parameters };
|
|
327
344
|
}
|
|
328
345
|
|
|
@@ -380,7 +397,7 @@ function valueItems(analysis, at) {
|
|
|
380
397
|
items.push({
|
|
381
398
|
label: binding.name,
|
|
382
399
|
kind: kindOf(type, binding.kind),
|
|
383
|
-
detail: type ? (0,
|
|
400
|
+
detail: type ? (0, import_luaut_parser4.formatType)(type) : void 0,
|
|
384
401
|
// Locals before globals, and globals before library names.
|
|
385
402
|
sortText: `${binding.isBuiltin ? 2 : binding.kind === "global" ? 1 : 0}${binding.name}`
|
|
386
403
|
});
|
|
@@ -404,7 +421,7 @@ function memberItem(name, type, readonly) {
|
|
|
404
421
|
return {
|
|
405
422
|
label: name,
|
|
406
423
|
kind: import_vscode_languageserver3.CompletionItemKind.Field,
|
|
407
|
-
detail: `${readonly ? "readonly " : ""}${(0,
|
|
424
|
+
detail: `${readonly ? "readonly " : ""}${(0, import_luaut_parser4.formatType)(type)}`
|
|
408
425
|
};
|
|
409
426
|
}
|
|
410
427
|
function kindOf(type, bindingKind) {
|
|
@@ -534,7 +551,7 @@ function activeArgument(call, position) {
|
|
|
534
551
|
|
|
535
552
|
// src/features/symbols.ts
|
|
536
553
|
var import_vscode_languageserver4 = require("vscode-languageserver");
|
|
537
|
-
var
|
|
554
|
+
var import_luaut_parser5 = require("luaut-parser");
|
|
538
555
|
function documentSymbols(analysis) {
|
|
539
556
|
const out = [];
|
|
540
557
|
walk(analysis.program, (node) => {
|
|
@@ -551,7 +568,7 @@ function documentSymbols(analysis) {
|
|
|
551
568
|
const name = typeof named === "string" ? named : named?.name;
|
|
552
569
|
if (name) {
|
|
553
570
|
const alias = analysis.types.aliases.get(name);
|
|
554
|
-
out.push(symbol(name, import_vscode_languageserver4.SymbolKind.Interface, node, alias ? (0,
|
|
571
|
+
out.push(symbol(name, import_vscode_languageserver4.SymbolKind.Interface, node, alias ? (0, import_luaut_parser5.formatType)(alias) : void 0));
|
|
555
572
|
}
|
|
556
573
|
break;
|
|
557
574
|
}
|
|
@@ -580,9 +597,9 @@ function functionName(node) {
|
|
|
580
597
|
function detailOf(analysis, node) {
|
|
581
598
|
const name = node.name;
|
|
582
599
|
if (name && typeof name === "object") {
|
|
583
|
-
const binding = (
|
|
600
|
+
const binding = bindingOfNode(analysis, name);
|
|
584
601
|
const type = binding && analysis.types.bindingType.get(binding.id);
|
|
585
|
-
if (type) return (0,
|
|
602
|
+
if (type) return (0, import_luaut_parser5.formatType)(type);
|
|
586
603
|
}
|
|
587
604
|
return void 0;
|
|
588
605
|
}
|