@void2610/tyranoscript-lsp 0.1.0 → 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/README.md +26 -0
- package/dist/server.js +355 -20
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @void2610/tyranoscript-lsp
|
|
2
|
+
|
|
3
|
+
[TyranoScript](https://tyrano.jp/)向けLanguage Serverです。[Zed](https://zed.dev/)拡張 [tyranoscript-zed](https://github.com/void2610/tyranoscript-zed) から利用されます。
|
|
4
|
+
|
|
5
|
+
## 機能
|
|
6
|
+
|
|
7
|
+
- タグ名の補完(`[` または `@` 入力時)
|
|
8
|
+
- パラメータの補完(使用済みパラメータを自動除外)
|
|
9
|
+
- 必須パラメータのスニペット自動挿入
|
|
10
|
+
- ホバーによるタグ・パラメータのドキュメント表示
|
|
11
|
+
|
|
12
|
+
## 開発
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
npm run build # dist/server.js にバンドル
|
|
17
|
+
npm run watch # ファイル変更時に自動ビルド
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 謝辞
|
|
21
|
+
|
|
22
|
+
タグ辞書データは [orukRed/tyranosyntax](https://github.com/orukRed/tyranosyntax)(VSCode拡張)の `tyrano.Tooltip.json` を基に作成しました。
|
|
23
|
+
|
|
24
|
+
## ライセンス
|
|
25
|
+
|
|
26
|
+
[MIT](../LICENSE)
|
package/dist/server.js
CHANGED
|
@@ -3081,7 +3081,7 @@ var require_main = __commonJS({
|
|
|
3081
3081
|
exports2.createMessageConnection = exports2.createServerSocketTransport = exports2.createClientSocketTransport = exports2.createServerPipeTransport = exports2.createClientPipeTransport = exports2.generateRandomPipeName = exports2.StreamMessageWriter = exports2.StreamMessageReader = exports2.SocketMessageWriter = exports2.SocketMessageReader = exports2.PortMessageWriter = exports2.PortMessageReader = exports2.IPCMessageWriter = exports2.IPCMessageReader = void 0;
|
|
3082
3082
|
var ril_1 = require_ril();
|
|
3083
3083
|
ril_1.default.install();
|
|
3084
|
-
var
|
|
3084
|
+
var path2 = require("path");
|
|
3085
3085
|
var os = require("os");
|
|
3086
3086
|
var crypto_1 = require("crypto");
|
|
3087
3087
|
var net_1 = require("net");
|
|
@@ -3217,9 +3217,9 @@ var require_main = __commonJS({
|
|
|
3217
3217
|
}
|
|
3218
3218
|
let result;
|
|
3219
3219
|
if (XDG_RUNTIME_DIR) {
|
|
3220
|
-
result =
|
|
3220
|
+
result = path2.join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`);
|
|
3221
3221
|
} else {
|
|
3222
|
-
result =
|
|
3222
|
+
result = path2.join(os.tmpdir(), `vscode-${randomSuffix}.sock`);
|
|
3223
3223
|
}
|
|
3224
3224
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3225
3225
|
if (limit !== void 0 && result.length > limit) {
|
|
@@ -8309,8 +8309,8 @@ var require_files = __commonJS({
|
|
|
8309
8309
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
8310
8310
|
exports2.resolveModulePath = exports2.FileSystem = exports2.resolveGlobalYarnPath = exports2.resolveGlobalNodePath = exports2.resolve = exports2.uriToFilePath = void 0;
|
|
8311
8311
|
var url = require("url");
|
|
8312
|
-
var
|
|
8313
|
-
var
|
|
8312
|
+
var path2 = require("path");
|
|
8313
|
+
var fs2 = require("fs");
|
|
8314
8314
|
var child_process_1 = require("child_process");
|
|
8315
8315
|
function uriToFilePath(uri) {
|
|
8316
8316
|
let parsed = url.parse(uri);
|
|
@@ -8328,7 +8328,7 @@ var require_files = __commonJS({
|
|
|
8328
8328
|
segments.shift();
|
|
8329
8329
|
}
|
|
8330
8330
|
}
|
|
8331
|
-
return
|
|
8331
|
+
return path2.normalize(segments.join("/"));
|
|
8332
8332
|
}
|
|
8333
8333
|
exports2.uriToFilePath = uriToFilePath;
|
|
8334
8334
|
function isWindows() {
|
|
@@ -8357,9 +8357,9 @@ var require_files = __commonJS({
|
|
|
8357
8357
|
let env = process.env;
|
|
8358
8358
|
let newEnv = /* @__PURE__ */ Object.create(null);
|
|
8359
8359
|
Object.keys(env).forEach((key) => newEnv[key] = env[key]);
|
|
8360
|
-
if (nodePath &&
|
|
8360
|
+
if (nodePath && fs2.existsSync(nodePath)) {
|
|
8361
8361
|
if (newEnv[nodePathKey]) {
|
|
8362
|
-
newEnv[nodePathKey] = nodePath +
|
|
8362
|
+
newEnv[nodePathKey] = nodePath + path2.delimiter + newEnv[nodePathKey];
|
|
8363
8363
|
} else {
|
|
8364
8364
|
newEnv[nodePathKey] = nodePath;
|
|
8365
8365
|
}
|
|
@@ -8432,9 +8432,9 @@ var require_files = __commonJS({
|
|
|
8432
8432
|
}
|
|
8433
8433
|
if (prefix.length > 0) {
|
|
8434
8434
|
if (isWindows()) {
|
|
8435
|
-
return
|
|
8435
|
+
return path2.join(prefix, "node_modules");
|
|
8436
8436
|
} else {
|
|
8437
|
-
return
|
|
8437
|
+
return path2.join(prefix, "lib", "node_modules");
|
|
8438
8438
|
}
|
|
8439
8439
|
}
|
|
8440
8440
|
return void 0;
|
|
@@ -8474,7 +8474,7 @@ var require_files = __commonJS({
|
|
|
8474
8474
|
try {
|
|
8475
8475
|
let yarn = JSON.parse(line);
|
|
8476
8476
|
if (yarn.type === "log") {
|
|
8477
|
-
return
|
|
8477
|
+
return path2.join(yarn.data, "node_modules");
|
|
8478
8478
|
}
|
|
8479
8479
|
} catch (e) {
|
|
8480
8480
|
}
|
|
@@ -8497,24 +8497,24 @@ var require_files = __commonJS({
|
|
|
8497
8497
|
if (process.platform === "win32") {
|
|
8498
8498
|
_isCaseSensitive = false;
|
|
8499
8499
|
} else {
|
|
8500
|
-
_isCaseSensitive = !
|
|
8500
|
+
_isCaseSensitive = !fs2.existsSync(__filename.toUpperCase()) || !fs2.existsSync(__filename.toLowerCase());
|
|
8501
8501
|
}
|
|
8502
8502
|
return _isCaseSensitive;
|
|
8503
8503
|
}
|
|
8504
8504
|
FileSystem2.isCaseSensitive = isCaseSensitive;
|
|
8505
8505
|
function isParent(parent, child) {
|
|
8506
8506
|
if (isCaseSensitive()) {
|
|
8507
|
-
return
|
|
8507
|
+
return path2.normalize(child).indexOf(path2.normalize(parent)) === 0;
|
|
8508
8508
|
} else {
|
|
8509
|
-
return
|
|
8509
|
+
return path2.normalize(child).toLowerCase().indexOf(path2.normalize(parent).toLowerCase()) === 0;
|
|
8510
8510
|
}
|
|
8511
8511
|
}
|
|
8512
8512
|
FileSystem2.isParent = isParent;
|
|
8513
8513
|
})(FileSystem || (exports2.FileSystem = FileSystem = {}));
|
|
8514
8514
|
function resolveModulePath(workspaceRoot, moduleName, nodePath, tracer) {
|
|
8515
8515
|
if (nodePath) {
|
|
8516
|
-
if (!
|
|
8517
|
-
nodePath =
|
|
8516
|
+
if (!path2.isAbsolute(nodePath)) {
|
|
8517
|
+
nodePath = path2.join(workspaceRoot, nodePath);
|
|
8518
8518
|
}
|
|
8519
8519
|
return resolve(moduleName, nodePath, nodePath, tracer).then((value) => {
|
|
8520
8520
|
if (FileSystem.isParent(nodePath, value)) {
|
|
@@ -12057,16 +12057,266 @@ vmax\u5C5E\u6027\u30920\u306B\u8A2D\u5B9A\u3059\u308B\u3068\u6A2A\u63FA\u308C\u3
|
|
|
12057
12057
|
]);
|
|
12058
12058
|
var TAG_NAMES = Array.from(TAG_DATABASE.keys());
|
|
12059
12059
|
|
|
12060
|
+
// src/workspaceScanner.ts
|
|
12061
|
+
var fs = __toESM(require("fs"));
|
|
12062
|
+
var path = __toESM(require("path"));
|
|
12063
|
+
var TAG_STORAGE_MAPPING = /* @__PURE__ */ new Map([
|
|
12064
|
+
["bg", "bgimage"],
|
|
12065
|
+
["bg2", "bgimage"],
|
|
12066
|
+
["chara_new", "fgimage"],
|
|
12067
|
+
["chara_face", "fgimage"],
|
|
12068
|
+
["chara_mod", "fgimage"],
|
|
12069
|
+
["chara_show", "fgimage"],
|
|
12070
|
+
["chara_layer", "fgimage"],
|
|
12071
|
+
["image", "image"],
|
|
12072
|
+
["cursor", "image"],
|
|
12073
|
+
["graph", "image"],
|
|
12074
|
+
["mask", "image"],
|
|
12075
|
+
["playbgm", "bgm"],
|
|
12076
|
+
["fadeinbgm", "bgm"],
|
|
12077
|
+
["xchgbgm", "bgm"],
|
|
12078
|
+
["playse", "sound"],
|
|
12079
|
+
["fadeinse", "sound"],
|
|
12080
|
+
["movie", "video"],
|
|
12081
|
+
["bgmovie", "video"],
|
|
12082
|
+
["layer_video", "video"],
|
|
12083
|
+
["jump", "scenario"],
|
|
12084
|
+
["call", "scenario"],
|
|
12085
|
+
["link", "scenario"],
|
|
12086
|
+
["glink", "scenario"],
|
|
12087
|
+
["clickable", "scenario"],
|
|
12088
|
+
["button", "scenario"]
|
|
12089
|
+
]);
|
|
12090
|
+
var CACHE_TTL = 3e4;
|
|
12091
|
+
var WorkspaceScanner = class {
|
|
12092
|
+
constructor() {
|
|
12093
|
+
this.rootPath = "";
|
|
12094
|
+
this.dataPath = "";
|
|
12095
|
+
this.initialized = false;
|
|
12096
|
+
// アセットファイルキャッシュ(カテゴリ別)
|
|
12097
|
+
this.assetCache = /* @__PURE__ */ new Map();
|
|
12098
|
+
// KSファイルインデックス(ファイルパスをキーに)
|
|
12099
|
+
this.ksFileIndices = /* @__PURE__ */ new Map();
|
|
12100
|
+
}
|
|
12101
|
+
/**
|
|
12102
|
+
* ワークスペースルートを設定しdataディレクトリの存在を確認する
|
|
12103
|
+
*/
|
|
12104
|
+
initialize(rootUri) {
|
|
12105
|
+
try {
|
|
12106
|
+
const url = new URL(rootUri);
|
|
12107
|
+
this.rootPath = decodeURIComponent(url.pathname);
|
|
12108
|
+
this.dataPath = path.join(this.rootPath, "data");
|
|
12109
|
+
if (fs.existsSync(this.dataPath)) {
|
|
12110
|
+
this.initialized = true;
|
|
12111
|
+
return true;
|
|
12112
|
+
}
|
|
12113
|
+
} catch {
|
|
12114
|
+
}
|
|
12115
|
+
this.initialized = false;
|
|
12116
|
+
return false;
|
|
12117
|
+
}
|
|
12118
|
+
/**
|
|
12119
|
+
* アセットスキャンとKSファイルスキャンを並行実行する
|
|
12120
|
+
*/
|
|
12121
|
+
async scanAll() {
|
|
12122
|
+
if (!this.initialized) return;
|
|
12123
|
+
await Promise.all([this.scanAssets(), this.scanKsFiles()]);
|
|
12124
|
+
}
|
|
12125
|
+
/**
|
|
12126
|
+
* 全アセットカテゴリのディレクトリを走査する
|
|
12127
|
+
*/
|
|
12128
|
+
async scanAssets() {
|
|
12129
|
+
const categories = [
|
|
12130
|
+
"bgimage",
|
|
12131
|
+
"fgimage",
|
|
12132
|
+
"image",
|
|
12133
|
+
"bgm",
|
|
12134
|
+
"sound",
|
|
12135
|
+
"video",
|
|
12136
|
+
"scenario",
|
|
12137
|
+
"others"
|
|
12138
|
+
];
|
|
12139
|
+
for (const category of categories) {
|
|
12140
|
+
this.scanAssetCategory(category);
|
|
12141
|
+
}
|
|
12142
|
+
}
|
|
12143
|
+
/**
|
|
12144
|
+
* 指定カテゴリのアセットディレクトリを走査しキャッシュに格納する
|
|
12145
|
+
*/
|
|
12146
|
+
scanAssetCategory(category) {
|
|
12147
|
+
const dirPath = path.join(this.dataPath, category);
|
|
12148
|
+
try {
|
|
12149
|
+
if (!fs.existsSync(dirPath)) {
|
|
12150
|
+
this.assetCache.set(category, { files: [], timestamp: Date.now() });
|
|
12151
|
+
return;
|
|
12152
|
+
}
|
|
12153
|
+
const files = this.readDirRecursive(dirPath, dirPath);
|
|
12154
|
+
this.assetCache.set(category, { files, timestamp: Date.now() });
|
|
12155
|
+
} catch {
|
|
12156
|
+
this.assetCache.set(category, { files: [], timestamp: Date.now() });
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
/**
|
|
12160
|
+
* ディレクトリを再帰的に走査しファイルの相対パスリストを返す
|
|
12161
|
+
*/
|
|
12162
|
+
readDirRecursive(dirPath, basePath) {
|
|
12163
|
+
const results = [];
|
|
12164
|
+
try {
|
|
12165
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
12166
|
+
for (const entry of entries) {
|
|
12167
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
12168
|
+
if (entry.isDirectory()) {
|
|
12169
|
+
results.push(...this.readDirRecursive(fullPath, basePath));
|
|
12170
|
+
} else {
|
|
12171
|
+
results.push(path.relative(basePath, fullPath));
|
|
12172
|
+
}
|
|
12173
|
+
}
|
|
12174
|
+
} catch {
|
|
12175
|
+
}
|
|
12176
|
+
return results;
|
|
12177
|
+
}
|
|
12178
|
+
/**
|
|
12179
|
+
* data/scenario/ 配下の .ks ファイルを全件読み込み、ラベルとマクロを抽出する
|
|
12180
|
+
*/
|
|
12181
|
+
async scanKsFiles() {
|
|
12182
|
+
const scenarioPath = path.join(this.dataPath, "scenario");
|
|
12183
|
+
if (!fs.existsSync(scenarioPath)) return;
|
|
12184
|
+
const ksFiles = this.findKsFiles(scenarioPath);
|
|
12185
|
+
this.ksFileIndices.clear();
|
|
12186
|
+
for (const filePath of ksFiles) {
|
|
12187
|
+
try {
|
|
12188
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
12189
|
+
const relativePath = path.relative(this.dataPath, filePath);
|
|
12190
|
+
this.indexKsContent(relativePath, content);
|
|
12191
|
+
} catch {
|
|
12192
|
+
}
|
|
12193
|
+
}
|
|
12194
|
+
}
|
|
12195
|
+
/**
|
|
12196
|
+
* 指定ディレクトリ配下の .ks ファイルを再帰的に検索する
|
|
12197
|
+
*/
|
|
12198
|
+
findKsFiles(dirPath) {
|
|
12199
|
+
const results = [];
|
|
12200
|
+
try {
|
|
12201
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
12202
|
+
for (const entry of entries) {
|
|
12203
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
12204
|
+
if (entry.isDirectory()) {
|
|
12205
|
+
results.push(...this.findKsFiles(fullPath));
|
|
12206
|
+
} else if (entry.name.endsWith(".ks")) {
|
|
12207
|
+
results.push(fullPath);
|
|
12208
|
+
}
|
|
12209
|
+
}
|
|
12210
|
+
} catch {
|
|
12211
|
+
}
|
|
12212
|
+
return results;
|
|
12213
|
+
}
|
|
12214
|
+
/**
|
|
12215
|
+
* KSファイルの内容からラベルとマクロを正規表現で抽出しインデックスに格納する
|
|
12216
|
+
*/
|
|
12217
|
+
indexKsContent(relativePath, content) {
|
|
12218
|
+
const labels = [];
|
|
12219
|
+
const macros = [];
|
|
12220
|
+
const lines = content.split("\n");
|
|
12221
|
+
for (let i = 0; i < lines.length; i++) {
|
|
12222
|
+
const line = lines[i];
|
|
12223
|
+
const labelMatch = line.match(/^\*(\w+)/);
|
|
12224
|
+
if (labelMatch) {
|
|
12225
|
+
labels.push({
|
|
12226
|
+
name: labelMatch[1],
|
|
12227
|
+
file: relativePath,
|
|
12228
|
+
line: i
|
|
12229
|
+
});
|
|
12230
|
+
}
|
|
12231
|
+
const macroMatch = line.match(/\[macro\s+name\s*=\s*"(\w+)"\s*\]/i);
|
|
12232
|
+
if (macroMatch) {
|
|
12233
|
+
macros.push({
|
|
12234
|
+
name: macroMatch[1],
|
|
12235
|
+
file: relativePath,
|
|
12236
|
+
line: i
|
|
12237
|
+
});
|
|
12238
|
+
}
|
|
12239
|
+
}
|
|
12240
|
+
this.ksFileIndices.set(relativePath, { labels, macros });
|
|
12241
|
+
}
|
|
12242
|
+
/**
|
|
12243
|
+
* 単一ファイルのインクリメンタル更新(編集中ファイルのインデックスを差し替え)
|
|
12244
|
+
*/
|
|
12245
|
+
updateFile(uri, content) {
|
|
12246
|
+
if (!this.initialized) return;
|
|
12247
|
+
try {
|
|
12248
|
+
const url = new URL(uri);
|
|
12249
|
+
const filePath = decodeURIComponent(url.pathname);
|
|
12250
|
+
const relativePath = path.relative(this.dataPath, filePath);
|
|
12251
|
+
if (relativePath.startsWith("scenario") && filePath.endsWith(".ks")) {
|
|
12252
|
+
this.indexKsContent(relativePath, content);
|
|
12253
|
+
}
|
|
12254
|
+
} catch {
|
|
12255
|
+
}
|
|
12256
|
+
}
|
|
12257
|
+
/**
|
|
12258
|
+
* 指定カテゴリのアセットファイル一覧を返す
|
|
12259
|
+
* キャッシュTTL超過時は自動再スキャンする
|
|
12260
|
+
*/
|
|
12261
|
+
getAssetsForCategory(category) {
|
|
12262
|
+
if (!this.initialized) return [];
|
|
12263
|
+
const cached = this.assetCache.get(category);
|
|
12264
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
12265
|
+
return cached.files;
|
|
12266
|
+
}
|
|
12267
|
+
this.scanAssetCategory(category);
|
|
12268
|
+
return this.assetCache.get(category)?.files ?? [];
|
|
12269
|
+
}
|
|
12270
|
+
/**
|
|
12271
|
+
* 全ラベル定義を返す
|
|
12272
|
+
*/
|
|
12273
|
+
getLabels() {
|
|
12274
|
+
const labels = [];
|
|
12275
|
+
for (const index of this.ksFileIndices.values()) {
|
|
12276
|
+
labels.push(...index.labels);
|
|
12277
|
+
}
|
|
12278
|
+
return labels;
|
|
12279
|
+
}
|
|
12280
|
+
/**
|
|
12281
|
+
* 全マクロ定義を返す
|
|
12282
|
+
*/
|
|
12283
|
+
getMacros() {
|
|
12284
|
+
const macros = [];
|
|
12285
|
+
for (const index of this.ksFileIndices.values()) {
|
|
12286
|
+
macros.push(...index.macros);
|
|
12287
|
+
}
|
|
12288
|
+
return macros;
|
|
12289
|
+
}
|
|
12290
|
+
/**
|
|
12291
|
+
* シナリオファイル一覧を返す(.ks拡張子)
|
|
12292
|
+
*/
|
|
12293
|
+
getScenarioFiles() {
|
|
12294
|
+
return this.getAssetsForCategory("scenario");
|
|
12295
|
+
}
|
|
12296
|
+
/**
|
|
12297
|
+
* 初期化済みかどうかを返す
|
|
12298
|
+
*/
|
|
12299
|
+
isInitialized() {
|
|
12300
|
+
return this.initialized;
|
|
12301
|
+
}
|
|
12302
|
+
};
|
|
12303
|
+
|
|
12060
12304
|
// src/server.ts
|
|
12061
12305
|
var connection = (0, import_node.createConnection)(import_node.ProposedFeatures.all);
|
|
12062
12306
|
var documents = new import_node.TextDocuments(TextDocument);
|
|
12063
|
-
|
|
12307
|
+
var scanner = new WorkspaceScanner();
|
|
12308
|
+
connection.onInitialize((params) => {
|
|
12309
|
+
const rootUri = params.rootUri ?? params.workspaceFolders?.[0]?.uri;
|
|
12310
|
+
if (rootUri && scanner.initialize(rootUri)) {
|
|
12311
|
+
scanner.scanAll().catch(() => {
|
|
12312
|
+
});
|
|
12313
|
+
}
|
|
12064
12314
|
return {
|
|
12065
12315
|
capabilities: {
|
|
12066
12316
|
textDocumentSync: import_node.TextDocumentSyncKind.Incremental,
|
|
12067
12317
|
completionProvider: {
|
|
12068
|
-
// "["
|
|
12069
|
-
triggerCharacters: ["[", "@", " "],
|
|
12318
|
+
// "[", "@", スペース, '"' で補完をトリガー
|
|
12319
|
+
triggerCharacters: ["[", "@", " ", '"'],
|
|
12070
12320
|
resolveProvider: false
|
|
12071
12321
|
},
|
|
12072
12322
|
hoverProvider: true
|
|
@@ -12112,6 +12362,18 @@ function isTagNameTrigger(lineText, character) {
|
|
|
12112
12362
|
}
|
|
12113
12363
|
return null;
|
|
12114
12364
|
}
|
|
12365
|
+
function getParamValueContext(lineText, character) {
|
|
12366
|
+
const tagCtx = getTagContext(lineText, character);
|
|
12367
|
+
if (!tagCtx) return null;
|
|
12368
|
+
const textUpToCursor = lineText.substring(0, character);
|
|
12369
|
+
const valueMatch = textUpToCursor.match(/(\w+)\s*=\s*"([^"]*)$/);
|
|
12370
|
+
if (!valueMatch) return null;
|
|
12371
|
+
return {
|
|
12372
|
+
tagName: tagCtx.tagName,
|
|
12373
|
+
paramName: valueMatch[1],
|
|
12374
|
+
currentValue: valueMatch[2]
|
|
12375
|
+
};
|
|
12376
|
+
}
|
|
12115
12377
|
function createTagCompletions(trigger) {
|
|
12116
12378
|
return TAG_NAMES.map((name, index) => {
|
|
12117
12379
|
const tag = TAG_DATABASE.get(name);
|
|
@@ -12189,6 +12451,53 @@ function createTagDocumentation(tag) {
|
|
|
12189
12451
|
}
|
|
12190
12452
|
return doc;
|
|
12191
12453
|
}
|
|
12454
|
+
function createMacroCompletions(trigger) {
|
|
12455
|
+
const macros = scanner.getMacros();
|
|
12456
|
+
return macros.map((macro, index) => {
|
|
12457
|
+
const insertText = trigger === "bracket" ? `${macro.name}]` : macro.name;
|
|
12458
|
+
return {
|
|
12459
|
+
label: macro.name,
|
|
12460
|
+
kind: import_node.CompletionItemKind.Function,
|
|
12461
|
+
detail: `\u30DE\u30AF\u30ED (${macro.file})`,
|
|
12462
|
+
documentation: {
|
|
12463
|
+
kind: import_node.MarkupKind.Markdown,
|
|
12464
|
+
value: `**[${macro.name}]** \u2014 \u30E6\u30FC\u30B6\u30FC\u5B9A\u7FA9\u30DE\u30AF\u30ED
|
|
12465
|
+
|
|
12466
|
+
\u5B9A\u7FA9\u5143: \`${macro.file}\` (\u884C ${macro.line + 1})`
|
|
12467
|
+
},
|
|
12468
|
+
insertText,
|
|
12469
|
+
insertTextFormat: import_node.InsertTextFormat.PlainText,
|
|
12470
|
+
// タグ補完の後に表示(5000番台)
|
|
12471
|
+
sortText: String(5e3 + index).padStart(6, "0")
|
|
12472
|
+
};
|
|
12473
|
+
});
|
|
12474
|
+
}
|
|
12475
|
+
function createStorageCompletions(tagName) {
|
|
12476
|
+
const category = TAG_STORAGE_MAPPING.get(tagName);
|
|
12477
|
+
if (!category) return [];
|
|
12478
|
+
const files = scanner.getAssetsForCategory(category);
|
|
12479
|
+
return files.map((file, index) => ({
|
|
12480
|
+
label: file,
|
|
12481
|
+
kind: import_node.CompletionItemKind.File,
|
|
12482
|
+
detail: `${category}/`,
|
|
12483
|
+
sortText: String(index).padStart(4, "0")
|
|
12484
|
+
}));
|
|
12485
|
+
}
|
|
12486
|
+
function createTargetCompletions() {
|
|
12487
|
+
const labels = scanner.getLabels();
|
|
12488
|
+
return labels.map((label, index) => ({
|
|
12489
|
+
label: `*${label.name}`,
|
|
12490
|
+
kind: import_node.CompletionItemKind.Reference,
|
|
12491
|
+
detail: label.file,
|
|
12492
|
+
documentation: {
|
|
12493
|
+
kind: import_node.MarkupKind.Markdown,
|
|
12494
|
+
value: `\u30E9\u30D9\u30EB ***${label.name}**
|
|
12495
|
+
|
|
12496
|
+
\u5B9A\u7FA9\u5143: \`${label.file}\` (\u884C ${label.line + 1})`
|
|
12497
|
+
},
|
|
12498
|
+
sortText: String(index).padStart(4, "0")
|
|
12499
|
+
}));
|
|
12500
|
+
}
|
|
12192
12501
|
connection.onCompletion(
|
|
12193
12502
|
(params) => {
|
|
12194
12503
|
const document = documents.get(params.textDocument.uri);
|
|
@@ -12199,7 +12508,19 @@ connection.onCompletion(
|
|
|
12199
12508
|
});
|
|
12200
12509
|
const trigger = isTagNameTrigger(line, params.position.character);
|
|
12201
12510
|
if (trigger) {
|
|
12202
|
-
|
|
12511
|
+
const tagItems = createTagCompletions(trigger);
|
|
12512
|
+
const macroItems = createMacroCompletions(trigger);
|
|
12513
|
+
return [...tagItems, ...macroItems];
|
|
12514
|
+
}
|
|
12515
|
+
const valueCtx = getParamValueContext(line, params.position.character);
|
|
12516
|
+
if (valueCtx) {
|
|
12517
|
+
if (valueCtx.paramName === "storage") {
|
|
12518
|
+
return createStorageCompletions(valueCtx.tagName);
|
|
12519
|
+
}
|
|
12520
|
+
if (valueCtx.paramName === "target") {
|
|
12521
|
+
return createTargetCompletions();
|
|
12522
|
+
}
|
|
12523
|
+
return [];
|
|
12203
12524
|
}
|
|
12204
12525
|
const context = getTagContext(line, params.position.character);
|
|
12205
12526
|
if (context && context.isInParams) {
|
|
@@ -12333,5 +12654,19 @@ ${param.description}`
|
|
|
12333
12654
|
}
|
|
12334
12655
|
return null;
|
|
12335
12656
|
}
|
|
12657
|
+
var updateTimers = /* @__PURE__ */ new Map();
|
|
12658
|
+
documents.onDidChangeContent((change) => {
|
|
12659
|
+
const uri = change.document.uri;
|
|
12660
|
+
if (!uri.endsWith(".ks")) return;
|
|
12661
|
+
const existing = updateTimers.get(uri);
|
|
12662
|
+
if (existing) clearTimeout(existing);
|
|
12663
|
+
updateTimers.set(
|
|
12664
|
+
uri,
|
|
12665
|
+
setTimeout(() => {
|
|
12666
|
+
scanner.updateFile(uri, change.document.getText());
|
|
12667
|
+
updateTimers.delete(uri);
|
|
12668
|
+
}, 500)
|
|
12669
|
+
);
|
|
12670
|
+
});
|
|
12336
12671
|
documents.listen(connection);
|
|
12337
12672
|
connection.listen();
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void2610/tyranoscript-lsp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TyranoScript Language Server for Zed",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
|
-
"files": [
|
|
6
|
+
"files": [
|
|
7
|
+
"dist/"
|
|
8
|
+
],
|
|
7
9
|
"scripts": {
|
|
8
10
|
"build": "esbuild src/server.ts --bundle --platform=node --outfile=dist/server.js --format=cjs",
|
|
9
11
|
"watch": "esbuild src/server.ts --bundle --platform=node --outfile=dist/server.js --format=cjs --watch",
|