@gi-tcg/gts-language-server 0.2.0

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.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ if (process.argv.includes("--version")) {
3
+ const pkgJSON = await import("../package.json");
4
+ console.log(`${pkgJSON["version"]}`);
5
+ } else {
6
+ await import("../dist/index.js");
7
+ }
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ // packages/language-server/src/index.ts
2
+ import {
3
+ createConnection,
4
+ createServer,
5
+ createTypeScriptProject,
6
+ loadTsdkByPath
7
+ } from "@volar/language-server/node.js";
8
+ import { create as createTypeScriptServices } from "volar-service-typescript";
9
+ import { createGtsLanguagePlugin } from "@gi-tcg/gts-language-plugin";
10
+
11
+ // packages/language-server/src/diagnostics.ts
12
+ import {
13
+ DiagnosticSeverity
14
+ } from "@volar/language-server";
15
+
16
+ // packages/language-server/src/utils.ts
17
+ import { URI } from "vscode-uri";
18
+ import { GtsVirtualCode } from "@gi-tcg/gts-language-plugin";
19
+ function getVirtualCode(document, context) {
20
+ const uri = URI.parse(document.uri);
21
+ const decoded = context.decodeEmbeddedDocumentUri(uri);
22
+ const [sourceUri, virtualCodeId] = decoded;
23
+ const sourceScript = context.language.scripts.get(sourceUri);
24
+ const virtualCode = sourceScript?.generated?.embeddedCodes.get(virtualCodeId);
25
+ if (!virtualCode) {
26
+ return [null, sourceUri];
27
+ }
28
+ if (!(virtualCode instanceof GtsVirtualCode)) {
29
+ return [null, sourceUri];
30
+ }
31
+ return [virtualCode, sourceUri];
32
+ }
33
+
34
+ // packages/language-server/src/diagnostics.ts
35
+ var createDiagnosticsPlugin = () => {
36
+ return {
37
+ name: "gts-diagnostics",
38
+ capabilities: {
39
+ diagnosticProvider: {
40
+ interFileDependencies: false,
41
+ workspaceDiagnostics: false
42
+ }
43
+ },
44
+ create: (context) => {
45
+ return {
46
+ provideDiagnostics: (document) => {
47
+ try {
48
+ console.log(document.uri);
49
+ const [virtualCode] = getVirtualCode(document, context);
50
+ console.log(document.uri, virtualCode);
51
+ if (!virtualCode) {
52
+ return;
53
+ }
54
+ return virtualCode.errors.map((err) => {
55
+ const loc = err.position ?? {
56
+ start: { line: 1, column: 0 },
57
+ end: { line: 1, column: 1 }
58
+ };
59
+ const range = {
60
+ start: {
61
+ line: loc.start.line - 1,
62
+ character: loc.start.column
63
+ },
64
+ end: { line: loc.end.line - 1, character: loc.end.column }
65
+ };
66
+ console.log(range);
67
+ return {
68
+ severity: DiagnosticSeverity.Error,
69
+ range,
70
+ message: err.message,
71
+ source: "gts-transpiler",
72
+ code: "gts-transpiler-error"
73
+ };
74
+ });
75
+ } catch (e) {
76
+ console.error(e);
77
+ return;
78
+ }
79
+ }
80
+ };
81
+ }
82
+ };
83
+ };
84
+
85
+ // packages/language-server/src/index.ts
86
+ var connection = createConnection();
87
+ var server = createServer(connection);
88
+ connection.listen();
89
+ connection.onInitialize((params) => {
90
+ const tsdk = loadTsdkByPath(params.initializationOptions.typescript.tsdk, params.locale);
91
+ return server.initialize(params, createTypeScriptProject(tsdk.typescript, tsdk.diagnosticMessages, () => {
92
+ return {
93
+ languagePlugins: [createGtsLanguagePlugin(tsdk.typescript)]
94
+ };
95
+ }), [...createTypeScriptServices(tsdk.typescript), createDiagnosticsPlugin()]);
96
+ });
97
+ connection.onInitialized(server.initialized);
98
+ connection.onShutdown(server.shutdown);
99
+ process.on("uncaughtException", (err) => {
100
+ console.error("Uncaught exception:", err);
101
+ });
102
+ process.on("unhandledRejection", (reason, promise) => {
103
+ console.error("Unhandled rejection at:", promise, "reason:", reason);
104
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@gi-tcg/gts-language-server",
3
+ "version": "0.2.0",
4
+ "repository": "https://github.com/piovium/gts.git",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "bun": "./src/index.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "gts-language-server": "./bin/gts-language-server.js"
16
+ },
17
+ "files": [
18
+ "src",
19
+ "dist",
20
+ "bin"
21
+ ],
22
+ "scripts": {},
23
+ "dependencies": {
24
+ "@gi-tcg/gts-transpiler": "0.2.0",
25
+ "@gi-tcg/gts-language-plugin": "0.2.0",
26
+ "@volar/language-server": "~2.4.0",
27
+ "volar-service-typescript": "volar-2.4",
28
+ "vscode-uri": "^3.0.8"
29
+ },
30
+ "devDependencies": {
31
+ "vscode-languageserver-textdocument": "^1.0.12"
32
+ }
33
+ }
@@ -0,0 +1,55 @@
1
+ import {
2
+ DiagnosticSeverity,
3
+ Range,
4
+ type LanguageServicePlugin,
5
+ } from "@volar/language-server";
6
+ import { getVirtualCode } from "./utils";
7
+ export const createDiagnosticsPlugin = (): LanguageServicePlugin => {
8
+ return {
9
+ name: "gts-diagnostics",
10
+ capabilities: {
11
+ diagnosticProvider: {
12
+ interFileDependencies: false,
13
+ workspaceDiagnostics: false,
14
+ },
15
+ },
16
+ create: (context) => {
17
+ return {
18
+ provideDiagnostics: (document) => {
19
+ try {
20
+ console.log(document.uri);
21
+ const [virtualCode] = getVirtualCode(document, context);
22
+ console.log(document.uri, virtualCode);
23
+ if (!virtualCode) {
24
+ return;
25
+ }
26
+ return virtualCode.errors.map((err) => {
27
+ const loc = err.position ?? {
28
+ start: { line: 1, column: 0 },
29
+ end: { line: 1, column: 1 },
30
+ };
31
+ const range: Range = {
32
+ start: {
33
+ line: loc.start.line - 1,
34
+ character: loc.start.column,
35
+ },
36
+ end: { line: loc.end.line - 1, character: loc.end.column },
37
+ };
38
+ console.log(range);
39
+ return {
40
+ severity: DiagnosticSeverity.Error,
41
+ range,
42
+ message: err.message,
43
+ source: "gts-transpiler",
44
+ code: "gts-transpiler-error",
45
+ };
46
+ });
47
+ } catch (e) {
48
+ console.error(e);
49
+ return;
50
+ }
51
+ },
52
+ };
53
+ },
54
+ };
55
+ };
@@ -0,0 +1,114 @@
1
+ import type {
2
+ LanguageServicePluginInstance,
3
+ LanguageServicePlugin,
4
+ } from "@volar/language-server";
5
+ import { getVirtualCode, getWordFromPosition } from "./utils";
6
+
7
+ export function createDocumentHighlightPlugin(): 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
+ }
package/src/index.ts ADDED
@@ -0,0 +1,47 @@
1
+ import {
2
+ createConnection,
3
+ createServer,
4
+ createTypeScriptProject,
5
+ Diagnostic,
6
+ loadTsdkByPath,
7
+ } from "@volar/language-server/node.js";
8
+ import { create as createTypeScriptServices } from "volar-service-typescript";
9
+ import { createGtsLanguagePlugin } from "@gi-tcg/gts-language-plugin";
10
+ import { createDiagnosticsPlugin } from "./diagnostics";
11
+
12
+ const connection = createConnection();
13
+ const server = createServer(connection);
14
+
15
+ connection.listen();
16
+
17
+ connection.onInitialize((params) => {
18
+ const tsdk = loadTsdkByPath(
19
+ params.initializationOptions.typescript.tsdk,
20
+ params.locale,
21
+ );
22
+ return server.initialize(
23
+ params,
24
+ createTypeScriptProject(
25
+ tsdk.typescript,
26
+ tsdk.diagnosticMessages,
27
+ () => {
28
+ return {
29
+ languagePlugins: [createGtsLanguagePlugin(tsdk.typescript)],
30
+ };
31
+ },
32
+ ),
33
+ [...createTypeScriptServices(tsdk.typescript), createDiagnosticsPlugin()],
34
+ );
35
+ });
36
+
37
+ connection.onInitialized(server.initialized);
38
+
39
+ connection.onShutdown(server.shutdown);
40
+
41
+ process.on("uncaughtException", (err) => {
42
+ console.error("Uncaught exception:", err);
43
+ });
44
+
45
+ process.on("unhandledRejection", (reason, promise) => {
46
+ console.error("Unhandled rejection at:", promise, "reason:", reason);
47
+ });
package/src/utils.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { TextDocument } from "vscode-languageserver-textdocument";
2
+ import type { LanguageServiceContext } from "@volar/language-server";
3
+
4
+ import { URI } from "vscode-uri";
5
+ import { GtsVirtualCode } from "@gi-tcg/gts-language-plugin";
6
+
7
+ /**
8
+ * Get virtual code from the encoded document URI
9
+ */
10
+ export function getVirtualCode(
11
+ document: TextDocument,
12
+ context: LanguageServiceContext
13
+ ): [GtsVirtualCode | null, URI] {
14
+ const uri = URI.parse(document.uri);
15
+ const decoded = context.decodeEmbeddedDocumentUri(uri) as [
16
+ documentUri: URI,
17
+ embeddedCodeId: string
18
+ ];
19
+ const [sourceUri, virtualCodeId] = decoded;
20
+ const sourceScript = context.language.scripts.get(sourceUri);
21
+ const virtualCode = sourceScript?.generated?.embeddedCodes.get(
22
+ virtualCodeId
23
+ );
24
+
25
+ if (!virtualCode) {
26
+ return [null, sourceUri];
27
+ }
28
+ if (!(virtualCode instanceof GtsVirtualCode)) {
29
+ return [null, sourceUri];
30
+ }
31
+ return [virtualCode, sourceUri];
32
+ }
33
+
34
+ const wordRegex = /\w/;
35
+
36
+ /**
37
+ * Get the word at a specific position in the text
38
+ */
39
+ export function getWordFromPosition(
40
+ text: string,
41
+ start: number
42
+ ): { word: string; start: number; end: number } {
43
+ let wordStart = start;
44
+ let wordEnd = start;
45
+ while (wordStart > 0 && wordRegex.test(text[wordStart - 1])) {
46
+ wordStart--;
47
+ }
48
+ while (wordEnd < text.length && wordRegex.test(text[wordEnd])) {
49
+ wordEnd++;
50
+ }
51
+
52
+ const word = text.substring(wordStart, wordEnd);
53
+
54
+ return {
55
+ word,
56
+ start: wordStart,
57
+ end: wordEnd,
58
+ };
59
+ }