@adecore/lsp 0.0.1 → 0.17.0-beta.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.
Files changed (48) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +47 -0
  3. package/dist/connection.d.ts +50 -0
  4. package/dist/connection.js +290 -0
  5. package/dist/document.d.ts +60 -0
  6. package/dist/document.js +282 -0
  7. package/dist/edits.d.ts +18 -0
  8. package/dist/edits.js +161 -0
  9. package/dist/file-operations.d.ts +5 -0
  10. package/dist/file-operations.js +23 -0
  11. package/dist/glob.d.ts +1 -0
  12. package/dist/glob.js +67 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.js +10 -0
  15. package/dist/protocol.d.ts +470 -0
  16. package/dist/protocol.js +1 -0
  17. package/dist/service.d.ts +53 -0
  18. package/dist/service.js +1 -0
  19. package/dist/session.d.ts +78 -0
  20. package/dist/session.js +528 -0
  21. package/dist/test-transport.d.ts +33 -0
  22. package/dist/test-transport.js +72 -0
  23. package/dist/testing.d.ts +33 -0
  24. package/dist/testing.js +72 -0
  25. package/dist/transport.d.ts +27 -0
  26. package/dist/transport.js +258 -0
  27. package/dist/uris.d.ts +2 -0
  28. package/dist/uris.js +28 -0
  29. package/dist/vue.d.ts +6 -0
  30. package/dist/vue.js +86 -0
  31. package/dist/watched-files.d.ts +3 -0
  32. package/dist/watched-files.js +41 -0
  33. package/package.json +36 -3
  34. package/src/connection.ts +310 -0
  35. package/src/document.ts +357 -0
  36. package/src/edits.ts +182 -0
  37. package/src/file-operations.ts +30 -0
  38. package/src/glob.ts +60 -0
  39. package/src/index.ts +30 -0
  40. package/src/protocol.ts +479 -0
  41. package/src/service.ts +109 -0
  42. package/src/session.ts +613 -0
  43. package/src/test-transport.ts +92 -0
  44. package/src/testing.ts +105 -0
  45. package/src/transport.ts +290 -0
  46. package/src/uris.ts +29 -0
  47. package/src/vue.ts +93 -0
  48. package/src/watched-files.ts +49 -0
