@venn-lang/lsp 0.1.1 → 0.1.2
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/dist/bin/venn-lsp.mjs +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{server-BET60xwK.mjs → server-CS9hyKZX.mjs} +28 -6
- package/dist/server-CS9hyKZX.mjs.map +1 -0
- package/package.json +7 -7
- package/src/completion/context.ts +32 -5
- package/dist/server-BET60xwK.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venn-lang/lsp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The Venn language server: diagnostics, hover, completion, definition, references, rename, signature help, semantic tokens, formatting, document highlight and quick fixes for .vn files.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"venn",
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"langium": "^4.3.1",
|
|
45
45
|
"vscode-languageserver": "^10.0.1",
|
|
46
46
|
"vscode-languageserver-textdocument": "^1.0.13",
|
|
47
|
-
"@venn-lang/contracts": "0.1.
|
|
48
|
-
"@venn-lang/core": "0.1.
|
|
49
|
-
"@venn-lang/runtime": "0.1.
|
|
50
|
-
"@venn-lang/sdk": "0.1.
|
|
51
|
-
"@venn-lang/stdlib": "0.1.
|
|
52
|
-
"@venn-lang/types": "0.1.
|
|
47
|
+
"@venn-lang/contracts": "0.1.2",
|
|
48
|
+
"@venn-lang/core": "0.1.2",
|
|
49
|
+
"@venn-lang/runtime": "0.1.2",
|
|
50
|
+
"@venn-lang/sdk": "0.1.2",
|
|
51
|
+
"@venn-lang/stdlib": "0.1.2",
|
|
52
|
+
"@venn-lang/types": "0.1.2"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/node": "^24",
|
|
@@ -14,7 +14,6 @@ const ACTION = /(?<![\w.])([A-Za-z_]\w*(?:\.\w+)*)\.(\w*)$/;
|
|
|
14
14
|
const ANNOTATION = /@(\w*)$/;
|
|
15
15
|
const FRAGMENT = /\brun\s+(\w*)$/;
|
|
16
16
|
const MATCHER = /\bexpect\b\s+\S+\s+(\w*)$/;
|
|
17
|
-
const WORD = /(\w*)$/;
|
|
18
17
|
/**
|
|
19
18
|
* Where a type goes: after the `:` of a field, a parameter or a binding, after
|
|
20
19
|
* the `->` of a declared return, or after the `|` of a union.
|
|
@@ -30,7 +29,7 @@ const TYPE_POSITION = /(?:^|[^:])(?::|->|\|)\s*([A-Za-z_][\w.]*)?$/;
|
|
|
30
29
|
* `http.on api ▮`, `print ▮`. The leading anchor keeps this to the head of a
|
|
31
30
|
* statement, so `1 + ▮` and the tail of an expression are not swept in.
|
|
32
31
|
*/
|
|
33
|
-
const ARGUMENT = /^\s*([A-Za-z_]\w*(?:\.\w+)*)\s
|
|
32
|
+
const ARGUMENT = /^\s*([A-Za-z_]\w*(?:\.\w+)*)\s/;
|
|
34
33
|
const FROM_PATH = /\bfrom\s+"([^"]+)"/;
|
|
35
34
|
/**
|
|
36
35
|
* A dot whose receiver no path can name: `1234.567.round`, `f(x).len`,
|
|
@@ -68,7 +67,7 @@ export function contextAt(text: CursorText): CompletionContext {
|
|
|
68
67
|
return (
|
|
69
68
|
optionContext(text) ??
|
|
70
69
|
typeContext(text) ??
|
|
71
|
-
argumentContext(prefix) ?? { kind: "statement", from: back(prefix,
|
|
70
|
+
argumentContext(prefix) ?? { kind: "statement", from: back(prefix, trailingWord(prefix)) }
|
|
72
71
|
);
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -86,9 +85,13 @@ function typeContext(text: CursorText): CompletionContext | undefined {
|
|
|
86
85
|
|
|
87
86
|
/** `http.on ▮`: the head of the line is a verb, and an argument is due. */
|
|
88
87
|
function argumentContext(prefix: string): CompletionContext | undefined {
|
|
88
|
+
// A brace anywhere means this is a map being written, not an argument. Asked
|
|
89
|
+
// outright rather than folded into the pattern as a trailing `[^{}]*$`: that
|
|
90
|
+
// made the pattern describe the whole line to say something about its head.
|
|
91
|
+
if (prefix.includes("{") || prefix.includes("}")) return undefined;
|
|
89
92
|
const found = ARGUMENT.exec(prefix);
|
|
90
93
|
if (!found?.[1]) return undefined;
|
|
91
|
-
return { kind: "argument", target: found[1], from: back(prefix,
|
|
94
|
+
return { kind: "argument", target: found[1], from: back(prefix, trailingWord(prefix)) };
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
/**
|
|
@@ -102,7 +105,7 @@ function optionContext(text: CursorText): CompletionContext | undefined {
|
|
|
102
105
|
if (open < 0) return undefined;
|
|
103
106
|
const target = OPTION_OWNER.exec(text.before.slice(0, open))?.[1];
|
|
104
107
|
if (!target) return undefined;
|
|
105
|
-
return { kind: "optionKey", target, from: back(text.prefix,
|
|
108
|
+
return { kind: "optionKey", target, from: back(text.prefix, trailingWord(text.prefix)) };
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
/** The `{` still open at the cursor, scanning backwards. */
|
|
@@ -142,3 +145,27 @@ function pathOf(line: string): string | undefined {
|
|
|
142
145
|
function back(prefix: string, partial: string | undefined): number {
|
|
143
146
|
return prefix.length - (partial?.length ?? 0);
|
|
144
147
|
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The word being typed at the end of `text`, empty when the last character is
|
|
151
|
+
* not part of one.
|
|
152
|
+
*
|
|
153
|
+
* Scanned backwards rather than matched with `/(\w*)$/`. That pattern has
|
|
154
|
+
* nothing to anchor to, so the engine tries it at every position, which is
|
|
155
|
+
* quadratic in the length of the line: 27ms at ten thousand characters and
|
|
156
|
+
* 454ms at forty thousand, while a completion is meant to be instant. A line is
|
|
157
|
+
* as long as whoever wrote the file made it.
|
|
158
|
+
*/
|
|
159
|
+
function trailingWord(text: string): string {
|
|
160
|
+
let at = text.length;
|
|
161
|
+
while (at > 0 && isWordCharacter(text.charCodeAt(at - 1))) at -= 1;
|
|
162
|
+
return text.slice(at);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** `\w`: a digit, a letter or an underscore. */
|
|
166
|
+
function isWordCharacter(code: number): boolean {
|
|
167
|
+
if (code >= 48 && code <= 57) return true;
|
|
168
|
+
if (code >= 65 && code <= 90) return true;
|
|
169
|
+
if (code >= 97 && code <= 122) return true;
|
|
170
|
+
return code === 95;
|
|
171
|
+
}
|