@gi-tcg/gts-language-server 0.3.7 → 0.3.8

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/browser.js CHANGED
@@ -2,10 +2,49 @@ import { createConnection, createServer, createTypeScriptProject, loadTsdkByUrl
2
2
  import { GtsVirtualCode, createGtsLanguagePlugin } from "@gi-tcg/gts-language-plugin";
3
3
  import path from "path-browserify-esm";
4
4
  import "@gi-tcg/gts-transpiler";
5
+ import { fs } from "@zenfs/core";
5
6
  import { DiagnosticSeverity } from "@volar/language-server";
6
7
  import { URI } from "vscode-uri";
7
8
  import { create } from "volar-service-typescript";
8
- import { fs } from "@zenfs/core";
9
+ //#region src/zen_fs_provider.ts
10
+ function zenFsProvider(fs) {
11
+ return {
12
+ stat(uri) {
13
+ try {
14
+ const stats = fs.statSync(uri.path);
15
+ console.log("stat", uri.path, stats);
16
+ return {
17
+ type: stats.isFile() ? 1 : stats.isDirectory() ? 2 : stats.isSymbolicLink() ? 64 : 0,
18
+ ctime: stats.ctimeMs,
19
+ mtime: stats.mtimeMs,
20
+ size: stats.size
21
+ };
22
+ } catch {
23
+ return;
24
+ }
25
+ },
26
+ readFile(uri, encoding) {
27
+ try {
28
+ console.log("readFile", uri.path);
29
+ return fs.readFileSync(uri.path, { encoding: encoding ?? "utf-8" });
30
+ } catch {
31
+ return;
32
+ }
33
+ },
34
+ readDirectory(uri) {
35
+ try {
36
+ const files = fs.readdirSync(uri.path, { withFileTypes: true });
37
+ console.log("readDirectory", uri.path, files.map((f) => f.name));
38
+ return files.map((file) => {
39
+ return [file.name, file.isFile() ? 1 : file.isDirectory() ? 2 : file.isSymbolicLink() ? 64 : 0];
40
+ });
41
+ } catch {
42
+ return [];
43
+ }
44
+ }
45
+ };
46
+ }
47
+ //#endregion
9
48
  //#region src/utils.ts
10
49
  /**
11
50
  * Get virtual code from the encoded document URI
@@ -19,7 +58,73 @@ function getVirtualCode(document, context) {
19
58
  return [virtualCode, sourceUri];
20
59
  }
21
60
  //#endregion
22
- //#region src/diagnostics.ts
61
+ //#region src/services/code_lens.ts
62
+ const createCodeLensPlugin = () => {
63
+ return {
64
+ name: "gts-code-lens",
65
+ capabilities: { codeLensProvider: {} },
66
+ create: (context) => {
67
+ return { provideCodeLenses: async (document, token) => {
68
+ const [virtualCode] = getVirtualCode(document, context);
69
+ if (!virtualCode) return [];
70
+ const result = [];
71
+ for (const mapping of virtualCode.mappings) if (mapping.data.directActionStub) {
72
+ const startOffset = mapping.generatedOffsets[0];
73
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
74
+ const start = document.positionAt(startOffset);
75
+ const end = document.positionAt(startOffset + length);
76
+ result.push({
77
+ range: {
78
+ start,
79
+ end
80
+ },
81
+ command: {
82
+ title: "~action",
83
+ command: ""
84
+ }
85
+ });
86
+ }
87
+ return result;
88
+ } };
89
+ }
90
+ };
91
+ };
92
+ //#endregion
93
+ //#region src/services/completion.ts
94
+ const createCompletionPlugin = () => {
95
+ return {
96
+ name: "gts-completion",
97
+ capabilities: { completionProvider: { triggerCharacters: [":"] } },
98
+ create: (context) => {
99
+ let tsProvideCompletionItems;
100
+ for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideCompletionItems) {
101
+ let originalProvideCompletionItems = instance.provideCompletionItems;
102
+ tsProvideCompletionItems = instance.provideCompletionItems = async (...args) => {
103
+ const response = await originalProvideCompletionItems.apply(instance, args);
104
+ if (!response) return response;
105
+ const items = response.items.filter((item) => !item.label.startsWith("__gts_"));
106
+ return {
107
+ ...response,
108
+ items
109
+ };
110
+ };
111
+ }
112
+ if (!tsProvideCompletionItems) {
113
+ console.warn(`TS's original provideCompletionItems not found`);
114
+ return {};
115
+ }
116
+ return { provideCompletionItems: async (document, position, context, token) => {
117
+ if (context.triggerCharacter === ":") {
118
+ context.triggerCharacter = ".";
119
+ return await tsProvideCompletionItems(document, position, context, token);
120
+ }
121
+ return null;
122
+ } };
123
+ }
124
+ };
125
+ };
126
+ //#endregion
127
+ //#region src/services/diagnostics.ts
23
128
  const createDiagnosticsPlugin = () => {
24
129
  return {
25
130
  name: "gts-diagnostics",
@@ -70,85 +175,97 @@ const createDiagnosticsPlugin = () => {
70
175
  };
71
176
  };
72
177
  //#endregion
73
- //#region src/typescript.ts
74
- function createTypeScriptServices(ts) {
75
- const services = create(ts);
76
- const semanticService = services.find((service) => service.name === "typescript-semantic");
77
- if (semanticService?.capabilities.signatureHelpProvider?.triggerCharacters) semanticService.capabilities.signatureHelpProvider.triggerCharacters.push(" ");
78
- return services;
79
- }
80
- //#endregion
81
- //#region src/completion.ts
82
- const createCompletionPlugin = () => {
178
+ //#region src/services/semantic_tokens.ts
179
+ const createSemanticTokensPlugin = () => {
83
180
  return {
84
- name: "gts-completion",
85
- capabilities: { completionProvider: { triggerCharacters: [":"] } },
181
+ name: "gts-semantic-tokens",
182
+ capabilities: { semanticTokensProvider: { legend: {
183
+ tokenTypes: ["string"],
184
+ tokenModifiers: ["gtsAttribute"]
185
+ } } },
86
186
  create: (context) => {
87
- let tsProvideCompletionItems;
88
- for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideCompletionItems) {
89
- let originalProvideCompletionItems = instance.provideCompletionItems;
90
- tsProvideCompletionItems = instance.provideCompletionItems = async (...args) => {
91
- const response = await originalProvideCompletionItems.apply(instance, args);
187
+ let tsSemanticTokens;
188
+ for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideDocumentSemanticTokens) {
189
+ let originalProvideDocumentSemanticTokens = instance.provideDocumentSemanticTokens;
190
+ tsSemanticTokens = instance.provideDocumentSemanticTokens = async (document, range, legend, token) => {
191
+ const response = await originalProvideDocumentSemanticTokens.call(instance, document, range, legend, token);
92
192
  if (!response) return response;
93
- const items = response.items.filter((item) => !item.label.startsWith("__gts_"));
94
- return {
95
- ...response,
96
- items
97
- };
193
+ const gtsAttributeIndex = legend.tokenModifiers.indexOf("gtsAttribute");
194
+ if (gtsAttributeIndex === -1) return response;
195
+ const [virtualCode] = getVirtualCode(document, context);
196
+ if (!virtualCode) return response;
197
+ for (let i = 0; i < response.length; i++) {
198
+ const [line, character, length, tokenType, tokenModifiers] = response[i];
199
+ if (tokenType !== legend.tokenTypes.indexOf("method")) continue;
200
+ const offset = document.offsetAt({
201
+ line,
202
+ character
203
+ });
204
+ if (virtualCode.mappings.find((m) => m.generatedOffsets[0] === offset && m.data.gtsAttribute)) response[i][4] |= 1 << gtsAttributeIndex;
205
+ }
206
+ return response;
98
207
  };
99
208
  }
100
- if (!tsProvideCompletionItems) {
101
- console.warn(`TS's original provideCompletionItems not found`);
102
- return {};
103
- }
104
- return { provideCompletionItems: async (document, position, context, token) => {
105
- if (context.triggerCharacter === ":") {
106
- context.triggerCharacter = ".";
107
- return await tsProvideCompletionItems(document, position, context, token);
209
+ if (!tsSemanticTokens) console.warn(`TS's original provideDocumentSemanticTokens not found`);
210
+ return { provideDocumentSemanticTokens(document, range, legend, token) {
211
+ const [virtualCode] = getVirtualCode(document, context);
212
+ if (!virtualCode) return null;
213
+ const stringIdx = legend.tokenTypes.indexOf("string");
214
+ if (stringIdx === -1) return null;
215
+ const gtsAttributeIndex = legend.tokenModifiers.indexOf("gtsAttribute");
216
+ const result = [];
217
+ const text = document.getText();
218
+ for (const mapping of virtualCode.mappings) {
219
+ if (mapping.data.literalFromId) {
220
+ const offset = mapping.generatedOffsets[0];
221
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
222
+ const pos = document.positionAt(offset);
223
+ result.push([
224
+ pos.line,
225
+ pos.character,
226
+ length,
227
+ stringIdx,
228
+ 0
229
+ ]);
230
+ }
231
+ if (gtsAttributeIndex >= 0 && mapping.data.gtsAttribute) {
232
+ const offset = mapping.generatedOffsets[0];
233
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
234
+ const pos = document.positionAt(offset);
235
+ const char = text[offset];
236
+ if (char === "\"" || char === "'") result.push([
237
+ pos.line,
238
+ pos.character,
239
+ length,
240
+ stringIdx,
241
+ 1 << gtsAttributeIndex
242
+ ]);
243
+ }
108
244
  }
109
- return null;
245
+ return result;
110
246
  } };
111
247
  }
112
248
  };
113
249
  };
114
250
  //#endregion
115
- //#region src/zen_fs_provider.ts
116
- function zenFsProvider(fs) {
117
- return {
118
- stat(uri) {
119
- try {
120
- const stats = fs.statSync(uri.path);
121
- console.log("stat", uri.path, stats);
122
- return {
123
- type: stats.isFile() ? 1 : stats.isDirectory() ? 2 : stats.isSymbolicLink() ? 64 : 0,
124
- ctime: stats.ctimeMs,
125
- mtime: stats.mtimeMs,
126
- size: stats.size
127
- };
128
- } catch {
129
- return;
130
- }
131
- },
132
- readFile(uri, encoding) {
133
- try {
134
- console.log("readFile", uri.path);
135
- return fs.readFileSync(uri.path, { encoding: encoding ?? "utf-8" });
136
- } catch {
137
- return;
138
- }
139
- },
140
- readDirectory(uri) {
141
- try {
142
- const files = fs.readdirSync(uri.path, { withFileTypes: true });
143
- console.log("readDirectory", uri.path, files.map((f) => f.name));
144
- return files.map((file) => {
145
- return [file.name, file.isFile() ? 1 : file.isDirectory() ? 2 : file.isSymbolicLink() ? 64 : 0];
146
- });
147
- } catch {
148
- return [];
149
- }
150
- }
151
- };
251
+ //#region src/services/typescript.ts
252
+ function createTypeScriptServices(ts) {
253
+ const services = create(ts);
254
+ const semanticService = services.find((service) => service.name === "typescript-semantic");
255
+ if (semanticService?.capabilities.signatureHelpProvider?.triggerCharacters) semanticService.capabilities.signatureHelpProvider.triggerCharacters.push(" ");
256
+ if (semanticService?.capabilities.semanticTokensProvider?.legend) semanticService.capabilities.semanticTokensProvider.legend.tokenModifiers.push("gtsAttribute");
257
+ return services;
258
+ }
259
+ //#endregion
260
+ //#region src/services/index.ts
261
+ function createLanguageServicePlugins(ts) {
262
+ return [
263
+ ...createTypeScriptServices(ts),
264
+ createDiagnosticsPlugin(),
265
+ createCompletionPlugin(),
266
+ createSemanticTokensPlugin(),
267
+ createCodeLensPlugin()
268
+ ];
152
269
  }
153
270
  //#endregion
154
271
  //#region src/browser.ts
@@ -166,11 +283,7 @@ connection.onInitialize(async (params) => {
166
283
  }
167
284
  return server.initialize(params, createTypeScriptProject(tsdk.typescript, tsdk.diagnosticMessages, ({ env }) => {
168
285
  return { languagePlugins: [createGtsLanguagePlugin(tsdk.typescript, inlineGtsConfig)] };
169
- }), [
170
- ...createTypeScriptServices(tsdk.typescript),
171
- createDiagnosticsPlugin(),
172
- createCompletionPlugin()
173
- ]);
286
+ }), createLanguageServicePlugins(tsdk.typescript));
174
287
  });
175
288
  connection.onInitialized(server.initialized);
176
289
  connection.onShutdown(server.shutdown);
package/dist/node.js CHANGED
@@ -16,7 +16,73 @@ function getVirtualCode(document, context) {
16
16
  return [virtualCode, sourceUri];
17
17
  }
18
18
  //#endregion
19
- //#region src/diagnostics.ts
19
+ //#region src/services/code_lens.ts
20
+ const createCodeLensPlugin = () => {
21
+ return {
22
+ name: "gts-code-lens",
23
+ capabilities: { codeLensProvider: {} },
24
+ create: (context) => {
25
+ return { provideCodeLenses: async (document, token) => {
26
+ const [virtualCode] = getVirtualCode(document, context);
27
+ if (!virtualCode) return [];
28
+ const result = [];
29
+ for (const mapping of virtualCode.mappings) if (mapping.data.directActionStub) {
30
+ const startOffset = mapping.generatedOffsets[0];
31
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
32
+ const start = document.positionAt(startOffset);
33
+ const end = document.positionAt(startOffset + length);
34
+ result.push({
35
+ range: {
36
+ start,
37
+ end
38
+ },
39
+ command: {
40
+ title: "~action",
41
+ command: ""
42
+ }
43
+ });
44
+ }
45
+ return result;
46
+ } };
47
+ }
48
+ };
49
+ };
50
+ //#endregion
51
+ //#region src/services/completion.ts
52
+ const createCompletionPlugin = () => {
53
+ return {
54
+ name: "gts-completion",
55
+ capabilities: { completionProvider: { triggerCharacters: [":"] } },
56
+ create: (context) => {
57
+ let tsProvideCompletionItems;
58
+ for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideCompletionItems) {
59
+ let originalProvideCompletionItems = instance.provideCompletionItems;
60
+ tsProvideCompletionItems = instance.provideCompletionItems = async (...args) => {
61
+ const response = await originalProvideCompletionItems.apply(instance, args);
62
+ if (!response) return response;
63
+ const items = response.items.filter((item) => !item.label.startsWith("__gts_"));
64
+ return {
65
+ ...response,
66
+ items
67
+ };
68
+ };
69
+ }
70
+ if (!tsProvideCompletionItems) {
71
+ console.warn(`TS's original provideCompletionItems not found`);
72
+ return {};
73
+ }
74
+ return { provideCompletionItems: async (document, position, context, token) => {
75
+ if (context.triggerCharacter === ":") {
76
+ context.triggerCharacter = ".";
77
+ return await tsProvideCompletionItems(document, position, context, token);
78
+ }
79
+ return null;
80
+ } };
81
+ }
82
+ };
83
+ };
84
+ //#endregion
85
+ //#region src/services/diagnostics.ts
20
86
  const createDiagnosticsPlugin = () => {
21
87
  return {
22
88
  name: "gts-diagnostics",
@@ -67,48 +133,99 @@ const createDiagnosticsPlugin = () => {
67
133
  };
68
134
  };
69
135
  //#endregion
70
- //#region src/typescript.ts
71
- function createTypeScriptServices(ts) {
72
- const services = create(ts);
73
- const semanticService = services.find((service) => service.name === "typescript-semantic");
74
- if (semanticService?.capabilities.signatureHelpProvider?.triggerCharacters) semanticService.capabilities.signatureHelpProvider.triggerCharacters.push(" ");
75
- return services;
76
- }
77
- //#endregion
78
- //#region src/completion.ts
79
- const createCompletionPlugin = () => {
136
+ //#region src/services/semantic_tokens.ts
137
+ const createSemanticTokensPlugin = () => {
80
138
  return {
81
- name: "gts-completion",
82
- capabilities: { completionProvider: { triggerCharacters: [":"] } },
139
+ name: "gts-semantic-tokens",
140
+ capabilities: { semanticTokensProvider: { legend: {
141
+ tokenTypes: ["string"],
142
+ tokenModifiers: ["gtsAttribute"]
143
+ } } },
83
144
  create: (context) => {
84
- let tsProvideCompletionItems;
85
- for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideCompletionItems) {
86
- let originalProvideCompletionItems = instance.provideCompletionItems;
87
- tsProvideCompletionItems = instance.provideCompletionItems = async (...args) => {
88
- const response = await originalProvideCompletionItems.apply(instance, args);
145
+ let tsSemanticTokens;
146
+ for (const [plugin, instance] of context.plugins) if (plugin.name === "typescript-semantic" && instance.provideDocumentSemanticTokens) {
147
+ let originalProvideDocumentSemanticTokens = instance.provideDocumentSemanticTokens;
148
+ tsSemanticTokens = instance.provideDocumentSemanticTokens = async (document, range, legend, token) => {
149
+ const response = await originalProvideDocumentSemanticTokens.call(instance, document, range, legend, token);
89
150
  if (!response) return response;
90
- const items = response.items.filter((item) => !item.label.startsWith("__gts_"));
91
- return {
92
- ...response,
93
- items
94
- };
151
+ const gtsAttributeIndex = legend.tokenModifiers.indexOf("gtsAttribute");
152
+ if (gtsAttributeIndex === -1) return response;
153
+ const [virtualCode] = getVirtualCode(document, context);
154
+ if (!virtualCode) return response;
155
+ for (let i = 0; i < response.length; i++) {
156
+ const [line, character, length, tokenType, tokenModifiers] = response[i];
157
+ if (tokenType !== legend.tokenTypes.indexOf("method")) continue;
158
+ const offset = document.offsetAt({
159
+ line,
160
+ character
161
+ });
162
+ if (virtualCode.mappings.find((m) => m.generatedOffsets[0] === offset && m.data.gtsAttribute)) response[i][4] |= 1 << gtsAttributeIndex;
163
+ }
164
+ return response;
95
165
  };
96
166
  }
97
- if (!tsProvideCompletionItems) {
98
- console.warn(`TS's original provideCompletionItems not found`);
99
- return {};
100
- }
101
- return { provideCompletionItems: async (document, position, context, token) => {
102
- if (context.triggerCharacter === ":") {
103
- context.triggerCharacter = ".";
104
- return await tsProvideCompletionItems(document, position, context, token);
167
+ if (!tsSemanticTokens) console.warn(`TS's original provideDocumentSemanticTokens not found`);
168
+ return { provideDocumentSemanticTokens(document, range, legend, token) {
169
+ const [virtualCode] = getVirtualCode(document, context);
170
+ if (!virtualCode) return null;
171
+ const stringIdx = legend.tokenTypes.indexOf("string");
172
+ if (stringIdx === -1) return null;
173
+ const gtsAttributeIndex = legend.tokenModifiers.indexOf("gtsAttribute");
174
+ const result = [];
175
+ const text = document.getText();
176
+ for (const mapping of virtualCode.mappings) {
177
+ if (mapping.data.literalFromId) {
178
+ const offset = mapping.generatedOffsets[0];
179
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
180
+ const pos = document.positionAt(offset);
181
+ result.push([
182
+ pos.line,
183
+ pos.character,
184
+ length,
185
+ stringIdx,
186
+ 0
187
+ ]);
188
+ }
189
+ if (gtsAttributeIndex >= 0 && mapping.data.gtsAttribute) {
190
+ const offset = mapping.generatedOffsets[0];
191
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
192
+ const pos = document.positionAt(offset);
193
+ const char = text[offset];
194
+ if (char === "\"" || char === "'") result.push([
195
+ pos.line,
196
+ pos.character,
197
+ length,
198
+ stringIdx,
199
+ 1 << gtsAttributeIndex
200
+ ]);
201
+ }
105
202
  }
106
- return null;
203
+ return result;
107
204
  } };
108
205
  }
109
206
  };
110
207
  };
111
208
  //#endregion
209
+ //#region src/services/typescript.ts
210
+ function createTypeScriptServices(ts) {
211
+ const services = create(ts);
212
+ const semanticService = services.find((service) => service.name === "typescript-semantic");
213
+ if (semanticService?.capabilities.signatureHelpProvider?.triggerCharacters) semanticService.capabilities.signatureHelpProvider.triggerCharacters.push(" ");
214
+ if (semanticService?.capabilities.semanticTokensProvider?.legend) semanticService.capabilities.semanticTokensProvider.legend.tokenModifiers.push("gtsAttribute");
215
+ return services;
216
+ }
217
+ //#endregion
218
+ //#region src/services/index.ts
219
+ function createLanguageServicePlugins(ts) {
220
+ return [
221
+ ...createTypeScriptServices(ts),
222
+ createDiagnosticsPlugin(),
223
+ createCompletionPlugin(),
224
+ createSemanticTokensPlugin(),
225
+ createCodeLensPlugin()
226
+ ];
227
+ }
228
+ //#endregion
112
229
  //#region src/node.ts
113
230
  const connection = createConnection();
114
231
  const server = createServer(connection);
@@ -117,11 +234,7 @@ connection.onInitialize((params) => {
117
234
  const tsdk = loadTsdkByPath(params.initializationOptions.typescript.tsdk, params.locale);
118
235
  return server.initialize(params, createTypeScriptProject(tsdk.typescript, tsdk.diagnosticMessages, () => {
119
236
  return { languagePlugins: [createGtsLanguagePlugin(tsdk.typescript)] };
120
- }), [
121
- ...createTypeScriptServices(tsdk.typescript),
122
- createDiagnosticsPlugin(),
123
- createCompletionPlugin()
124
- ]);
237
+ }), createLanguageServicePlugins(tsdk.typescript));
125
238
  });
126
239
  connection.onInitialized(server.initialized);
127
240
  connection.onShutdown(server.shutdown);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gi-tcg/gts-language-server",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/piovium/gts.git"
@@ -31,8 +31,8 @@
31
31
  "path-browserify-esm": "^1.0.6",
32
32
  "volar-service-typescript": "volar-2.4",
33
33
  "vscode-uri": "^3.0.8",
34
- "@gi-tcg/gts-transpiler": "0.3.7",
35
- "@gi-tcg/gts-language-plugin": "0.3.7"
34
+ "@gi-tcg/gts-language-plugin": "0.3.8",
35
+ "@gi-tcg/gts-transpiler": "0.3.8"
36
36
  },
37
37
  "devDependencies": {
38
38
  "vscode-languageserver-textdocument": "^1.0.12"
package/src/browser.ts CHANGED
@@ -11,12 +11,10 @@ import {
11
11
  import { createGtsLanguagePlugin } from "@gi-tcg/gts-language-plugin";
12
12
  import path from "path-browserify-esm";
13
13
  import { type GtsConfig } from "@gi-tcg/gts-transpiler";
14
- import { createDiagnosticsPlugin } from "./diagnostics.ts";
15
- import { createTypeScriptServices } from "./typescript.ts";
16
- import { createCompletionPlugin } from "./completion.ts";
17
- import { Dirent, fs as memfs } from "@zenfs/core";
14
+ import { fs as memfs } from "@zenfs/core";
18
15
  import type ts from "typescript";
19
16
  import zenFsProvider from "./zen_fs_provider.ts";
17
+ import { createLanguageServicePlugins } from "./services/index.ts";
20
18
 
21
19
  export interface GtsLanguageServerBrowserInitializationOptions {
22
20
  tsdkUrl?: string;
@@ -65,11 +63,7 @@ connection.onInitialize(
65
63
  };
66
64
  },
67
65
  ),
68
- [
69
- ...createTypeScriptServices(tsdk.typescript),
70
- createDiagnosticsPlugin(),
71
- createCompletionPlugin(),
72
- ],
66
+ createLanguageServicePlugins(tsdk.typescript),
73
67
  );
74
68
  },
75
69
  );
@@ -95,7 +89,9 @@ async function loadLibs(tsdkUrl: string) {
95
89
  memfs.mkdirSync("/node_modules/typescript/lib", { recursive: true });
96
90
  const libs = await Promise.all(
97
91
  ALL_LIBS.map((lib) =>
98
- fetch(`${tsdkUrl}/${lib}`).then((res) => res.text()).then((content) => [lib, content] as const),
92
+ fetch(`${tsdkUrl}/${lib}`)
93
+ .then((res) => res.text())
94
+ .then((content) => [lib, content] as const),
99
95
  ),
100
96
  );
101
97
  for (const [lib, content] of libs) {
package/src/node.ts CHANGED
@@ -6,9 +6,7 @@ import {
6
6
  loadTsdkByPath,
7
7
  } from "@volar/language-server/node.js";
8
8
  import { createGtsLanguagePlugin } from "@gi-tcg/gts-language-plugin";
9
- import { createDiagnosticsPlugin } from "./diagnostics.ts";
10
- import { createTypeScriptServices } from "./typescript.ts";
11
- import { createCompletionPlugin } from "./completion.ts";
9
+ import { createLanguageServicePlugins } from "./services/index.ts";
12
10
 
13
11
  const connection = createConnection();
14
12
  const server = createServer(connection);
@@ -27,11 +25,7 @@ connection.onInitialize((params) => {
27
25
  languagePlugins: [createGtsLanguagePlugin(tsdk.typescript)],
28
26
  };
29
27
  }),
30
- [
31
- ...createTypeScriptServices(tsdk.typescript),
32
- createDiagnosticsPlugin(),
33
- createCompletionPlugin(),
34
- ],
28
+ createLanguageServicePlugins(tsdk.typescript),
35
29
  );
36
30
  });
37
31
 
@@ -0,0 +1,39 @@
1
+ import { CodeLens, type LanguageServicePlugin } from "@volar/language-server";
2
+ import { getVirtualCode } from "../utils.ts";
3
+
4
+ export const createCodeLensPlugin = (): LanguageServicePlugin => {
5
+ return {
6
+ name: "gts-code-lens",
7
+ capabilities: {
8
+ codeLensProvider: {},
9
+ },
10
+ create: (context) => {
11
+ return {
12
+ provideCodeLenses: async (document, token) => {
13
+ const [virtualCode] = getVirtualCode(document, context);
14
+ if (!virtualCode) {
15
+ return [];
16
+ }
17
+ const result: CodeLens[] = [];
18
+ for (const mapping of virtualCode.mappings) {
19
+ if (mapping.data.directActionStub) {
20
+ const startOffset = mapping.generatedOffsets[0];
21
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
22
+ const start = document.positionAt(startOffset);
23
+ // two character: "0;"
24
+ const end = document.positionAt(startOffset + length);
25
+ result.push({
26
+ range: { start, end },
27
+ command: {
28
+ title: "~action",
29
+ command: "",
30
+ },
31
+ });
32
+ }
33
+ }
34
+ return result;
35
+ },
36
+ };
37
+ },
38
+ };
39
+ };
@@ -3,7 +3,7 @@ import {
3
3
  Range,
4
4
  type LanguageServicePlugin,
5
5
  } from "@volar/language-server";
6
- import { getVirtualCode } from "./utils.ts";
6
+ import { getVirtualCode } from "../utils.ts";
7
7
  export const createDiagnosticsPlugin = (): LanguageServicePlugin => {
8
8
  return {
9
9
  name: "gts-diagnostics",
@@ -0,0 +1,18 @@
1
+ import { createCodeLensPlugin } from "./code_lens.ts";
2
+ import { createCompletionPlugin } from "./completion.ts";
3
+ import { createDiagnosticsPlugin } from "./diagnostics.ts";
4
+ import { createSemanticTokensPlugin } from "./semantic_tokens.ts";
5
+ import { createTypeScriptServices } from "./typescript.ts";
6
+ import type { LanguageServicePlugin } from "@volar/language-server";
7
+
8
+ export function createLanguageServicePlugins(
9
+ ts: typeof import("typescript"),
10
+ ): LanguageServicePlugin[] {
11
+ return [
12
+ ...createTypeScriptServices(ts),
13
+ createDiagnosticsPlugin(),
14
+ createCompletionPlugin(),
15
+ createSemanticTokensPlugin(),
16
+ createCodeLensPlugin(),
17
+ ];
18
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ type LanguageServicePlugin,
3
+ type LanguageServicePluginInstance,
4
+ type SemanticToken,
5
+ } from "@volar/language-server";
6
+ import { getVirtualCode } from "../utils.ts";
7
+
8
+ export const createSemanticTokensPlugin = (): LanguageServicePlugin => {
9
+ return {
10
+ name: "gts-semantic-tokens",
11
+ capabilities: {
12
+ semanticTokensProvider: {
13
+ legend: {
14
+ tokenTypes: ["string"],
15
+ tokenModifiers: ["gtsAttribute"],
16
+ },
17
+ },
18
+ },
19
+ create: (context) => {
20
+ let tsSemanticTokens: LanguageServicePluginInstance["provideDocumentSemanticTokens"];
21
+
22
+ for (const [plugin, instance] of context.plugins) {
23
+ if (
24
+ plugin.name === "typescript-semantic" &&
25
+ instance.provideDocumentSemanticTokens
26
+ ) {
27
+ let originalProvideDocumentSemanticTokens =
28
+ instance.provideDocumentSemanticTokens;
29
+ tsSemanticTokens = instance.provideDocumentSemanticTokens = async (
30
+ document,
31
+ range,
32
+ legend,
33
+ token,
34
+ ) => {
35
+ const response = await originalProvideDocumentSemanticTokens.call(
36
+ instance,
37
+ document,
38
+ range,
39
+ legend,
40
+ token,
41
+ );
42
+ if (!response) {
43
+ return response;
44
+ }
45
+ const gtsAttributeIndex =
46
+ legend.tokenModifiers.indexOf("gtsAttribute");
47
+ if (gtsAttributeIndex === -1) {
48
+ return response;
49
+ }
50
+ const [virtualCode] = getVirtualCode(document, context);
51
+ if (!virtualCode) {
52
+ return response;
53
+ }
54
+ for (let i = 0; i < response.length; i++) {
55
+ const [line, character, length, tokenType, tokenModifiers] =
56
+ response[i];
57
+ if (tokenType !== legend.tokenTypes.indexOf("method")) {
58
+ continue;
59
+ }
60
+ const offset = document.offsetAt({ line, character });
61
+ const mapping = virtualCode.mappings.find(
62
+ (m) => m.generatedOffsets[0] === offset && m.data.gtsAttribute,
63
+ );
64
+ if (mapping) {
65
+ response[i][4] |= 1 << gtsAttributeIndex;
66
+ }
67
+ }
68
+ return response;
69
+ };
70
+ }
71
+ }
72
+ if (!tsSemanticTokens) {
73
+ console.warn(`TS's original provideDocumentSemanticTokens not found`);
74
+ }
75
+ return {
76
+ // Attribute value that is a string literal but no wrapping quotation with it,
77
+ // add a semantic token of type "string" to them so they show same color as string literal
78
+ provideDocumentSemanticTokens(document, range, legend, token) {
79
+ const [virtualCode] = getVirtualCode(document, context);
80
+ if (!virtualCode) {
81
+ return null;
82
+ }
83
+ const stringIdx = legend.tokenTypes.indexOf("string");
84
+ if (stringIdx === -1) {
85
+ return null;
86
+ }
87
+ const gtsAttributeIndex =
88
+ legend.tokenModifiers.indexOf("gtsAttribute");
89
+ const result: SemanticToken[] = [];
90
+ const text = document.getText();
91
+ for (const mapping of virtualCode.mappings) {
92
+ if (mapping.data.literalFromId) {
93
+ const offset = mapping.generatedOffsets[0];
94
+ const length = mapping.generatedLengths?.[0] ?? mapping.lengths[0];
95
+ const pos = document.positionAt(offset);
96
+ result.push([pos.line, pos.character, length, stringIdx, 0]);
97
+ }
98
+ if (gtsAttributeIndex >= 0 && mapping.data.gtsAttribute) {
99
+ const offset = mapping.generatedOffsets[0];
100
+ const length = mapping.generatedLengths?.[0] ??mapping.lengths[0];
101
+ const pos = document.positionAt(offset);
102
+ const char = text[offset];
103
+ if (char === '"' || char === "'") {
104
+ // A GTS attribute name with quotation, that will not have TS semantic token ("member")
105
+ // we add it as "string" with "gtsAttribute" modifier too.
106
+ result.push([
107
+ pos.line,
108
+ pos.character,
109
+ length,
110
+ stringIdx,
111
+ 1 << gtsAttributeIndex,
112
+ ]);
113
+ }
114
+ }
115
+ }
116
+ return result;
117
+ },
118
+ };
119
+ },
120
+ };
121
+ };
@@ -16,5 +16,10 @@ export function createTypeScriptServices(
16
16
  " ",
17
17
  );
18
18
  }
19
+ if (semanticService?.capabilities.semanticTokensProvider?.legend) {
20
+ semanticService.capabilities.semanticTokensProvider.legend.tokenModifiers.push(
21
+ "gtsAttribute",
22
+ );
23
+ }
19
24
  return services;
20
25
  }
@@ -1,114 +0,0 @@
1
- import type {
2
- LanguageServicePluginInstance,
3
- LanguageServicePlugin,
4
- } from "@volar/language-server";
5
- import { getVirtualCode, getWordFromPosition } from "./utils.ts";
6
-
7
- export function createDocumentHighlightService(): LanguageServicePlugin {
8
- return {
9
- name: "gts-document-highlight",
10
- capabilities: {
11
- documentHighlightProvider: true,
12
- },
13
- create(context) {
14
- let originalProvideDocumentHighlights: LanguageServicePluginInstance["provideDocumentHighlights"];
15
- let originalInstance: LanguageServicePluginInstance;
16
-
17
- // Get TypeScript's document highlights provider
18
- for (const [plugin, instance] of context.plugins) {
19
- if (
20
- plugin.name === "typescript-semantic" &&
21
- instance.provideDocumentHighlights
22
- ) {
23
- originalInstance = instance;
24
- originalProvideDocumentHighlights =
25
- instance.provideDocumentHighlights;
26
- instance.provideDocumentHighlights = undefined;
27
- break;
28
- }
29
- }
30
-
31
- if (!originalProvideDocumentHighlights) {
32
- console?.log(
33
- "'typescript-semantic plugin' was not found or has no 'provideDocumentHighlights'. \
34
- Document highlights will be limited to custom GamingTS keywords only."
35
- );
36
- }
37
-
38
- return {
39
- async provideDocumentHighlights(document, position, token) {
40
- if (!originalProvideDocumentHighlights) {
41
- return null;
42
- }
43
-
44
- let tsHighlights = await originalProvideDocumentHighlights.call(
45
- originalInstance,
46
- document,
47
- position,
48
- token
49
- );
50
-
51
- if (!tsHighlights || tsHighlights.length > 0) {
52
- // If TypeScript recognized tokens and provided highlights, return them
53
- return tsHighlights;
54
- }
55
-
56
- const [virtualCode] = getVirtualCode(document, context);
57
-
58
- if (!virtualCode) {
59
- return tsHighlights;
60
- }
61
-
62
- // Check if we're on a custom Ripple keyword
63
- const offset = document.offsetAt(position);
64
- const text = document.getText();
65
-
66
- // Find word boundaries
67
- const { word } = getWordFromPosition(text, offset);
68
-
69
- // If the word is a Ripple keyword, find all occurrences in the document
70
-
71
- const regex = new RegExp(`\\b${word}\\b`, "g");
72
- let match;
73
-
74
- // while ((match = regex.exec(text)) !== null) {
75
- // const start = match.index;
76
- // const end = match.index + word.length;
77
- // const mapping = virtualCode.findMappingByGeneratedRange(start, end);
78
-
79
- // if (!mapping) {
80
- // // If no mapping, skip all others as well
81
- // // This shouldn't happen as TS handles only mapped ranges
82
- // return tsHighlights;
83
- // }
84
-
85
- // if (!mapping.data.customData?.wordHighlight?.kind) {
86
- // // Skip if we didn't define word highlighting in segments
87
- // continue;
88
- // }
89
-
90
- // if (!tsHighlights) {
91
- // tsHighlights = [];
92
- // }
93
-
94
- // tsHighlights.push({
95
- // range: {
96
- // start: document.positionAt(start),
97
- // end: document.positionAt(end),
98
- // },
99
-
100
- // kind: mapping.data.customData.wordHighlight.kind,
101
- // });
102
- // }
103
-
104
- if (tsHighlights.length > 0) {
105
- console?.log(`Found ${tsHighlights.length} occurrences of '${word}'`);
106
- }
107
-
108
- // Return TypeScript highlights if no custom keyword was found
109
- return [...tsHighlights];
110
- },
111
- };
112
- },
113
- };
114
- }
File without changes