@@ -0,0 +1,282 @@
1
+ import { ErrorCodes, LspError, StaleResultError } from './connection.js';
2
+ import { applyContentChanges, minimalChange } from './edits.js';
3
+ /*
4
+ * One open text document in one session. It owns the version: every change raises it by one, and a
5
+ * result that arrives for an older version is refused, so nothing computed for a text that is gone
6
+ * reaches the caller.
7
+ */
8
+ export class LspDocument {
9
+ session;
10
+ uri;
11
+ languageId;
12
+ ready;
13
+ revision;
14
+ content;
15
+ closed = false;
16
+ synced;
17
+ closePromise;
18
+ inFlight = new Set();
19
+ latest = new Map();
20
+ resultVersions = new WeakMap();
21
+ constructor(session, item) {
22
+ // A uri that does not parse is the caller's bug, and fails here instead of inside a server.
23
+ new URL(item.uri);
24
+ this.session = session;
25
+ this.uri = item.uri;
26
+ this.languageId = item.languageId;
27
+ this.content = item.text;
28
+ this.revision = item.version ?? 1;
29
+ if (!Number.isSafeInteger(this.revision) || this.revision < 0) {
30
+ throw new LspError('Invalid document version');
31
+ }
32
+ this.ready = session.syncOptions.openClose
33
+ ? session.notify('textDocument/didOpen', { textDocument: { ...item, version: this.revision } })
34
+ : Promise.resolve();
35
+ this.synced = this.ready;
36
+ void this.ready.catch((error) => session.connection.reportError(error));
37
+ }
38
+ get version() {
39
+ return this.revision;
40
+ }
41
+ get text() {
42
+ return this.content;
43
+ }
44
+ get isClosed() {
45
+ return this.closed;
46
+ }
47
+ /* Sends the difference to the text as the one range that changed, or the whole text to a server that wants no ranges. */
48
+ updateText(text) {
49
+ this.assertOpen();
50
+ if (text === this.content) {
51
+ return this.synced;
52
+ }
53
+ return this.applyChanges([minimalChange(this.content, text)]);
54
+ }
55
+ /*
56
+ * Applies `didChange` entries in order, which a server that negotiated incremental sync gets as
57
+ * they are. The version goes up by one even when the text ends up the same.
58
+ */
59
+ applyChanges(changes) {
60
+ this.assertOpen();
61
+ const kind = this.session.syncOptions.change ?? 0;
62
+ if (!kind) {
63
+ return Promise.reject(new LspError('Server did not negotiate document changes', ErrorCodes.MethodNotFound));
64
+ }
65
+ let next;
66
+ try {
67
+ next = applyContentChanges(this.content, changes);
68
+ }
69
+ catch (error) {
70
+ return Promise.reject(error);
71
+ }
72
+ this.content = next;
73
+ const version = ++this.revision;
74
+ this.cancelRequests();
75
+ const contentChanges = kind === 1 ? [{ text: next }] : changes.map((change) => ({ ...change }));
76
+ this.synced = this.synced.then(() => this.session.connection.notify('textDocument/didChange', { textDocument: { uri: this.uri, version }, contentChanges }));
77
+ void this.synced.catch((error) => this.session.connection.reportError(error));
78
+ return this.synced;
79
+ }
80
+ /* Call it once the file is on disk; it sends `didSave` when the server asked for it. */
81
+ async save() {
82
+ this.assertOpen();
83
+ const version = this.version;
84
+ const text = this.text;
85
+ await this.synced;
86
+ this.assertCurrent(version);
87
+ const save = this.session.syncOptions.save;
88
+ if (save) {
89
+ await this.session.notify('textDocument/didSave', {
90
+ textDocument: { uri: this.uri },
91
+ text: typeof save === 'object' && save.includeText ? text : undefined
92
+ });
93
+ }
94
+ }
95
+ completion(position, context, options) {
96
+ return this.feature('textDocument/completion', { position, context }, options);
97
+ }
98
+ resolveCompletion(item, options) {
99
+ return this.resolve('completionItem/resolve', item, options);
100
+ }
101
+ hover(position, options) {
102
+ return this.feature('textDocument/hover', { position }, options);
103
+ }
104
+ signatureHelp(position, context, options) {
105
+ return this.feature('textDocument/signatureHelp', { position, context }, options);
106
+ }
107
+ definition(position, options) {
108
+ return this.feature('textDocument/definition', { position }, options);
109
+ }
110
+ declaration(position, options) {
111
+ return this.feature('textDocument/declaration', { position }, options);
112
+ }
113
+ typeDefinition(position, options) {
114
+ return this.feature('textDocument/typeDefinition', { position }, options);
115
+ }
116
+ implementation(position, options) {
117
+ return this.feature('textDocument/implementation', { position }, options);
118
+ }
119
+ references(position, includeDeclaration = true, options) {
120
+ return this.feature('textDocument/references', { position, context: { includeDeclaration } }, options);
121
+ }
122
+ documentHighlights(position, options) {
123
+ return this.feature('textDocument/documentHighlight', { position }, options);
124
+ }
125
+ documentSymbols(options) {
126
+ return this.feature('textDocument/documentSymbol', {}, options);
127
+ }
128
+ prepareRename(position, options) {
129
+ return this.feature('textDocument/prepareRename', { position }, options);
130
+ }
131
+ rename(position, newName, options) {
132
+ return this.feature('textDocument/rename', { position, newName }, options);
133
+ }
134
+ codeActions(range, context = { diagnostics: [] }, options) {
135
+ return this.feature('textDocument/codeAction', { range, context }, options);
136
+ }
137
+ resolveCodeAction(action, options) {
138
+ return this.resolve('codeAction/resolve', action, options);
139
+ }
140
+ codeLenses(options) {
141
+ return this.feature('textDocument/codeLens', {}, options);
142
+ }
143
+ resolveCodeLens(lens, options) {
144
+ return this.resolve('codeLens/resolve', lens, options);
145
+ }
146
+ formatting(formatting, options) {
147
+ return this.feature('textDocument/formatting', { options: formatting }, options);
148
+ }
149
+ rangeFormatting(range, formatting, options) {
150
+ return this.feature('textDocument/rangeFormatting', { range, options: formatting }, options);
151
+ }
152
+ foldingRanges(options) {
153
+ return this.feature('textDocument/foldingRange', {}, options);
154
+ }
155
+ selectionRanges(positions, options) {
156
+ return this.feature('textDocument/selectionRange', { positions }, options);
157
+ }
158
+ semanticTokens(options) {
159
+ return this.feature('textDocument/semanticTokens/full', {}, options);
160
+ }
161
+ semanticTokensDelta(previousResultId, options) {
162
+ return this.feature('textDocument/semanticTokens/full/delta', { previousResultId }, options);
163
+ }
164
+ semanticTokensRange(range, options) {
165
+ return this.feature('textDocument/semanticTokens/range', { range }, options);
166
+ }
167
+ inlayHints(range, options) {
168
+ return this.feature('textDocument/inlayHint', { range }, options);
169
+ }
170
+ resolveInlayHint(hint, options) {
171
+ return this.resolve('inlayHint/resolve', hint, options);
172
+ }
173
+ diagnostics(previousResultId, identifier, options) {
174
+ return this.feature('textDocument/diagnostic', { previousResultId, identifier }, options);
175
+ }
176
+ /* Any feature by its LSP method, for a caller that gets the method from elsewhere. `params` leave out `textDocument`; a resolve method takes the item. */
177
+ request(method, params, options) {
178
+ return method.endsWith('/resolve') ? this.resolve(method, params, options) : this.feature(method, params, options);
179
+ }
180
+ /* Sends `didClose` after every change queued before it. The uri stays taken until then, so a reopen cannot overtake it. */
181
+ close() {
182
+ if (this.closePromise) {
183
+ return this.closePromise;
184
+ }
185
+ if (this.closed) {
186
+ return Promise.resolve();
187
+ }
188
+ this.closed = true;
189
+ this.cancelRequests();
190
+ this.closePromise = this.synced
191
+ .then(async () => {
192
+ if (!this.session.connection.isClosed && this.session.syncOptions.openClose) {
193
+ await this.session.connection.notify('textDocument/didClose', { textDocument: { uri: this.uri } });
194
+ }
195
+ })
196
+ .finally(() => this.session.forgetDocument(this));
197
+ return this.closePromise;
198
+ }
199
+ /* The connection is gone, so there is nobody to tell. */
200
+ disconnect() {
201
+ this.closed = true;
202
+ this.cancelRequests();
203
+ this.session.forgetDocument(this);
204
+ }
205
+ feature(method, params, options) {
206
+ return this.perform(method, { ...params, textDocument: { uri: this.uri } }, options);
207
+ }
208
+ resolve(method, item, options) {
209
+ const version = this.resultVersions.get(item);
210
+ if (version !== undefined && version !== this.version) {
211
+ return Promise.reject(new StaleResultError(this.uri));
212
+ }
213
+ return this.perform(method, item, options);
214
+ }
215
+ async perform(method, params, options = {}) {
216
+ this.assertOpen();
217
+ if (!this.session.supports(method, this)) {
218
+ throw new LspError(`Server does not support ${method}`, ErrorCodes.MethodNotFound);
219
+ }
220
+ const version = this.version;
221
+ const controller = new AbortController();
222
+ if (options.cancelPrevious !== false) {
223
+ this.latest.get(method)?.abort();
224
+ }
225
+ this.latest.set(method, controller);
226
+ this.inFlight.add(controller);
227
+ const abort = () => controller.abort();
228
+ options.signal?.addEventListener('abort', abort, { once: true });
229
+ if (options.signal?.aborted) {
230
+ abort();
231
+ }
232
+ try {
233
+ await this.synced;
234
+ this.assertCurrent(version);
235
+ const result = await this.session.request(method, params, { signal: controller.signal, timeoutMs: options.timeoutMs });
236
+ this.assertCurrent(version);
237
+ this.remember(result, version);
238
+ return result;
239
+ }
240
+ catch (error) {
241
+ this.assertCurrent(version);
242
+ throw error;
243
+ }
244
+ finally {
245
+ options.signal?.removeEventListener('abort', abort);
246
+ this.inFlight.delete(controller);
247
+ if (this.latest.get(method) === controller) {
248
+ this.latest.delete(method);
249
+ }
250
+ }
251
+ }
252
+ /* A completion item, code action or hint resolves only against the version it was produced for. */
253
+ remember(result, version) {
254
+ if (!result || typeof result !== 'object') {
255
+ return;
256
+ }
257
+ this.resultVersions.set(result, version);
258
+ if (Array.isArray(result)) {
259
+ for (const item of result) {
260
+ this.remember(item, version);
261
+ }
262
+ }
263
+ else if ('items' in result) {
264
+ this.remember(result.items, version);
265
+ }
266
+ }
267
+ assertOpen() {
268
+ if (this.closed) {
269
+ throw new LspError(`Document is closed: ${this.uri}`);
270
+ }
271
+ }
272
+ assertCurrent(version) {
273
+ if (this.closed || this.version !== version) {
274
+ throw new StaleResultError(this.uri);
275
+ }
276
+ }
277
+ cancelRequests() {
278
+ for (const controller of this.inFlight) {
279
+ controller.abort();
280
+ }
281
+ }
282
+ }
@@ -0,0 +1,18 @@
1
+ import type { ContentChange, Position, TextEdit, WorkspaceEdit } from './protocol.ts';
2
+ export interface DocumentSnapshot {
3
+ text: string;
4
+ version: number | null;
5
+ }
6
+ export interface PlannedDocumentEdit {
7
+ uri: string;
8
+ version: number | null;
9
+ before: string;
10
+ text: string;
11
+ }
12
+ export declare function offsetAt(text: string, position: Position): number;
13
+ export declare function positionAt(text: string, offset: number): Position;
14
+ export declare function endPosition(text: string): Position;
15
+ export declare function applyTextEdits(text: string, edits: readonly TextEdit[]): string;
16
+ export declare function applyContentChanges(text: string, changes: readonly ContentChange[]): string;
17
+ export declare function minimalChange(before: string, after: string): ContentChange;
18
+ export declare function planWorkspaceEdit(edit: WorkspaceEdit, snapshots: ReadonlyMap<string, DocumentSnapshot>): PlannedDocumentEdit[];
package/dist/edits.js ADDED
@@ -0,0 +1,161 @@
1
+ import { ErrorCodes, LspError } from './connection.js';
2
+ /* A line ends at `\n`, `\r\n` or a lone `\r`, which is how the protocol counts lines. */
3
+ export function offsetAt(text, position) {
4
+ if (!Number.isInteger(position.line) || !Number.isInteger(position.character) || position.line < 0 || position.character < 0) {
5
+ throw new LspError('Invalid edit position');
6
+ }
7
+ let line = 0;
8
+ let offset = 0;
9
+ while (line < position.line && offset < text.length) {
10
+ const char = text.charCodeAt(offset++);
11
+ if (char === 13) {
12
+ if (text.charCodeAt(offset) === 10) {
13
+ offset++;
14
+ }
15
+ line++;
16
+ }
17
+ else if (char === 10) {
18
+ line++;
19
+ }
20
+ }
21
+ if (line !== position.line) {
22
+ throw new LspError('Edit line is outside the document');
23
+ }
24
+ let end = offset;
25
+ while (end < text.length && text[end] !== '\r' && text[end] !== '\n') {
26
+ end++;
27
+ }
28
+ if (offset + position.character > end) {
29
+ throw new LspError('Edit character is outside the line');
30
+ }
31
+ return offset + position.character;
32
+ }
33
+ export function positionAt(text, offset) {
34
+ if (!Number.isInteger(offset) || offset < 0 || offset > text.length) {
35
+ throw new LspError('Offset is outside the document');
36
+ }
37
+ let line = 0;
38
+ let lineStart = 0;
39
+ let i = 0;
40
+ while (i < offset) {
41
+ const code = text.charCodeAt(i);
42
+ if (code === 13 && text.charCodeAt(i + 1) === 10) {
43
+ if (i + 2 > offset) {
44
+ break;
45
+ }
46
+ i += 2;
47
+ line++;
48
+ lineStart = i;
49
+ }
50
+ else if (code === 13 || code === 10) {
51
+ i++;
52
+ line++;
53
+ lineStart = i;
54
+ }
55
+ else {
56
+ i++;
57
+ }
58
+ }
59
+ return { line, character: offset - lineStart };
60
+ }
61
+ /* The position after the last character. */
62
+ export function endPosition(text) {
63
+ return positionAt(text, text.length);
64
+ }
65
+ /* Edits are simultaneous and in the coordinates of `text`; two inserts at one place keep their order. */
66
+ export function applyTextEdits(text, edits) {
67
+ const spans = edits
68
+ .map((edit, index) => ({
69
+ start: offsetAt(text, edit.range.start),
70
+ end: offsetAt(text, edit.range.end),
71
+ text: edit.newText,
72
+ index
73
+ }))
74
+ .sort((a, b) => a.start - b.start || a.index - b.index);
75
+ let cursor = 0;
76
+ const parts = [];
77
+ for (const span of spans) {
78
+ if (span.end < span.start) {
79
+ throw new LspError('Edit range is reversed');
80
+ }
81
+ if (span.start < cursor) {
82
+ throw new LspError('Workspace edits overlap');
83
+ }
84
+ parts.push(text.slice(cursor, span.start), span.text);
85
+ cursor = span.end;
86
+ }
87
+ parts.push(text.slice(cursor));
88
+ return parts.join('');
89
+ }
90
+ /* `didChange` entries apply one after the other, each in the coordinates the one before left. */
91
+ export function applyContentChanges(text, changes) {
92
+ let result = text;
93
+ for (const change of changes) {
94
+ if (!change.range) {
95
+ result = change.text;
96
+ continue;
97
+ }
98
+ const start = offsetAt(result, change.range.start);
99
+ const end = offsetAt(result, change.range.end);
100
+ if (end < start) {
101
+ throw new LspError('Change range is reversed');
102
+ }
103
+ result = result.slice(0, start) + change.text + result.slice(end);
104
+ }
105
+ return result;
106
+ }
107
+ function isHighSurrogate(code) {
108
+ return code >= 0xd800 && code <= 0xdbff;
109
+ }
110
+ function isLowSurrogate(code) {
111
+ return code >= 0xdc00 && code <= 0xdfff;
112
+ }
113
+ /*
114
+ * The one change that turns `before` into `after`: what is left once the shared start and end are
115
+ * taken off. A range never starts or ends inside a surrogate pair or a CRLF, which no position can
116
+ * name.
117
+ */
118
+ export function minimalChange(before, after) {
119
+ const shortest = Math.min(before.length, after.length);
120
+ let start = 0;
121
+ while (start < shortest && before.charCodeAt(start) === after.charCodeAt(start)) {
122
+ start++;
123
+ }
124
+ if (start > 0 && (isHighSurrogate(before.charCodeAt(start - 1)) || (before[start - 1] === '\r' && before[start] === '\n'))) {
125
+ start--;
126
+ }
127
+ let end = 0;
128
+ while (end < shortest - start && before.charCodeAt(before.length - 1 - end) === after.charCodeAt(after.length - 1 - end)) {
129
+ end++;
130
+ }
131
+ if (end > 0 &&
132
+ (isLowSurrogate(before.charCodeAt(before.length - end)) || (before[before.length - end] === '\n' && before[before.length - end - 1] === '\r'))) {
133
+ end--;
134
+ }
135
+ return {
136
+ range: { start: positionAt(before, start), end: positionAt(before, before.length - end) },
137
+ text: after.slice(start, after.length - end)
138
+ };
139
+ }
140
+ /* Plans text edits in memory. A create, rename or delete needs the host's own handling and is refused. */
141
+ export function planWorkspaceEdit(edit, snapshots) {
142
+ const planned = new Map();
143
+ const changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
144
+ for (const change of changes) {
145
+ if ('kind' in change) {
146
+ throw new LspError(`A host file-operation handler is required for ${change.kind}`);
147
+ }
148
+ const { uri, version } = change.textDocument;
149
+ const snapshot = snapshots.get(uri);
150
+ if (!snapshot) {
151
+ throw new LspError(`Missing workspace snapshot: ${uri}`);
152
+ }
153
+ if (version !== null && snapshot.version !== version) {
154
+ throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
155
+ }
156
+ const before = planned.get(uri)?.text ?? snapshot.text;
157
+ const text = applyTextEdits(before, change.edits);
158
+ planned.set(uri, { uri, version: snapshot.version, before: snapshot.text, text });
159
+ }
160
+ return [...planned.values()];
161
+ }
@@ -0,0 +1,5 @@
1
+ import type { FileOperationFilter, FileRename } from './protocol.ts';
2
+ export interface RenamedFile extends FileRename {
3
+ directory: boolean;
4
+ }
5
+ export declare function renamesTaken(filters: readonly FileOperationFilter[], files: readonly RenamedFile[]): FileRename[];
@@ -0,0 +1,23 @@
1
+ import { globMatch } from './glob.js';
2
+ function filterTakes(filter, file) {
3
+ const { matches, glob, options } = filter.pattern;
4
+ if (matches !== undefined && (matches === 'folder') !== file.directory) {
5
+ return false;
6
+ }
7
+ let url;
8
+ try {
9
+ url = new URL(file.oldUri);
10
+ }
11
+ catch {
12
+ return false;
13
+ }
14
+ if (filter.scheme !== undefined && filter.scheme !== url.protocol.slice(0, -1)) {
15
+ return false;
16
+ }
17
+ const path = decodeURIComponent(url.pathname);
18
+ return options?.ignoreCase === true ? globMatch(path.toLowerCase(), glob.toLowerCase()) : globMatch(path, glob);
19
+ }
20
+ /* The renames a server asked to hear about, by the filters it registered. A rename is matched on the place it leaves. */
21
+ export function renamesTaken(filters, files) {
22
+ return files.filter((file) => filters.some((filter) => filterTakes(filter, file))).map(({ oldUri, newUri }) => ({ oldUri, newUri }));
23
+ }
package/dist/glob.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function globMatch(path: string, pattern: string): boolean;
package/dist/glob.js ADDED
@@ -0,0 +1,67 @@
1
+ const compiled = new Map();
2
+ function escapeRegex(char) {
3
+ return char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
4
+ }
5
+ function compileGlob(pattern) {
6
+ let expression = '';
7
+ let depth = 0;
8
+ for (let i = 0; i < pattern.length; i++) {
9
+ const char = pattern[i];
10
+ if (char === '*') {
11
+ if (pattern[i + 1] === '*') {
12
+ i++;
13
+ if (pattern[i + 1] === '/') {
14
+ expression += '(?:.*/)?';
15
+ i++;
16
+ }
17
+ else {
18
+ expression += '.*';
19
+ }
20
+ }
21
+ else {
22
+ expression += '[^/]*';
23
+ }
24
+ }
25
+ else if (char === '?') {
26
+ expression += '[^/]';
27
+ }
28
+ else if (char === '[') {
29
+ const close = pattern.indexOf(']', i + 2);
30
+ if (close === -1) {
31
+ expression += '\\[';
32
+ }
33
+ else {
34
+ const body = pattern.slice(i + 1, close);
35
+ expression += `[${body.startsWith('!') ? `^${body.slice(1)}` : body}]`;
36
+ i = close;
37
+ }
38
+ }
39
+ else if (char === '{') {
40
+ depth++;
41
+ expression += '(?:';
42
+ }
43
+ else if (char === '}' && depth > 0) {
44
+ depth--;
45
+ expression += ')';
46
+ }
47
+ else if (char === ',' && depth > 0) {
48
+ expression += '|';
49
+ }
50
+ else {
51
+ expression += escapeRegex(char);
52
+ }
53
+ }
54
+ try {
55
+ return new RegExp(`^${expression}$`);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ /* The glob of the protocol: `*`, `**`, `?`, `[a-z]`, `[!a-z]` and `{a,b}`. */
62
+ export function globMatch(path, pattern) {
63
+ if (!compiled.has(pattern)) {
64
+ compiled.set(pattern, compileGlob(pattern));
65
+ }
66
+ return compiled.get(pattern)?.test(path) ?? false;
67
+ }
@@ -0,0 +1,12 @@
1
+ export { ErrorCodes, JsonRpcConnection, LspError, StaleResultError, type RequestHandler } from './connection.ts';
2
+ export { LspDocument } from './document.ts';
3
+ export { applyContentChanges, applyTextEdits, endPosition, minimalChange, offsetAt, planWorkspaceEdit, positionAt, type DocumentSnapshot, type PlannedDocumentEdit } from './edits.ts';
4
+ export { renamesTaken, type RenamedFile } from './file-operations.ts';
5
+ export { watchesFile, type FileChangeType } from './watched-files.ts';
6
+ export { LspSession, type LspSessionOptions, type SessionState } from './session.ts';
7
+ export type { DiagnosticsReport, LanguageDocument, LanguageRequestOptions, LanguageService } from './service.ts';
8
+ export { ContentLengthDecoder, connectWebSocket, createMemoryTransportPair, createStreamTransport, encodeMessage, type ByteStream, type WebSocketTransportOptions } from './transport.ts';
9
+ export { globMatch } from './glob.ts';
10
+ export { fileUriToPath, pathToFileUri } from './uris.ts';
11
+ export { bridgeVueTypeScript, isVueExpression, vueServerOrder, type VueServer } from './vue.ts';
12
+ export type * from './protocol.ts';
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { ErrorCodes, JsonRpcConnection, LspError, StaleResultError } from './connection.js';
2
+ export { LspDocument } from './document.js';
3
+ export { applyContentChanges, applyTextEdits, endPosition, minimalChange, offsetAt, planWorkspaceEdit, positionAt } from './edits.js';
4
+ export { renamesTaken } from './file-operations.js';
5
+ export { watchesFile } from './watched-files.js';
6
+ export { LspSession } from './session.js';
7
+ export { ContentLengthDecoder, connectWebSocket, createMemoryTransportPair, createStreamTransport, encodeMessage } from './transport.js';
8
+ export { globMatch } from './glob.js';
9
+ export { fileUriToPath, pathToFileUri } from './uris.js';
10
+ export { bridgeVueTypeScript, isVueExpression, vueServerOrder } from './vue.js';