arkts-lsp-proxy 0.1.1 → 0.1.3

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/proxy.js CHANGED
@@ -1,64 +1,802 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.injectInitializationOptions = injectInitializationOptions;
4
37
  exports.createProxy = createProxy;
5
38
  const node_1 = require("vscode-jsonrpc/node");
6
- function injectInitializationOptions(message, payload) {
7
- if (message.method === 'initialize' && message.params) {
8
- const params = { ...message.params };
9
- params.initializationOptions = {
10
- ...(params.initializationOptions || {}),
11
- rootUri: payload.rootUri,
12
- lspServerWorkspacePath: payload.lspServerWorkspacePath,
13
- modules: payload.modules,
39
+ const vscode_jsonrpc_1 = require("vscode-jsonrpc");
40
+ const path = __importStar(require("node:path"));
41
+ const node_url_1 = require("node:url");
42
+ const project_1 = require("./project");
43
+ const ace_server_1 = require("./ace-server");
44
+ const hvigor_1 = require("./hvigor");
45
+ const ACE_NOTIFICATION_METHODS = {
46
+ 'textDocument/didOpen': 'aceProject/onAsyncDidOpen',
47
+ 'textDocument/didChange': 'aceProject/onAsyncDidChange',
48
+ 'textDocument/didClose': 'aceProject/onAsyncDidClose',
49
+ };
50
+ const ACE_FIND_USAGES_METHOD = 'aceProject/onAsyncFindUsages';
51
+ const ACE_REQUEST_METHODS = {
52
+ 'textDocument/hover': 'aceProject/onAsyncHover',
53
+ 'textDocument/completion': 'aceProject/onAsyncCompletion',
54
+ 'completionItem/resolve': 'aceProject/onAsyncCompletionResolve',
55
+ 'textDocument/definition': 'aceProject/onAsyncDefinition',
56
+ 'textDocument/references': ACE_FIND_USAGES_METHOD,
57
+ 'textDocument/signatureHelp': 'aceProject/onAsyncSignatureHelp',
58
+ };
59
+ const ACE_RESPONSE_METHODS = new Set(Object.values(ACE_REQUEST_METHODS));
60
+ const ACE_MODULE_INIT_METHOD = 'aceProject/onModuleInitFinish';
61
+ function isPlainObject(value) {
62
+ return typeof value === 'object' && value !== null;
63
+ }
64
+ function decodeHtmlEntities(value) {
65
+ return value
66
+ .replace(/&gt;/g, '>')
67
+ .replace(/&lt;/g, '<')
68
+ .replace(/&quot;/g, '"')
69
+ .replace(/&#39;/g, "'")
70
+ .replace(/&amp;/g, '&');
71
+ }
72
+ function sanitizeMarkdownLanguage(value) {
73
+ return typeof value === 'string' ? value.replace(/[^A-Za-z0-9_+#.-]/g, '') : '';
74
+ }
75
+ function extractHoverTag(tag) {
76
+ if (typeof tag === 'string') {
77
+ const trimmed = tag.trim();
78
+ return trimmed.length ? trimmed : null;
79
+ }
80
+ if (!isPlainObject(tag)) {
81
+ return null;
82
+ }
83
+ const name = typeof tag.name === 'string' ? tag.name.trim() : '';
84
+ const textValue = tag.text ?? tag.comment ?? tag.documentation;
85
+ const text = typeof textValue === 'string' ? decodeHtmlEntities(textValue).trim() : '';
86
+ if (name && text) {
87
+ return `@${name} ${text}`;
88
+ }
89
+ if (name) {
90
+ return `@${name}`;
91
+ }
92
+ return text.length ? text : null;
93
+ }
94
+ function extractHoverDocuments(data) {
95
+ const documents = [];
96
+ const items = Array.isArray(data) ? data : [data];
97
+ for (const item of items) {
98
+ if (!isPlainObject(item)) {
99
+ continue;
100
+ }
101
+ if (typeof item.document === 'string') {
102
+ const document = decodeHtmlEntities(item.document).trim();
103
+ if (document.length) {
104
+ documents.push(document);
105
+ }
106
+ }
107
+ if (Array.isArray(item.tags)) {
108
+ for (const tag of item.tags) {
109
+ const formatted = extractHoverTag(tag);
110
+ if (formatted) {
111
+ documents.push(formatted);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ return documents;
117
+ }
118
+ function parseAceHoverPayload(value) {
119
+ const trimmed = value.trim();
120
+ if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
121
+ return null;
122
+ }
123
+ try {
124
+ const parsed = JSON.parse(trimmed);
125
+ if (!isPlainObject(parsed)) {
126
+ return null;
127
+ }
128
+ if (!isPlainObject(parsed.code) && !('data' in parsed)) {
129
+ return null;
130
+ }
131
+ return parsed;
132
+ }
133
+ catch {
134
+ return null;
135
+ }
136
+ }
137
+ function normalizeAceHoverContents(contents) {
138
+ const rawValue = typeof contents === 'string'
139
+ ? contents
140
+ : isPlainObject(contents) && typeof contents.value === 'string'
141
+ ? contents.value
142
+ : null;
143
+ if (!rawValue) {
144
+ return contents;
145
+ }
146
+ const payload = parseAceHoverPayload(rawValue);
147
+ if (!payload) {
148
+ return contents;
149
+ }
150
+ const parts = [];
151
+ const code = isPlainObject(payload.code) ? payload.code : null;
152
+ const codeValue = typeof code?.value === 'string' ? decodeHtmlEntities(code.value).trim() : '';
153
+ if (codeValue.length) {
154
+ const language = sanitizeMarkdownLanguage(code?.language);
155
+ parts.push(`\`\`\`${language}\n${codeValue}\n\`\`\``);
156
+ }
157
+ parts.push(...extractHoverDocuments(payload.data));
158
+ if (parts.length === 0) {
159
+ return contents;
160
+ }
161
+ return {
162
+ kind: 'markdown',
163
+ value: parts.join('\n\n'),
164
+ };
165
+ }
166
+ function normalizeHoverResult(result) {
167
+ if (!isPlainObject(result) || !('contents' in result)) {
168
+ return result;
169
+ }
170
+ const contents = normalizeAceHoverContents(result.contents);
171
+ if (contents === result.contents) {
172
+ return result;
173
+ }
174
+ return {
175
+ ...result,
176
+ contents,
177
+ };
178
+ }
179
+ function normalizeClientResult(method, result) {
180
+ if (method === 'textDocument/hover') {
181
+ return normalizeHoverResult(result);
182
+ }
183
+ return result;
184
+ }
185
+ function createQueueToken() {
186
+ return `arkts-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
187
+ }
188
+ function uriToFilePath(uri) {
189
+ if (typeof uri !== 'string' || !uri.length) {
190
+ return null;
191
+ }
192
+ if (uri.startsWith('file://')) {
193
+ try {
194
+ return (0, node_url_1.fileURLToPath)(uri);
195
+ }
196
+ catch {
197
+ return null;
198
+ }
199
+ }
200
+ return path.resolve(uri);
201
+ }
202
+ function detectLanguageId(uri) {
203
+ const ext = path.extname(uri).toLowerCase();
204
+ if (ext === '.ets' || ext === '.d.ets') {
205
+ return 'deveco.apptool.ets';
206
+ }
207
+ if (ext === '.ts') {
208
+ return 'deveco.apptool.ts';
209
+ }
210
+ if (ext === '.js') {
211
+ return 'deveco.apptool.js';
212
+ }
213
+ return 'deveco.apptool.ts';
214
+ }
215
+ function normalizeAceLanguageId(uri, languageId) {
216
+ if (typeof languageId === 'string' && languageId.startsWith('deveco.apptool.')) {
217
+ return languageId;
218
+ }
219
+ return detectLanguageId(uri);
220
+ }
221
+ function inferProjectRootFromInitializeParams(params) {
222
+ if (!isPlainObject(params)) {
223
+ return null;
224
+ }
225
+ const candidate = (Array.isArray(params.workspaceFolders)
226
+ ? params.workspaceFolders[0]?.uri
227
+ : undefined) ??
228
+ params.rootUri ??
229
+ params.rootPath;
230
+ return uriToFilePath(candidate ?? null);
231
+ }
232
+ function getEditorFiles(openFiles) {
233
+ return Array.from(openFiles)
234
+ .map((uri) => uriToFilePath(uri))
235
+ .filter((filePath) => Boolean(filePath));
236
+ }
237
+ function getOpenFileUris(openFiles) {
238
+ return Array.from(openFiles);
239
+ }
240
+ function createRequestPayload(params) {
241
+ return {
242
+ requestId: createQueueToken(),
243
+ params: params || {},
244
+ editorFiles: [],
245
+ traceId: createQueueToken(),
246
+ };
247
+ }
248
+ function isModuleInitSuccess(payload) {
249
+ if (Array.isArray(payload)) {
250
+ return payload.length > 0 && payload.every(isModuleInitSuccess);
251
+ }
252
+ if (typeof payload === 'boolean') {
253
+ return payload;
254
+ }
255
+ if (!isPlainObject(payload)) {
256
+ return false;
257
+ }
258
+ if ('success' in payload && typeof payload.success === 'boolean') {
259
+ return payload.success;
260
+ }
261
+ if ('code' in payload && typeof payload.code === 'number') {
262
+ return payload.code === 0;
263
+ }
264
+ if ('status' in payload && typeof payload.status === 'string') {
265
+ return payload.status.toLowerCase() === 'success';
266
+ }
267
+ if ('result' in payload && typeof payload.result === 'boolean') {
268
+ return payload.result;
269
+ }
270
+ return false;
271
+ }
272
+ function extractTextDocument(params) {
273
+ if (!isPlainObject(params)) {
274
+ return null;
275
+ }
276
+ const textDocument = params.textDocument ?? null;
277
+ return isPlainObject(textDocument) ? textDocument : null;
278
+ }
279
+ function mapNotification(method, params, openFiles) {
280
+ if (!ACE_NOTIFICATION_METHODS[method]) {
281
+ return null;
282
+ }
283
+ if (method === 'textDocument/didOpen') {
284
+ const textDocument = extractTextDocument(params);
285
+ if (!textDocument) {
286
+ return null;
287
+ }
288
+ const uri = typeof textDocument.uri === 'string' ? textDocument.uri : null;
289
+ if (!uri) {
290
+ return null;
291
+ }
292
+ return {
293
+ method: ACE_NOTIFICATION_METHODS[method],
294
+ params: {
295
+ requestId: createQueueToken(),
296
+ params: {
297
+ textDocument: {
298
+ uri,
299
+ languageId: normalizeAceLanguageId(uri, textDocument.languageId),
300
+ version: textDocument.version,
301
+ text: typeof textDocument.text === 'string' ? textDocument.text : '',
302
+ },
303
+ },
304
+ editorFiles: getEditorFiles(openFiles),
305
+ traceId: createQueueToken(),
306
+ },
14
307
  };
15
- return { ...message, params };
16
308
  }
17
- return message;
309
+ if (method === 'textDocument/didClose') {
310
+ const textDocument = extractTextDocument(params);
311
+ if (!textDocument) {
312
+ return null;
313
+ }
314
+ const uri = typeof textDocument.uri === 'string' ? textDocument.uri : null;
315
+ if (!uri) {
316
+ return null;
317
+ }
318
+ return {
319
+ method: ACE_NOTIFICATION_METHODS[method],
320
+ params: {
321
+ requestId: createQueueToken(),
322
+ params: {
323
+ textDocument,
324
+ },
325
+ editorFiles: getEditorFiles(openFiles),
326
+ traceId: createQueueToken(),
327
+ },
328
+ };
329
+ }
330
+ if (method === 'textDocument/didChange') {
331
+ const textDocument = extractTextDocument(params);
332
+ if (!textDocument) {
333
+ return null;
334
+ }
335
+ const uri = typeof textDocument.uri === 'string' ? textDocument.uri : null;
336
+ if (!uri) {
337
+ return null;
338
+ }
339
+ return {
340
+ method: ACE_NOTIFICATION_METHODS[method],
341
+ params: {
342
+ requestId: createQueueToken(),
343
+ params: {
344
+ textDocument: {
345
+ uri,
346
+ languageId: detectLanguageId(uri),
347
+ version: textDocument.version,
348
+ text: typeof textDocument.text === 'string' ? textDocument.text : '',
349
+ },
350
+ contentChanges: Array.isArray(params?.contentChanges) ? params.contentChanges : [],
351
+ },
352
+ editorFiles: getEditorFiles(openFiles),
353
+ traceId: createQueueToken(),
354
+ },
355
+ };
356
+ }
357
+ return {
358
+ method: ACE_NOTIFICATION_METHODS[method],
359
+ params: {
360
+ ...createRequestPayload(params),
361
+ editorFiles: getEditorFiles(openFiles),
362
+ },
363
+ };
18
364
  }
19
- function createProxy(clientIn, clientOut, aceProcess, payload) {
20
- const aceIn = aceProcess.stdout;
21
- const aceOut = aceProcess.stdin;
22
- if (!aceIn || !aceOut) {
365
+ function mapRequest(method, params, openFiles) {
366
+ const mappedMethod = ACE_REQUEST_METHODS[method];
367
+ if (!mappedMethod) {
368
+ return null;
369
+ }
370
+ const payload = {
371
+ ...createRequestPayload(params),
372
+ editorFiles: getEditorFiles(openFiles),
373
+ };
374
+ if (method === 'textDocument/definition') {
375
+ payload.params = {
376
+ ...(isPlainObject(payload.params) ? payload.params : {}),
377
+ isGotoDefinition: true,
378
+ };
379
+ }
380
+ return {
381
+ method: mappedMethod,
382
+ params: payload,
383
+ };
384
+ }
385
+ function isDevEcoEnv(value) {
386
+ return (isPlainObject(value) &&
387
+ typeof value.devecoHome === 'string' &&
388
+ typeof value.sdkPath === 'string' &&
389
+ typeof value.aceServerPath === 'string' &&
390
+ typeof value.nodeBin === 'string' &&
391
+ typeof value.hvigorPath === 'string');
392
+ }
393
+ function isLegacyPayload(value) {
394
+ return (isPlainObject(value) &&
395
+ typeof value.rootUri === 'string' &&
396
+ typeof value.lspServerWorkspacePath === 'string' &&
397
+ Array.isArray(value.modules));
398
+ }
399
+ function isModernMode(value) {
400
+ return isPlainObject(value) && isDevEcoEnv(value.env);
401
+ }
402
+ function injectInitializationOptions(message, payload) {
403
+ if (message.method !== 'initialize' || !message.params) {
404
+ return message;
405
+ }
406
+ const params = { ...message.params };
407
+ const existing = typeof params.initializationOptions === 'object' &&
408
+ params.initializationOptions !== null
409
+ ? params.initializationOptions
410
+ : {};
411
+ params.initializationOptions = {
412
+ ...existing,
413
+ ...payload,
414
+ };
415
+ return { ...message, params };
416
+ }
417
+ function createLegacyProxy(clientConn, proc, payload) {
418
+ if (!proc.stdout || !proc.stdin) {
23
419
  throw new Error('aceProcess must have both stdout and stdin streams');
24
420
  }
25
- const clientConn = (0, node_1.createMessageConnection)(new node_1.StreamMessageReader(clientIn), new node_1.StreamMessageWriter(clientOut));
26
- const aceConn = (0, node_1.createMessageConnection)(new node_1.StreamMessageReader(aceIn), new node_1.StreamMessageWriter(aceOut));
27
- clientConn.onNotification((method, params) => {
28
- aceConn.sendNotification(method, params);
29
- });
30
- clientConn.onRequest((method, params, token) => {
31
- if (method === 'initialize') {
32
- const message = { method, params: params };
33
- const modified = injectInitializationOptions(message, payload);
34
- return aceConn.sendRequest(method, modified.params, token);
421
+ const aceConn = (0, node_1.createMessageConnection)(new node_1.StreamMessageReader(proc.stdout), new node_1.StreamMessageWriter(proc.stdin));
422
+ let disposed = false;
423
+ clientConn.onRequest((method, params) => {
424
+ if (method === 'initialize' && payload) {
425
+ const msg = injectInitializationOptions({
426
+ id: 0,
427
+ method,
428
+ params: isPlainObject(params) ? params : {},
429
+ }, payload);
430
+ return aceConn.sendRequest(method, msg.params);
35
431
  }
36
- return aceConn.sendRequest(method, params, token);
432
+ return aceConn.sendRequest(method, params);
37
433
  });
38
434
  aceConn.onNotification((method, params) => {
39
435
  clientConn.sendNotification(method, params);
40
436
  });
41
- aceConn.onRequest((method, params, token) => {
42
- return clientConn.sendRequest(method, params, token);
43
- });
44
- aceConn.onError(([err]) => {
45
- process.stderr.write(`[arkts-lsp] ace connection error: ${err.message}\n`);
46
- clientConn.dispose();
437
+ aceConn.onError((error) => {
438
+ process.stderr.write(`[arkts-lsp] legacy ace connection error: ${String(error)}\n`);
47
439
  });
48
- clientConn.onError(([err]) => {
49
- process.stderr.write(`[arkts-lsp] client connection error: ${err.message}\n`);
50
- aceConn.dispose();
440
+ clientConn.onNotification((method, params) => {
441
+ aceConn.sendNotification(method, params);
51
442
  });
52
- aceProcess.on('exit', () => {
53
- clientConn.dispose();
443
+ clientConn.onError((error) => {
444
+ process.stderr.write(`[arkts-lsp] client connection error: ${String(error)}\n`);
54
445
  });
55
446
  clientConn.listen();
56
447
  aceConn.listen();
57
448
  return {
58
449
  dispose: () => {
450
+ if (disposed) {
451
+ return;
452
+ }
453
+ disposed = true;
59
454
  clientConn.dispose();
60
455
  aceConn.dispose();
61
456
  },
62
457
  };
63
458
  }
459
+ function createProxy(clientIn, clientOut, envOrLegacy, maybeOptions) {
460
+ const clientConn = (0, node_1.createMessageConnection)(new node_1.StreamMessageReader(clientIn), new node_1.StreamMessageWriter(clientOut));
461
+ if (!isDevEcoEnv(envOrLegacy)) {
462
+ const legacyProc = envOrLegacy.process ?? envOrLegacy;
463
+ if (!legacyProc) {
464
+ throw new Error('legacy mode requires ChildProcess-like object with stdin/stdout streams');
465
+ }
466
+ return createLegacyProxy(clientConn, legacyProc, isLegacyPayload(maybeOptions) ? maybeOptions : undefined);
467
+ }
468
+ const env = envOrLegacy;
469
+ const options = isModernMode(maybeOptions) ? maybeOptions : { env, projectRootHint: undefined };
470
+ const explicitProjectRoot = options.projectRootHint || process.env.ARKTS_PROJECT_ROOT || process.env.ARKTS_PROJECT_PATH;
471
+ const openFiles = new Set();
472
+ const requestQueue = [];
473
+ const notificationQueue = [];
474
+ const pendingAceRequests = new Map();
475
+ let isInitialized = false;
476
+ let isServerReady = false;
477
+ let isBootstrapping = false;
478
+ let bootstrapPromise = null;
479
+ let initializePromise = null;
480
+ let syncPromise = null;
481
+ let aceConn = null;
482
+ let aceHandle = null;
483
+ let project = null;
484
+ let disposed = false;
485
+ function queueRequest(run) {
486
+ return new Promise((resolve, reject) => {
487
+ requestQueue.push({ run, resolve, reject });
488
+ });
489
+ }
490
+ function queueNotification(method, params) {
491
+ notificationQueue.push({ method, params });
492
+ }
493
+ function clearQueues(error) {
494
+ const finalError = error ?? new Error('proxy disposed');
495
+ while (requestQueue.length > 0) {
496
+ const p = requestQueue.shift();
497
+ if (p) {
498
+ p.reject(finalError);
499
+ }
500
+ }
501
+ notificationQueue.length = 0;
502
+ for (const pending of pendingAceRequests.values()) {
503
+ pending.reject(finalError);
504
+ }
505
+ pendingAceRequests.clear();
506
+ }
507
+ function flushQueues() {
508
+ if (!isServerReady || !aceConn) {
509
+ return;
510
+ }
511
+ while (notificationQueue.length > 0) {
512
+ const n = notificationQueue.shift();
513
+ if (!n) {
514
+ continue;
515
+ }
516
+ try {
517
+ aceConn.sendNotification(n.method, n.params);
518
+ }
519
+ catch {
520
+ // ignore flush notification errors
521
+ }
522
+ }
523
+ while (requestQueue.length > 0) {
524
+ const p = requestQueue.shift();
525
+ if (!p) {
526
+ continue;
527
+ }
528
+ p.run().then(p.resolve, p.reject);
529
+ }
530
+ }
531
+ function completePendingAceRequest(method, params) {
532
+ if (!ACE_RESPONSE_METHODS.has(method) || !isPlainObject(params)) {
533
+ return false;
534
+ }
535
+ const requestId = typeof params.requestId === 'string' ? params.requestId : null;
536
+ if (!requestId) {
537
+ return false;
538
+ }
539
+ const pending = pendingAceRequests.get(requestId);
540
+ if (!pending) {
541
+ return false;
542
+ }
543
+ pendingAceRequests.delete(requestId);
544
+ pending.resolve('result' in params ? params.result : params);
545
+ return true;
546
+ }
547
+ function sendAceRequestAsNotification(conn, method, params) {
548
+ const requestId = typeof params.requestId === 'string' ? params.requestId : createQueueToken();
549
+ const payload = { ...params, requestId };
550
+ return new Promise((resolve, reject) => {
551
+ pendingAceRequests.set(requestId, { resolve, reject });
552
+ try {
553
+ conn.sendNotification(method, payload);
554
+ }
555
+ catch (err) {
556
+ pendingAceRequests.delete(requestId);
557
+ reject(err);
558
+ }
559
+ });
560
+ }
561
+ function sendAceRequest(conn, method, params, useNotificationResponse) {
562
+ return useNotificationResponse
563
+ ? sendAceRequestAsNotification(conn, method, params)
564
+ : conn.sendRequest(method, params);
565
+ }
566
+ function createAceConnection(proc) {
567
+ if (!proc.stdout || !proc.stdin) {
568
+ throw new Error('aceProcess must have both stdout and stdin streams');
569
+ }
570
+ const conn = (0, node_1.createMessageConnection)(new node_1.StreamMessageReader(proc.stdout), new node_1.StreamMessageWriter(proc.stdin));
571
+ conn.onNotification((method, params) => {
572
+ if (completePendingAceRequest(method, params)) {
573
+ return;
574
+ }
575
+ if (method === ACE_MODULE_INIT_METHOD) {
576
+ if (isModuleInitSuccess(params)) {
577
+ isServerReady = true;
578
+ flushQueues();
579
+ }
580
+ else {
581
+ process.stderr.write('[arkts-lsp] ace server module init reported failure, continue serving requests\n');
582
+ }
583
+ }
584
+ clientConn.sendNotification(method, params);
585
+ });
586
+ conn.onRequest((method, params) => {
587
+ if (method === 'client/registerCapability') {
588
+ return null;
589
+ }
590
+ return clientConn.sendRequest(method, params).catch((error) => {
591
+ process.stderr.write(`[arkts-lsp] client request ${method} failed: ${String(error)}\n`);
592
+ return null;
593
+ });
594
+ });
595
+ conn.onError((error) => {
596
+ process.stderr.write(`[arkts-lsp] ace connection error: ${String(error)}\n`);
597
+ clearQueues(error);
598
+ });
599
+ return conn;
600
+ }
601
+ function scheduleHvigorSync(projectRoot) {
602
+ if (syncPromise) {
603
+ return;
604
+ }
605
+ const config = (0, hvigor_1.parseHvigorSyncConfig)();
606
+ process.stderr.write(`[arkts-lsp] hvigor metadata sync mode=${config.mode}, timeout=${config.timeoutMs}ms\n`);
607
+ syncPromise = (0, hvigor_1.runHvigorSyncAsync)(env, projectRoot, config)
608
+ .then((result) => {
609
+ const before = result.metadataBefore.state;
610
+ const after = result.metadataAfter?.state ?? before;
611
+ process.stderr.write(`[arkts-lsp] hvigor sync ${result.status}; metadata ${before} -> ${after}; elapsed=${result.elapsedMs}ms\n`);
612
+ if (result.errorMessage) {
613
+ process.stderr.write(`[arkts-lsp] hvigor sync detail: ${result.errorMessage}\n`);
614
+ }
615
+ })
616
+ .catch((error) => {
617
+ process.stderr.write(`[arkts-lsp] hvigor sync unexpected error: ${String(error)}\n`);
618
+ })
619
+ .finally(() => {
620
+ syncPromise = null;
621
+ });
622
+ }
623
+ function ensureServer(rootHint) {
624
+ if (bootstrapPromise) {
625
+ return bootstrapPromise;
626
+ }
627
+ bootstrapPromise = (async () => {
628
+ isBootstrapping = true;
629
+ const candidate = explicitProjectRoot
630
+ ? path.resolve(explicitProjectRoot)
631
+ : rootHint
632
+ ? path.resolve(rootHint)
633
+ : process.cwd();
634
+ const resolvedRoot = (0, project_1.findProjectRoot)(candidate);
635
+ if (!resolvedRoot) {
636
+ throw new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InvalidParams, 'No ArkTS project root found in workspace.');
637
+ }
638
+ const parsed = (0, project_1.parseProject)(resolvedRoot, env.sdkPath);
639
+ if (!parsed) {
640
+ throw new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InvalidParams, `Unable to parse build-profile.json5 at ${resolvedRoot}`);
641
+ }
642
+ project = parsed;
643
+ scheduleHvigorSync(parsed.projectRoot);
644
+ const handle = (0, ace_server_1.startAceServer)(env);
645
+ aceHandle = handle;
646
+ const conn = createAceConnection(handle.process);
647
+ aceConn = conn;
648
+ conn.listen();
649
+ handle.onExit((code, signal) => {
650
+ if (disposed) {
651
+ return;
652
+ }
653
+ process.stderr.write(`[arkts-lsp] ace-server exited (code=${code}, signal=${signal})\n`);
654
+ clearQueues(new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InternalError, 'ace-server exited unexpectedly'));
655
+ });
656
+ return conn;
657
+ })();
658
+ const runningPromise = bootstrapPromise;
659
+ runningPromise.finally(() => {
660
+ isBootstrapping = false;
661
+ if (bootstrapPromise === runningPromise) {
662
+ bootstrapPromise = null;
663
+ }
664
+ });
665
+ return runningPromise;
666
+ }
667
+ function resolveInitializeRequest(params) {
668
+ if (initializePromise) {
669
+ return initializePromise;
670
+ }
671
+ const initRootHint = inferProjectRootFromInitializeParams(params);
672
+ initializePromise = (async () => {
673
+ const connection = await ensureServer(initRootHint);
674
+ if (!project) {
675
+ throw new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InvalidParams, 'Project not available');
676
+ }
677
+ const payload = (0, project_1.buildInitializationOptions)(project);
678
+ const initMessage = injectInitializationOptions({
679
+ id: 0,
680
+ method: 'initialize',
681
+ params: isPlainObject(params) ? params : {},
682
+ }, payload);
683
+ const initParams = (initMessage.params || {});
684
+ if (getEditorFiles(openFiles).length > 0) {
685
+ initParams.editorFiles = getEditorFiles(openFiles);
686
+ }
687
+ const result = await connection.sendRequest('initialize', initParams);
688
+ isInitialized = true;
689
+ isServerReady = true;
690
+ flushQueues();
691
+ return result;
692
+ })().catch((err) => {
693
+ isInitialized = false;
694
+ clearQueues(err);
695
+ throw err;
696
+ }).finally(() => {
697
+ initializePromise = null;
698
+ });
699
+ return initializePromise;
700
+ }
701
+ function onRequest(method, params) {
702
+ if (method === 'initialize') {
703
+ return resolveInitializeRequest(params);
704
+ }
705
+ if (!isInitialized && !initializePromise) {
706
+ return Promise.reject(new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.ServerNotInitialized, 'Initialize first.'));
707
+ }
708
+ const mapped = mapRequest(method, params, openFiles);
709
+ const targetMethod = mapped?.method ?? method;
710
+ const targetParams = mapped?.params ?? createRequestPayload(params);
711
+ const useNotificationResponse = Boolean(mapped);
712
+ const sendToAce = (conn) => sendAceRequest(conn, targetMethod, targetParams, useNotificationResponse).then((result) => normalizeClientResult(method, result));
713
+ if (!aceConn) {
714
+ if (!initializePromise) {
715
+ return Promise.reject(new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InternalError, 'ace connection is not ready'));
716
+ }
717
+ return queueRequest(() => {
718
+ if (!aceConn) {
719
+ return Promise.reject(new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InternalError, 'ace connection is not ready'));
720
+ }
721
+ return sendToAce(aceConn);
722
+ });
723
+ }
724
+ if (!isServerReady) {
725
+ const activeConn = aceConn;
726
+ if (!activeConn) {
727
+ return Promise.reject(new vscode_jsonrpc_1.ResponseError(vscode_jsonrpc_1.ErrorCodes.InternalError, 'ace connection is not ready'));
728
+ }
729
+ return queueRequest(() => sendToAce(activeConn));
730
+ }
731
+ return sendToAce(aceConn);
732
+ }
733
+ function onNotification(method, params) {
734
+ if (method === 'textDocument/didOpen') {
735
+ const textDocument = isPlainObject(params) ? params.textDocument : undefined;
736
+ const uri = isPlainObject(textDocument) ? textDocument.uri : undefined;
737
+ if (uri && typeof uri === 'string') {
738
+ openFiles.add(uri);
739
+ }
740
+ }
741
+ if (method === 'textDocument/didClose') {
742
+ const textDocument = isPlainObject(params) ? params.textDocument : undefined;
743
+ const uri = isPlainObject(textDocument) ? textDocument.uri : undefined;
744
+ if (uri && typeof uri === 'string') {
745
+ openFiles.delete(uri);
746
+ }
747
+ }
748
+ if (method === 'initialized') {
749
+ const editors = getOpenFileUris(openFiles).map((uri) => ({ uri, selected: true, receivedOpened: false }));
750
+ const payload = {
751
+ ...(isPlainObject(params) ? params : {}),
752
+ editors,
753
+ };
754
+ if (!aceConn) {
755
+ queueNotification('initialized', payload);
756
+ return;
757
+ }
758
+ aceConn.sendNotification('initialized', payload);
759
+ return;
760
+ }
761
+ const mapped = mapNotification(method, params, openFiles);
762
+ const finalMethod = mapped ? mapped.method : method;
763
+ const finalParams = mapped ? mapped.params : createRequestPayload(params);
764
+ if (!isServerReady || !aceConn) {
765
+ queueNotification(finalMethod, finalParams);
766
+ return;
767
+ }
768
+ try {
769
+ aceConn.sendNotification(finalMethod, finalParams);
770
+ }
771
+ catch {
772
+ queueNotification(finalMethod, finalParams);
773
+ }
774
+ }
775
+ clientConn.onRequest((method, params) => {
776
+ return onRequest(method, params);
777
+ });
778
+ clientConn.onNotification((method, params) => {
779
+ onNotification(method, params);
780
+ });
781
+ clientConn.onError((error) => {
782
+ process.stderr.write(`[arkts-lsp] client connection error: ${String(error)}\n`);
783
+ });
784
+ clientConn.listen();
785
+ return {
786
+ dispose: () => {
787
+ if (disposed) {
788
+ return;
789
+ }
790
+ disposed = true;
791
+ clearQueues(new Error('disposed'));
792
+ if (aceHandle) {
793
+ aceHandle.dispose();
794
+ }
795
+ if (bootstrapPromise && isBootstrapping) {
796
+ bootstrapPromise = null;
797
+ }
798
+ clientConn.dispose();
799
+ },
800
+ };
801
+ }
64
802
  //# sourceMappingURL=proxy.js.map