@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.
- package/bin/gts-language-server.js +7 -0
- package/dist/index.js +104 -0
- package/package.json +33 -0
- package/src/diagnostics.ts +55 -0
- package/src/document_highlight.ts +114 -0
- package/src/index.ts +47 -0
- package/src/utils.ts +59 -0
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
|
+
}
|