@lokascript/language-server 2.3.1 → 2.4.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/dist/{dist-V7SKUZF5.js → dist-NRPF26M4.js} +2980 -2957
- package/dist/server.js +940 -1245
- package/package.json +3 -5
package/dist/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TextDocumentSyncKind,
|
|
9
9
|
CompletionItemKind,
|
|
10
10
|
MarkupKind,
|
|
11
|
-
DiagnosticSeverity,
|
|
11
|
+
DiagnosticSeverity as DiagnosticSeverity2,
|
|
12
12
|
SymbolKind,
|
|
13
13
|
CodeActionKind,
|
|
14
14
|
DidChangeConfigurationNotification,
|
|
@@ -203,6 +203,42 @@ function extractHyperscriptRegions(content) {
|
|
|
203
203
|
type: "script"
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
+
const templateRegex = /<template\s+[^>]*\bcomponent=["']([^"']+)["'][^>]*>([\s\S]*?)<\/template>/gi;
|
|
207
|
+
while ((match = templateRegex.exec(fullContent)) !== null) {
|
|
208
|
+
const tag = match[1];
|
|
209
|
+
const body = match[2];
|
|
210
|
+
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
211
|
+
const bodyEnd = match.index + match[0].lastIndexOf("</template>");
|
|
212
|
+
const startPos = offsetToPosition(content, bodyStart);
|
|
213
|
+
const endPos = offsetToPosition(content, bodyEnd);
|
|
214
|
+
regions.push({
|
|
215
|
+
code: body,
|
|
216
|
+
startLine: startPos.line,
|
|
217
|
+
startChar: startPos.character,
|
|
218
|
+
endLine: endPos.line,
|
|
219
|
+
endChar: endPos.character,
|
|
220
|
+
type: "template",
|
|
221
|
+
componentTag: tag
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
const templateScriptRegex = /<script\s+[^>]*\btype=["']text\/hyperscript-template["'][^>]*\bcomponent=["']([^"']+)["'][^>]*>([\s\S]*?)<\/script>/gi;
|
|
225
|
+
while ((match = templateScriptRegex.exec(fullContent)) !== null) {
|
|
226
|
+
const tag = match[1];
|
|
227
|
+
const body = match[2];
|
|
228
|
+
const bodyStart = match.index + match[0].indexOf(">") + 1;
|
|
229
|
+
const bodyEnd = match.index + match[0].lastIndexOf("</script>");
|
|
230
|
+
const startPos = offsetToPosition(content, bodyStart);
|
|
231
|
+
const endPos = offsetToPosition(content, bodyEnd);
|
|
232
|
+
regions.push({
|
|
233
|
+
code: body,
|
|
234
|
+
startLine: startPos.line,
|
|
235
|
+
startChar: startPos.character,
|
|
236
|
+
endLine: endPos.line,
|
|
237
|
+
endChar: endPos.character,
|
|
238
|
+
type: "template",
|
|
239
|
+
componentTag: tag
|
|
240
|
+
});
|
|
241
|
+
}
|
|
206
242
|
return regions;
|
|
207
243
|
}
|
|
208
244
|
function offsetToPosition(content, offset) {
|
|
@@ -243,20 +279,6 @@ function findRegionAtPosition(regions, line, character) {
|
|
|
243
279
|
}
|
|
244
280
|
return null;
|
|
245
281
|
}
|
|
246
|
-
function findLineInRegion(code, offset) {
|
|
247
|
-
let line = 0;
|
|
248
|
-
for (let i = 0; i < offset && i < code.length; i++) {
|
|
249
|
-
if (code[i] === "\n") line++;
|
|
250
|
-
}
|
|
251
|
-
return line;
|
|
252
|
-
}
|
|
253
|
-
function findCharInLine(code, offset) {
|
|
254
|
-
let lastNewline = -1;
|
|
255
|
-
for (let i = 0; i < offset && i < code.length; i++) {
|
|
256
|
-
if (code[i] === "\n") lastNewline = i;
|
|
257
|
-
}
|
|
258
|
-
return offset - lastNewline - 1;
|
|
259
|
-
}
|
|
260
282
|
|
|
261
283
|
// src/utils.ts
|
|
262
284
|
function getWordAtPosition(line, character) {
|
|
@@ -275,9 +297,6 @@ function getWordAtPosition(line, character) {
|
|
|
275
297
|
if (start === end) return null;
|
|
276
298
|
return { text: line.slice(start, end), start, end };
|
|
277
299
|
}
|
|
278
|
-
function escapeRegExp(str) {
|
|
279
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
280
|
-
}
|
|
281
300
|
function findNextNonEmptyLine(lines, startIndex) {
|
|
282
301
|
for (let i = startIndex; i < lines.length; i++) {
|
|
283
302
|
const trimmed = lines[i].trim();
|
|
@@ -322,953 +341,466 @@ function formatHyperscript(code, indentStr = " ") {
|
|
|
322
341
|
return formattedLines.join("\n");
|
|
323
342
|
}
|
|
324
343
|
|
|
325
|
-
// src/
|
|
326
|
-
import {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
var Kw_if = createToken({ name: "Kw_if", pattern: /if/, longer_alt: Identifier });
|
|
371
|
-
var Kw_to = createToken({ name: "Kw_to", pattern: /to/, longer_alt: Identifier });
|
|
372
|
-
var Kw_in = createToken({ name: "Kw_in", pattern: /in/, longer_alt: Identifier });
|
|
373
|
-
var Kw_of = createToken({ name: "Kw_of", pattern: /of/, longer_alt: Identifier });
|
|
374
|
-
var Kw_as = createToken({ name: "Kw_as", pattern: /as/, longer_alt: Identifier });
|
|
375
|
-
var Kw_at = createToken({ name: "Kw_at", pattern: /at/, longer_alt: Identifier });
|
|
376
|
-
var Kw_by = createToken({ name: "Kw_by", pattern: /by/, longer_alt: Identifier });
|
|
377
|
-
var Kw_or = createToken({ name: "Kw_or", pattern: /or/, longer_alt: Identifier });
|
|
378
|
-
var Kw_is = createToken({ name: "Kw_is", pattern: /is/, longer_alt: Identifier });
|
|
379
|
-
var Kw_me = createToken({ name: "Kw_me", pattern: /me/, longer_alt: Identifier });
|
|
380
|
-
var Kw_it = createToken({ name: "Kw_it", pattern: /it/, longer_alt: Identifier });
|
|
381
|
-
var Kw_my = createToken({ name: "Kw_my", pattern: /my/, longer_alt: Identifier });
|
|
382
|
-
var Kw_no = createToken({ name: "Kw_no", pattern: /no/, longer_alt: Identifier });
|
|
383
|
-
var Cmd_transition = createToken({ name: "Cmd_transition", pattern: /transition/, longer_alt: Identifier });
|
|
384
|
-
var Cmd_show = createToken({ name: "Cmd_show", pattern: /show/, longer_alt: Identifier });
|
|
385
|
-
var Cmd_hide = createToken({ name: "Cmd_hide", pattern: /hide/, longer_alt: Identifier });
|
|
386
|
-
var Cmd_increment = createToken({ name: "Cmd_increment", pattern: /increment/, longer_alt: Identifier });
|
|
387
|
-
var Cmd_decrement = createToken({ name: "Cmd_decrement", pattern: /decrement/, longer_alt: Identifier });
|
|
388
|
-
var Cmd_default = createToken({ name: "Cmd_default", pattern: /default/, longer_alt: Identifier });
|
|
389
|
-
var Cmd_beep = createToken({ name: "Cmd_beep", pattern: /beep/, longer_alt: Identifier });
|
|
390
|
-
var Cmd_pick = createToken({ name: "Cmd_pick", pattern: /pick/, longer_alt: Identifier });
|
|
391
|
-
var Cmd_set = createToken({ name: "Cmd_set", pattern: /set/, longer_alt: Identifier });
|
|
392
|
-
var Cmd_log = createToken({ name: "Cmd_log", pattern: /log/, longer_alt: Identifier });
|
|
393
|
-
var Cmd_get = createToken({ name: "Cmd_get", pattern: /get/, longer_alt: Identifier });
|
|
394
|
-
var Cmd_continue = createToken({ name: "Cmd_continue", pattern: /continue/, longer_alt: Identifier });
|
|
395
|
-
var Cmd_behavior = createToken({ name: "Cmd_behavior", pattern: /behavior/, longer_alt: Identifier });
|
|
396
|
-
var Cmd_compound = createToken({ name: "Cmd_compound", pattern: /compound/, longer_alt: Identifier });
|
|
397
|
-
var Cmd_install = createToken({ name: "Cmd_install", pattern: /install/, longer_alt: Identifier });
|
|
398
|
-
var Cmd_unless = createToken({ name: "Cmd_unless", pattern: /unless/, longer_alt: Identifier });
|
|
399
|
-
var Cmd_repeat = createToken({ name: "Cmd_repeat", pattern: /repeat/, longer_alt: Identifier });
|
|
400
|
-
var Cmd_return = createToken({ name: "Cmd_return", pattern: /return/, longer_alt: Identifier });
|
|
401
|
-
var Cmd_throw = createToken({ name: "Cmd_throw", pattern: /throw/, longer_alt: Identifier });
|
|
402
|
-
var Cmd_break = createToken({ name: "Cmd_break", pattern: /break/, longer_alt: Identifier });
|
|
403
|
-
var Cmd_halt = createToken({ name: "Cmd_halt", pattern: /halt/, longer_alt: Identifier });
|
|
404
|
-
var Cmd_call = createToken({ name: "Cmd_call", pattern: /call/, longer_alt: Identifier });
|
|
405
|
-
var Cmd_tell = createToken({ name: "Cmd_tell", pattern: /tell/, longer_alt: Identifier });
|
|
406
|
-
var Cmd_init = createToken({ name: "Cmd_init", pattern: /init/, longer_alt: Identifier });
|
|
407
|
-
var Cmd_exit = createToken({ name: "Cmd_exit", pattern: /exit/, longer_alt: Identifier });
|
|
408
|
-
var Cmd_js = createToken({ name: "Cmd_js", pattern: /js/, longer_alt: Identifier });
|
|
409
|
-
var Cmd_trigger = createToken({ name: "Cmd_trigger", pattern: /trigger/, longer_alt: Identifier });
|
|
410
|
-
var Cmd_send = createToken({ name: "Cmd_send", pattern: /send/, longer_alt: Identifier });
|
|
411
|
-
var Cmd_prepend = createToken({ name: "Cmd_prepend", pattern: /prepend/, longer_alt: Identifier });
|
|
412
|
-
var Cmd_measure = createToken({ name: "Cmd_measure", pattern: /measure/, longer_alt: Identifier });
|
|
413
|
-
var Cmd_append = createToken({ name: "Cmd_append", pattern: /append/, longer_alt: Identifier });
|
|
414
|
-
var Cmd_render = createToken({ name: "Cmd_render", pattern: /render/, longer_alt: Identifier });
|
|
415
|
-
var Cmd_clone = createToken({ name: "Cmd_clone", pattern: /clone/, longer_alt: Identifier });
|
|
416
|
-
var Cmd_focus = createToken({ name: "Cmd_focus", pattern: /focus/, longer_alt: Identifier });
|
|
417
|
-
var Cmd_morph = createToken({ name: "Cmd_morph", pattern: /morph/, longer_alt: Identifier });
|
|
418
|
-
var Cmd_take = createToken({ name: "Cmd_take", pattern: /take/, longer_alt: Identifier });
|
|
419
|
-
var Cmd_make = createToken({ name: "Cmd_make", pattern: /make/, longer_alt: Identifier });
|
|
420
|
-
var Cmd_blur = createToken({ name: "Cmd_blur", pattern: /blur/, longer_alt: Identifier });
|
|
421
|
-
var Cmd_swap = createToken({ name: "Cmd_swap", pattern: /swap/, longer_alt: Identifier });
|
|
422
|
-
var Cmd_copy = createToken({ name: "Cmd_copy", pattern: /copy/, longer_alt: Identifier });
|
|
423
|
-
var Cmd_put = createToken({ name: "Cmd_put", pattern: /put/, longer_alt: Identifier });
|
|
424
|
-
var Cmd_toggle = createToken({ name: "Cmd_toggle", pattern: /toggle/, longer_alt: Identifier });
|
|
425
|
-
var Cmd_remove = createToken({ name: "Cmd_remove", pattern: /remove/, longer_alt: Identifier });
|
|
426
|
-
var Cmd_add = createToken({ name: "Cmd_add", pattern: /add/, longer_alt: Identifier });
|
|
427
|
-
var Cmd_settle = createToken({ name: "Cmd_settle", pattern: /settle/, longer_alt: Identifier });
|
|
428
|
-
var Cmd_fetch = createToken({ name: "Cmd_fetch", pattern: /fetch/, longer_alt: Identifier });
|
|
429
|
-
var Cmd_async = createToken({ name: "Cmd_async", pattern: /async/, longer_alt: Identifier });
|
|
430
|
-
var Cmd_wait = createToken({ name: "Cmd_wait", pattern: /wait/, longer_alt: Identifier });
|
|
431
|
-
var Cmd_go = createToken({ name: "Cmd_go", pattern: /go/, longer_alt: Identifier });
|
|
432
|
-
var Event_intersection = createToken({ name: "Event_intersection", pattern: /intersection/, longer_alt: Identifier });
|
|
433
|
-
var Event_mouseenter = createToken({ name: "Event_mouseenter", pattern: /mouseenter/, longer_alt: Identifier });
|
|
434
|
-
var Event_mouseleave = createToken({ name: "Event_mouseleave", pattern: /mouseleave/, longer_alt: Identifier });
|
|
435
|
-
var Event_mousedown = createToken({ name: "Event_mousedown", pattern: /mousedown/, longer_alt: Identifier });
|
|
436
|
-
var Event_mousemove = createToken({ name: "Event_mousemove", pattern: /mousemove/, longer_alt: Identifier });
|
|
437
|
-
var Event_dblclick = createToken({ name: "Event_dblclick", pattern: /dblclick/, longer_alt: Identifier });
|
|
438
|
-
var Event_keypress = createToken({ name: "Event_keypress", pattern: /keypress/, longer_alt: Identifier });
|
|
439
|
-
var Event_mutation = createToken({ name: "Event_mutation", pattern: /mutation/, longer_alt: Identifier });
|
|
440
|
-
var Event_mouseup = createToken({ name: "Event_mouseup", pattern: /mouseup/, longer_alt: Identifier });
|
|
441
|
-
var Event_keydown = createToken({ name: "Event_keydown", pattern: /keydown/, longer_alt: Identifier });
|
|
442
|
-
var Event_change = createToken({ name: "Event_change", pattern: /change/, longer_alt: Identifier });
|
|
443
|
-
var Event_submit = createToken({ name: "Event_submit", pattern: /submit/, longer_alt: Identifier });
|
|
444
|
-
var Event_scroll = createToken({ name: "Event_scroll", pattern: /scroll/, longer_alt: Identifier });
|
|
445
|
-
var Event_resize = createToken({ name: "Event_resize", pattern: /resize/, longer_alt: Identifier });
|
|
446
|
-
var Event_click = createToken({ name: "Event_click", pattern: /click/, longer_alt: Identifier });
|
|
447
|
-
var Event_keyup = createToken({ name: "Event_keyup", pattern: /keyup/, longer_alt: Identifier });
|
|
448
|
-
var Event_input = createToken({ name: "Event_input", pattern: /input/, longer_alt: Identifier });
|
|
449
|
-
var Event_every = createToken({ name: "Event_every", pattern: /every/, longer_alt: Identifier });
|
|
450
|
-
var Event_load = createToken({ name: "Event_load", pattern: /load/, longer_alt: Identifier });
|
|
451
|
-
var allTokens = [
|
|
452
|
-
// Whitespace
|
|
453
|
-
WhiteSpace,
|
|
454
|
-
// Literals
|
|
455
|
-
StringLiteral,
|
|
456
|
-
NumberLiteral,
|
|
457
|
-
URLLiteral,
|
|
458
|
-
// Selectors
|
|
459
|
-
CSSSelector,
|
|
460
|
-
AttributeSelector,
|
|
461
|
-
HTMLSelector,
|
|
462
|
-
// Variables
|
|
463
|
-
LocalVariable,
|
|
464
|
-
GlobalVariable,
|
|
465
|
-
// Punctuation
|
|
466
|
-
LParen,
|
|
467
|
-
RParen,
|
|
468
|
-
Comma,
|
|
469
|
-
Dot,
|
|
470
|
-
// Event names
|
|
471
|
-
Event_intersection,
|
|
472
|
-
Event_mouseenter,
|
|
473
|
-
Event_mouseleave,
|
|
474
|
-
Event_mousedown,
|
|
475
|
-
Event_mousemove,
|
|
476
|
-
Event_dblclick,
|
|
477
|
-
Event_keypress,
|
|
478
|
-
Event_mutation,
|
|
479
|
-
Event_mouseup,
|
|
480
|
-
Event_keydown,
|
|
481
|
-
Event_change,
|
|
482
|
-
Event_submit,
|
|
483
|
-
Event_scroll,
|
|
484
|
-
Event_resize,
|
|
485
|
-
Event_click,
|
|
486
|
-
Event_keyup,
|
|
487
|
-
Event_input,
|
|
488
|
-
Event_every,
|
|
489
|
-
Event_load,
|
|
490
|
-
// Command keywords
|
|
491
|
-
Cmd_transition,
|
|
492
|
-
Cmd_increment,
|
|
493
|
-
Cmd_decrement,
|
|
494
|
-
Cmd_continue,
|
|
495
|
-
Cmd_behavior,
|
|
496
|
-
Cmd_compound,
|
|
497
|
-
Cmd_trigger,
|
|
498
|
-
Cmd_prepend,
|
|
499
|
-
Cmd_default,
|
|
500
|
-
Cmd_install,
|
|
501
|
-
Cmd_measure,
|
|
502
|
-
Cmd_toggle,
|
|
503
|
-
Cmd_remove,
|
|
504
|
-
Cmd_append,
|
|
505
|
-
Cmd_settle,
|
|
506
|
-
Cmd_unless,
|
|
507
|
-
Cmd_repeat,
|
|
508
|
-
Cmd_return,
|
|
509
|
-
Cmd_render,
|
|
510
|
-
Cmd_fetch,
|
|
511
|
-
Cmd_throw,
|
|
512
|
-
Cmd_clone,
|
|
513
|
-
Cmd_focus,
|
|
514
|
-
Cmd_async,
|
|
515
|
-
Cmd_morph,
|
|
516
|
-
Cmd_break,
|
|
517
|
-
Cmd_show,
|
|
518
|
-
Cmd_hide,
|
|
519
|
-
Cmd_wait,
|
|
520
|
-
Cmd_take,
|
|
521
|
-
Cmd_make,
|
|
522
|
-
Cmd_halt,
|
|
523
|
-
Cmd_send,
|
|
524
|
-
Cmd_blur,
|
|
525
|
-
Cmd_call,
|
|
526
|
-
Cmd_tell,
|
|
527
|
-
Cmd_init,
|
|
528
|
-
Cmd_swap,
|
|
529
|
-
Cmd_beep,
|
|
530
|
-
Cmd_copy,
|
|
531
|
-
Cmd_exit,
|
|
532
|
-
Cmd_pick,
|
|
533
|
-
Cmd_add,
|
|
534
|
-
Cmd_put,
|
|
535
|
-
Cmd_set,
|
|
536
|
-
Cmd_log,
|
|
537
|
-
Cmd_get,
|
|
538
|
-
Cmd_go,
|
|
539
|
-
Cmd_js,
|
|
540
|
-
// Structural keywords
|
|
541
|
-
Kw_previous,
|
|
542
|
-
Kw_closest,
|
|
543
|
-
Kw_while,
|
|
544
|
-
Kw_until,
|
|
545
|
-
Kw_times,
|
|
546
|
-
Kw_first,
|
|
547
|
-
Kw_false,
|
|
548
|
-
Kw_then,
|
|
549
|
-
Kw_else,
|
|
550
|
-
Kw_from,
|
|
551
|
-
Kw_into,
|
|
552
|
-
Kw_with,
|
|
553
|
-
Kw_have,
|
|
554
|
-
Kw_last,
|
|
555
|
-
Kw_next,
|
|
556
|
-
Kw_true,
|
|
557
|
-
Kw_null,
|
|
558
|
-
Kw_end,
|
|
559
|
-
Kw_for,
|
|
560
|
-
Kw_and,
|
|
561
|
-
Kw_not,
|
|
562
|
-
Kw_has,
|
|
563
|
-
Kw_you,
|
|
564
|
-
Kw_its,
|
|
565
|
-
Kw_the,
|
|
566
|
-
Kw_on,
|
|
567
|
-
Kw_if,
|
|
568
|
-
Kw_to,
|
|
569
|
-
Kw_in,
|
|
570
|
-
Kw_of,
|
|
571
|
-
Kw_as,
|
|
572
|
-
Kw_at,
|
|
573
|
-
Kw_by,
|
|
574
|
-
Kw_or,
|
|
575
|
-
Kw_is,
|
|
576
|
-
Kw_me,
|
|
577
|
-
Kw_it,
|
|
578
|
-
Kw_my,
|
|
579
|
-
Kw_no,
|
|
580
|
-
// Identifier (catch-all)
|
|
581
|
-
Identifier
|
|
582
|
-
];
|
|
583
|
-
var hyperscriptLexer = new Lexer(allTokens, {
|
|
584
|
-
positionTracking: "full",
|
|
585
|
-
ensureOptimizations: true
|
|
586
|
-
});
|
|
587
|
-
var COMMAND_SCHEMAS = [
|
|
588
|
-
{ action: "toggle", category: "dom-class", roles: [{ role: "patient", required: true, expectedTypes: ["selector"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
589
|
-
{ action: "add", category: "dom-class", roles: [{ role: "patient", required: true, expectedTypes: ["selector"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
590
|
-
{ action: "remove", category: "dom-class", roles: [{ role: "patient", required: true, expectedTypes: ["selector"] }, { role: "source", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
591
|
-
{ action: "put", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector", "reference", "expression"] }, { role: "destination", required: true, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
592
|
-
{ action: "set", category: "variable", roles: [{ role: "destination", required: true, expectedTypes: ["selector", "reference", "expression"] }, { role: "patient", required: true, expectedTypes: ["literal", "expression", "reference"] }], primaryRole: "destination" },
|
|
593
|
-
{ action: "show", category: "dom-visibility", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference"] }, { role: "style", required: false, expectedTypes: ["literal"] }], primaryRole: "patient" },
|
|
594
|
-
{ action: "hide", category: "dom-visibility", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference"] }, { role: "style", required: false, expectedTypes: ["literal"] }], primaryRole: "patient" },
|
|
595
|
-
{ action: "on", category: "event", roles: [{ role: "event", required: true, expectedTypes: ["literal"] }, { role: "source", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "event" },
|
|
596
|
-
{ action: "trigger", category: "event", roles: [{ role: "event", required: true, expectedTypes: ["literal", "expression"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "event" },
|
|
597
|
-
{ action: "wait", category: "async", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "expression"] }], primaryRole: "patient" },
|
|
598
|
-
{ action: "fetch", category: "async", roles: [{ role: "source", required: true, expectedTypes: ["literal", "expression"] }, { role: "responseType", required: false, expectedTypes: ["literal", "expression"] }, { role: "method", required: false, expectedTypes: ["literal"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "source" },
|
|
599
|
-
{ action: "increment", category: "variable", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference", "expression"] }, { role: "quantity", required: false, expectedTypes: ["literal"] }], primaryRole: "patient" },
|
|
600
|
-
{ action: "decrement", category: "variable", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference", "expression"] }, { role: "quantity", required: false, expectedTypes: ["literal"] }], primaryRole: "patient" },
|
|
601
|
-
{ action: "append", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector", "expression"] }, { role: "destination", required: true, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
602
|
-
{ action: "prepend", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector", "expression"] }, { role: "destination", required: true, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
603
|
-
{ action: "log", category: "variable", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector", "reference", "expression"] }], primaryRole: "patient" },
|
|
604
|
-
{ action: "get", category: "variable", roles: [{ role: "source", required: true, expectedTypes: ["selector", "reference", "expression"] }, { role: "destination", required: false, expectedTypes: ["reference"] }], primaryRole: "source" },
|
|
605
|
-
{ action: "take", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["selector"] }, { role: "source", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
606
|
-
{ action: "make", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector"] }], primaryRole: "patient" },
|
|
607
|
-
{ action: "halt", category: "control-flow", roles: [{ role: "patient", required: false, expectedTypes: ["literal", "reference", "expression"] }], primaryRole: "patient" },
|
|
608
|
-
{ action: "settle", category: "async", roles: [{ role: "patient", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
609
|
-
{ action: "throw", category: "control-flow", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "expression"] }], primaryRole: "patient" },
|
|
610
|
-
{ action: "send", category: "event", roles: [{ role: "event", required: true, expectedTypes: ["literal", "expression"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "event" },
|
|
611
|
-
{ action: "if", category: "control-flow", roles: [{ role: "condition", required: true, expectedTypes: ["expression"] }], primaryRole: "condition" },
|
|
612
|
-
{ action: "unless", category: "control-flow", roles: [{ role: "condition", required: true, expectedTypes: ["expression"] }], primaryRole: "condition" },
|
|
613
|
-
{ action: "else", category: "control-flow", roles: [], primaryRole: "patient" },
|
|
614
|
-
{ action: "repeat", category: "control-flow", roles: [{ role: "loopType", required: true, expectedTypes: ["literal"] }, { role: "quantity", required: false, expectedTypes: ["literal", "expression"] }, { role: "event", required: false, expectedTypes: ["literal", "expression"] }, { role: "source", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "loopType" },
|
|
615
|
-
{ action: "for", category: "control-flow", roles: [{ role: "patient", required: true, expectedTypes: ["reference"] }, { role: "source", required: true, expectedTypes: ["selector", "reference", "expression"] }], primaryRole: "patient" },
|
|
616
|
-
{ action: "while", category: "control-flow", roles: [{ role: "condition", required: true, expectedTypes: ["expression"] }], primaryRole: "condition" },
|
|
617
|
-
{ action: "continue", category: "control-flow", roles: [], primaryRole: "patient" },
|
|
618
|
-
{ action: "go", category: "navigation", roles: [{ role: "destination", required: true, expectedTypes: ["literal", "expression"] }], primaryRole: "destination" },
|
|
619
|
-
{ action: "transition", category: "dom-visibility", roles: [{ role: "patient", required: true, expectedTypes: ["literal"] }, { role: "goal", required: true, expectedTypes: ["literal", "expression"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }, { role: "duration", required: false, expectedTypes: ["literal"] }, { role: "style", required: false, expectedTypes: ["literal"] }], primaryRole: "patient" },
|
|
620
|
-
{ action: "clone", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
621
|
-
{ action: "focus", category: "dom-content", roles: [{ role: "patient", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
622
|
-
{ action: "blur", category: "dom-content", roles: [{ role: "patient", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
623
|
-
{ action: "call", category: "control-flow", roles: [{ role: "patient", required: true, expectedTypes: ["expression", "reference"] }], primaryRole: "patient" },
|
|
624
|
-
{ action: "return", category: "control-flow", roles: [{ role: "patient", required: false, expectedTypes: ["literal", "expression", "reference"] }], primaryRole: "patient" },
|
|
625
|
-
{ action: "js", category: "control-flow", roles: [{ role: "patient", required: false, expectedTypes: ["expression"] }], primaryRole: "patient" },
|
|
626
|
-
{ action: "async", category: "async", roles: [], primaryRole: "patient" },
|
|
627
|
-
{ action: "tell", category: "control-flow", roles: [{ role: "destination", required: true, expectedTypes: ["selector", "reference"] }], primaryRole: "destination" },
|
|
628
|
-
{ action: "default", category: "variable", roles: [{ role: "destination", required: true, expectedTypes: ["reference"] }, { role: "patient", required: true, expectedTypes: ["literal", "expression"] }], primaryRole: "destination" },
|
|
629
|
-
{ action: "init", category: "control-flow", roles: [], primaryRole: "patient" },
|
|
630
|
-
{ action: "behavior", category: "control-flow", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "reference", "expression"] }], primaryRole: "patient" },
|
|
631
|
-
{ action: "install", category: "control-flow", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "reference", "expression"] }, { role: "destination", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
632
|
-
{ action: "measure", category: "dom-content", roles: [{ role: "patient", required: false, expectedTypes: ["literal", "expression"] }, { role: "source", required: false, expectedTypes: ["selector", "reference"] }], primaryRole: "patient" },
|
|
633
|
-
{ action: "swap", category: "dom-content", roles: [{ role: "method", required: false, expectedTypes: ["literal"] }, { role: "destination", required: true, expectedTypes: ["selector", "reference"] }, { role: "patient", required: false, expectedTypes: ["literal", "expression", "selector"] }], primaryRole: "destination" },
|
|
634
|
-
{ action: "morph", category: "dom-content", roles: [{ role: "destination", required: true, expectedTypes: ["selector", "reference"] }, { role: "patient", required: true, expectedTypes: ["literal", "expression", "selector"] }], primaryRole: "destination" },
|
|
635
|
-
{ action: "beep", category: "variable", roles: [{ role: "patient", required: false, expectedTypes: ["literal", "selector", "reference", "expression"] }], primaryRole: "patient" },
|
|
636
|
-
{ action: "break", category: "control-flow", roles: [], primaryRole: "patient" },
|
|
637
|
-
{ action: "copy", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "selector", "reference", "expression"] }], primaryRole: "patient" },
|
|
638
|
-
{ action: "exit", category: "control-flow", roles: [], primaryRole: "patient" },
|
|
639
|
-
{ action: "pick", category: "variable", roles: [{ role: "patient", required: true, expectedTypes: ["literal", "expression", "reference"] }, { role: "source", required: false, expectedTypes: ["reference", "expression"] }], primaryRole: "patient" },
|
|
640
|
-
{ action: "render", category: "dom-content", roles: [{ role: "patient", required: true, expectedTypes: ["selector", "reference", "expression"] }, { role: "style", required: false, expectedTypes: ["expression", "reference"] }], primaryRole: "patient" },
|
|
641
|
-
{ action: "compound", category: "control-flow", roles: [], primaryRole: "patient" }
|
|
642
|
-
];
|
|
643
|
-
|
|
644
|
-
// src/chevrotain-parser.ts
|
|
645
|
-
var HyperscriptParser = class extends CstParser {
|
|
646
|
-
constructor() {
|
|
647
|
-
super(allTokens, {
|
|
648
|
-
recoveryEnabled: true,
|
|
649
|
-
maxLookahead: 3
|
|
650
|
-
});
|
|
651
|
-
this.performSelfAnalysis();
|
|
652
|
-
}
|
|
653
|
-
// -------------------------------------------------------------------------
|
|
654
|
-
// Top-level rules
|
|
655
|
-
// -------------------------------------------------------------------------
|
|
656
|
-
program = this.RULE("program", () => {
|
|
657
|
-
this.MANY(() => {
|
|
658
|
-
this.SUBRULE(this.statement);
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
statement = this.RULE("statement", () => {
|
|
662
|
-
this.OR([
|
|
663
|
-
{ ALT: () => this.SUBRULE(this.eventHandler) },
|
|
664
|
-
{ ALT: () => this.SUBRULE(this.commandChain) },
|
|
665
|
-
{ ALT: () => this.SUBRULE(this.behaviorDef) }
|
|
666
|
-
]);
|
|
667
|
-
});
|
|
668
|
-
// -------------------------------------------------------------------------
|
|
669
|
-
// Event handlers
|
|
670
|
-
// -------------------------------------------------------------------------
|
|
671
|
-
eventHandler = this.RULE("eventHandler", () => {
|
|
672
|
-
this.CONSUME(Kw_on);
|
|
673
|
-
this.SUBRULE(this.eventName);
|
|
674
|
-
this.OPTION(() => {
|
|
675
|
-
this.SUBRULE(this.eventModifiers);
|
|
676
|
-
});
|
|
677
|
-
this.OPTION2(() => {
|
|
678
|
-
this.SUBRULE(this.commandChain);
|
|
679
|
-
});
|
|
680
|
-
this.OPTION3(() => {
|
|
681
|
-
this.CONSUME(Kw_end);
|
|
682
|
-
});
|
|
683
|
-
});
|
|
684
|
-
eventName = this.RULE("eventName", () => {
|
|
685
|
-
this.OR([
|
|
686
|
-
{ ALT: () => this.CONSUME(Event_click) },
|
|
687
|
-
{ ALT: () => this.CONSUME(Event_dblclick) },
|
|
688
|
-
{ ALT: () => this.CONSUME(Event_mousedown) },
|
|
689
|
-
{ ALT: () => this.CONSUME(Event_mouseup) },
|
|
690
|
-
{ ALT: () => this.CONSUME(Event_mousemove) },
|
|
691
|
-
{ ALT: () => this.CONSUME(Event_mouseenter) },
|
|
692
|
-
{ ALT: () => this.CONSUME(Event_mouseleave) },
|
|
693
|
-
{ ALT: () => this.CONSUME(Event_keydown) },
|
|
694
|
-
{ ALT: () => this.CONSUME(Event_keyup) },
|
|
695
|
-
{ ALT: () => this.CONSUME(Event_input) },
|
|
696
|
-
{ ALT: () => this.CONSUME(Event_change) },
|
|
697
|
-
{ ALT: () => this.CONSUME(Event_submit) },
|
|
698
|
-
{ ALT: () => this.CONSUME(Event_load) },
|
|
699
|
-
{ ALT: () => this.CONSUME(Event_scroll) },
|
|
700
|
-
{ ALT: () => this.CONSUME(Event_resize) },
|
|
701
|
-
{ ALT: () => this.CONSUME(Event_mutation) },
|
|
702
|
-
{ ALT: () => this.CONSUME(Event_intersection) },
|
|
703
|
-
{ ALT: () => this.CONSUME(Event_every) },
|
|
704
|
-
{ ALT: () => this.CONSUME(Identifier) }
|
|
705
|
-
// Custom event name
|
|
706
|
-
]);
|
|
707
|
-
});
|
|
708
|
-
eventModifiers = this.RULE("eventModifiers", () => {
|
|
709
|
-
this.MANY(() => {
|
|
710
|
-
this.CONSUME(Dot);
|
|
711
|
-
this.CONSUME(Identifier);
|
|
712
|
-
this.OPTION(() => {
|
|
713
|
-
this.CONSUME(LParen);
|
|
714
|
-
this.SUBRULE(this.expression);
|
|
715
|
-
this.CONSUME(RParen);
|
|
716
|
-
});
|
|
717
|
-
});
|
|
718
|
-
});
|
|
719
|
-
// -------------------------------------------------------------------------
|
|
720
|
-
// Command chain (commands connected by 'then')
|
|
721
|
-
// -------------------------------------------------------------------------
|
|
722
|
-
commandChain = this.RULE("commandChain", () => {
|
|
723
|
-
this.SUBRULE(this.command);
|
|
724
|
-
this.MANY(() => {
|
|
725
|
-
this.CONSUME(Kw_then);
|
|
726
|
-
this.SUBRULE2(this.command);
|
|
727
|
-
});
|
|
728
|
-
});
|
|
729
|
-
// -------------------------------------------------------------------------
|
|
730
|
-
// Commands (grouped by category for error messages)
|
|
731
|
-
// -------------------------------------------------------------------------
|
|
732
|
-
command = this.RULE("command", () => {
|
|
733
|
-
this.OR([
|
|
734
|
-
// DOM class/attribute
|
|
735
|
-
{ ALT: () => this.SUBRULE(this.toggleCommand) },
|
|
736
|
-
{ ALT: () => this.SUBRULE(this.addCommand) },
|
|
737
|
-
{ ALT: () => this.SUBRULE(this.removeCommand) },
|
|
738
|
-
// DOM content
|
|
739
|
-
{ ALT: () => this.SUBRULE(this.putCommand) },
|
|
740
|
-
{ ALT: () => this.SUBRULE(this.setCommand) },
|
|
741
|
-
{ ALT: () => this.SUBRULE(this.appendCommand) },
|
|
742
|
-
{ ALT: () => this.SUBRULE(this.prependCommand) },
|
|
743
|
-
// DOM visibility
|
|
744
|
-
{ ALT: () => this.SUBRULE(this.showCommand) },
|
|
745
|
-
{ ALT: () => this.SUBRULE(this.hideCommand) },
|
|
746
|
-
{ ALT: () => this.SUBRULE(this.transitionCommand) },
|
|
747
|
-
// Variable
|
|
748
|
-
{ ALT: () => this.SUBRULE(this.logCommand) },
|
|
749
|
-
{ ALT: () => this.SUBRULE(this.incrementCommand) },
|
|
750
|
-
{ ALT: () => this.SUBRULE(this.decrementCommand) },
|
|
751
|
-
// Async
|
|
752
|
-
{ ALT: () => this.SUBRULE(this.fetchCommand) },
|
|
753
|
-
{ ALT: () => this.SUBRULE(this.waitCommand) },
|
|
754
|
-
// Event
|
|
755
|
-
{ ALT: () => this.SUBRULE(this.sendCommand) },
|
|
756
|
-
{ ALT: () => this.SUBRULE(this.triggerCommand) },
|
|
757
|
-
// Navigation
|
|
758
|
-
{ ALT: () => this.SUBRULE(this.goCommand) },
|
|
759
|
-
// Control flow
|
|
760
|
-
{ ALT: () => this.SUBRULE(this.ifCommand) },
|
|
761
|
-
{ ALT: () => this.SUBRULE(this.repeatCommand) },
|
|
762
|
-
// Execution
|
|
763
|
-
{ ALT: () => this.SUBRULE(this.callCommand) },
|
|
764
|
-
{ ALT: () => this.SUBRULE(this.jsCommand) },
|
|
765
|
-
{ ALT: () => this.SUBRULE(this.returnCommand) },
|
|
766
|
-
{ ALT: () => this.SUBRULE(this.throwCommand) },
|
|
767
|
-
{ ALT: () => this.SUBRULE(this.haltCommand) }
|
|
768
|
-
]);
|
|
769
|
-
});
|
|
770
|
-
// --- DOM class/attribute ---
|
|
771
|
-
toggleCommand = this.RULE("toggleCommand", () => {
|
|
772
|
-
this.CONSUME(Cmd_toggle);
|
|
773
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
774
|
-
this.OPTION(() => {
|
|
775
|
-
this.CONSUME(Kw_on);
|
|
776
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
777
|
-
});
|
|
778
|
-
});
|
|
779
|
-
addCommand = this.RULE("addCommand", () => {
|
|
780
|
-
this.CONSUME(Cmd_add);
|
|
781
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
782
|
-
this.OPTION(() => {
|
|
783
|
-
this.CONSUME(Kw_to);
|
|
784
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
785
|
-
});
|
|
786
|
-
});
|
|
787
|
-
removeCommand = this.RULE("removeCommand", () => {
|
|
788
|
-
this.CONSUME(Cmd_remove);
|
|
789
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
790
|
-
this.OPTION(() => {
|
|
791
|
-
this.CONSUME(Kw_from);
|
|
792
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
793
|
-
});
|
|
794
|
-
});
|
|
795
|
-
// --- DOM content ---
|
|
796
|
-
putCommand = this.RULE("putCommand", () => {
|
|
797
|
-
this.CONSUME(Cmd_put);
|
|
798
|
-
this.SUBRULE(this.expression);
|
|
799
|
-
this.OPTION(() => {
|
|
800
|
-
this.OR([
|
|
801
|
-
{ ALT: () => this.CONSUME(Kw_into) },
|
|
802
|
-
{ ALT: () => this.CONSUME(Identifier, { LABEL: "putMarker" }) }
|
|
803
|
-
// before/after
|
|
804
|
-
]);
|
|
805
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
806
|
-
});
|
|
807
|
-
});
|
|
808
|
-
setCommand = this.RULE("setCommand", () => {
|
|
809
|
-
this.CONSUME(Cmd_set);
|
|
810
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
811
|
-
this.CONSUME(Kw_to);
|
|
812
|
-
this.SUBRULE2(this.expression);
|
|
813
|
-
});
|
|
814
|
-
appendCommand = this.RULE("appendCommand", () => {
|
|
815
|
-
this.CONSUME(Cmd_append);
|
|
816
|
-
this.SUBRULE(this.expression);
|
|
817
|
-
this.CONSUME(Kw_to);
|
|
818
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
819
|
-
});
|
|
820
|
-
prependCommand = this.RULE("prependCommand", () => {
|
|
821
|
-
this.CONSUME(Cmd_prepend);
|
|
822
|
-
this.SUBRULE(this.expression);
|
|
823
|
-
this.CONSUME(Kw_to);
|
|
824
|
-
this.SUBRULE2(this.selectorOrExpression);
|
|
825
|
-
});
|
|
826
|
-
// --- DOM visibility ---
|
|
827
|
-
showCommand = this.RULE("showCommand", () => {
|
|
828
|
-
this.CONSUME(Cmd_show);
|
|
829
|
-
this.OPTION(() => this.SUBRULE(this.selectorOrExpression));
|
|
830
|
-
});
|
|
831
|
-
hideCommand = this.RULE("hideCommand", () => {
|
|
832
|
-
this.CONSUME(Cmd_hide);
|
|
833
|
-
this.OPTION(() => this.SUBRULE(this.selectorOrExpression));
|
|
834
|
-
});
|
|
835
|
-
transitionCommand = this.RULE("transitionCommand", () => {
|
|
836
|
-
this.CONSUME(Cmd_transition);
|
|
837
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
838
|
-
});
|
|
839
|
-
// --- Variable ---
|
|
840
|
-
logCommand = this.RULE("logCommand", () => {
|
|
841
|
-
this.CONSUME(Cmd_log);
|
|
842
|
-
this.SUBRULE(this.expression);
|
|
843
|
-
});
|
|
844
|
-
incrementCommand = this.RULE("incrementCommand", () => {
|
|
845
|
-
this.CONSUME(Cmd_increment);
|
|
846
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
847
|
-
this.OPTION(() => {
|
|
848
|
-
this.CONSUME(Kw_by);
|
|
849
|
-
this.SUBRULE2(this.expression);
|
|
850
|
-
});
|
|
851
|
-
});
|
|
852
|
-
decrementCommand = this.RULE("decrementCommand", () => {
|
|
853
|
-
this.CONSUME(Cmd_decrement);
|
|
854
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
855
|
-
this.OPTION(() => {
|
|
856
|
-
this.CONSUME(Kw_by);
|
|
857
|
-
this.SUBRULE2(this.expression);
|
|
858
|
-
});
|
|
859
|
-
});
|
|
860
|
-
// --- Async ---
|
|
861
|
-
fetchCommand = this.RULE("fetchCommand", () => {
|
|
862
|
-
this.CONSUME(Cmd_fetch);
|
|
863
|
-
this.SUBRULE(this.expression);
|
|
864
|
-
this.OPTION(() => {
|
|
865
|
-
this.CONSUME(Kw_as);
|
|
866
|
-
this.CONSUME(Identifier, { LABEL: "responseType" });
|
|
867
|
-
});
|
|
868
|
-
});
|
|
869
|
-
waitCommand = this.RULE("waitCommand", () => {
|
|
870
|
-
this.CONSUME(Cmd_wait);
|
|
871
|
-
this.OPTION(() => this.SUBRULE(this.expression));
|
|
872
|
-
});
|
|
873
|
-
// --- Event ---
|
|
874
|
-
sendCommand = this.RULE("sendCommand", () => {
|
|
875
|
-
this.CONSUME(Cmd_send);
|
|
876
|
-
this.CONSUME(Identifier, { LABEL: "eventName" });
|
|
877
|
-
this.OPTION(() => {
|
|
878
|
-
this.CONSUME(Kw_to);
|
|
879
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
880
|
-
});
|
|
881
|
-
});
|
|
882
|
-
triggerCommand = this.RULE("triggerCommand", () => {
|
|
883
|
-
this.CONSUME(Cmd_trigger);
|
|
884
|
-
this.CONSUME(Identifier, { LABEL: "eventName" });
|
|
885
|
-
this.OPTION(() => {
|
|
886
|
-
this.CONSUME(Kw_on);
|
|
887
|
-
this.SUBRULE(this.selectorOrExpression);
|
|
888
|
-
});
|
|
889
|
-
});
|
|
890
|
-
// --- Navigation ---
|
|
891
|
-
goCommand = this.RULE("goCommand", () => {
|
|
892
|
-
this.CONSUME(Cmd_go);
|
|
893
|
-
this.OPTION(() => this.CONSUME(Kw_to));
|
|
894
|
-
this.SUBRULE(this.expression);
|
|
895
|
-
});
|
|
896
|
-
// --- Control flow ---
|
|
897
|
-
ifCommand = this.RULE("ifCommand", () => {
|
|
898
|
-
this.CONSUME(Kw_if);
|
|
899
|
-
this.SUBRULE(this.expression);
|
|
900
|
-
this.OPTION(() => this.SUBRULE(this.commandChain));
|
|
901
|
-
this.OPTION2(() => {
|
|
902
|
-
this.CONSUME(Kw_else);
|
|
903
|
-
this.OPTION3(() => this.SUBRULE2(this.commandChain));
|
|
904
|
-
});
|
|
905
|
-
this.OPTION4(() => this.CONSUME(Kw_end));
|
|
906
|
-
});
|
|
907
|
-
repeatCommand = this.RULE("repeatCommand", () => {
|
|
908
|
-
this.CONSUME(Cmd_repeat);
|
|
909
|
-
this.OR([
|
|
910
|
-
{
|
|
911
|
-
ALT: () => {
|
|
912
|
-
this.SUBRULE(this.expression);
|
|
913
|
-
this.CONSUME(Kw_times);
|
|
914
|
-
}
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
ALT: () => {
|
|
918
|
-
this.CONSUME(Kw_for);
|
|
919
|
-
this.CONSUME(Identifier, { LABEL: "loopVar" });
|
|
920
|
-
this.CONSUME(Kw_in);
|
|
921
|
-
this.SUBRULE2(this.expression);
|
|
922
|
-
}
|
|
923
|
-
},
|
|
924
|
-
{
|
|
925
|
-
ALT: () => {
|
|
926
|
-
this.CONSUME(Kw_while);
|
|
927
|
-
this.SUBRULE3(this.expression);
|
|
928
|
-
}
|
|
929
|
-
},
|
|
930
|
-
{
|
|
931
|
-
ALT: () => {
|
|
932
|
-
this.CONSUME(Kw_until);
|
|
933
|
-
this.SUBRULE4(this.expression);
|
|
344
|
+
// src/simple-diagnostics.ts
|
|
345
|
+
import { DiagnosticSeverity } from "vscode-languageserver/node.js";
|
|
346
|
+
function isPossessive(code, i) {
|
|
347
|
+
if (i === 0) return false;
|
|
348
|
+
const prev = code[i - 1];
|
|
349
|
+
if (!/[\w\-#.]/.test(prev)) return false;
|
|
350
|
+
if (i + 1 >= code.length || code[i + 1] !== "s") return false;
|
|
351
|
+
if (i + 2 >= code.length) return true;
|
|
352
|
+
const after = code[i + 2];
|
|
353
|
+
return /[\s),.\]}|@*]/.test(after);
|
|
354
|
+
}
|
|
355
|
+
function runSimpleDiagnostics(code, _language = "en") {
|
|
356
|
+
const diagnostics = [];
|
|
357
|
+
let state = 0 /* NORMAL */;
|
|
358
|
+
let quoteStartLine = 0;
|
|
359
|
+
let quoteStartChar = 0;
|
|
360
|
+
let parenDepth = 0;
|
|
361
|
+
let bracketDepth = 0;
|
|
362
|
+
let line = 0;
|
|
363
|
+
let col = 0;
|
|
364
|
+
for (let i = 0; i < code.length; i++) {
|
|
365
|
+
const ch = code[i];
|
|
366
|
+
if (ch === "\n") {
|
|
367
|
+
line++;
|
|
368
|
+
col = 0;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (state !== 0 /* NORMAL */) {
|
|
372
|
+
if (ch === "\\") {
|
|
373
|
+
i++;
|
|
374
|
+
col += 2;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (state === 1 /* IN_SINGLE */ && ch === "'" || state === 2 /* IN_DOUBLE */ && ch === '"' || state === 3 /* IN_BACKTICK */ && ch === "`") {
|
|
378
|
+
state = 0 /* NORMAL */;
|
|
379
|
+
}
|
|
380
|
+
col++;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
switch (ch) {
|
|
384
|
+
case "'":
|
|
385
|
+
if (isPossessive(code, i)) {
|
|
386
|
+
i++;
|
|
387
|
+
col += 2;
|
|
388
|
+
continue;
|
|
934
389
|
}
|
|
390
|
+
state = 1 /* IN_SINGLE */;
|
|
391
|
+
quoteStartLine = line;
|
|
392
|
+
quoteStartChar = col;
|
|
393
|
+
break;
|
|
394
|
+
case '"':
|
|
395
|
+
state = 2 /* IN_DOUBLE */;
|
|
396
|
+
quoteStartLine = line;
|
|
397
|
+
quoteStartChar = col;
|
|
398
|
+
break;
|
|
399
|
+
case "`":
|
|
400
|
+
state = 3 /* IN_BACKTICK */;
|
|
401
|
+
quoteStartLine = line;
|
|
402
|
+
quoteStartChar = col;
|
|
403
|
+
break;
|
|
404
|
+
case "(":
|
|
405
|
+
parenDepth++;
|
|
406
|
+
break;
|
|
407
|
+
case ")":
|
|
408
|
+
parenDepth--;
|
|
409
|
+
break;
|
|
410
|
+
case "[":
|
|
411
|
+
bracketDepth++;
|
|
412
|
+
break;
|
|
413
|
+
case "]":
|
|
414
|
+
bracketDepth--;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
col++;
|
|
418
|
+
}
|
|
419
|
+
if (state !== 0 /* NORMAL */) {
|
|
420
|
+
const quoteType = state === 1 /* IN_SINGLE */ ? "single" : state === 2 /* IN_DOUBLE */ ? "double" : "backtick";
|
|
421
|
+
diagnostics.push({
|
|
422
|
+
range: {
|
|
423
|
+
start: { line: quoteStartLine, character: quoteStartChar },
|
|
424
|
+
end: { line: quoteStartLine, character: quoteStartChar + 1 }
|
|
935
425
|
},
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
]);
|
|
941
|
-
this.OPTION(() => this.SUBRULE(this.commandChain));
|
|
942
|
-
this.OPTION2(() => this.CONSUME(Kw_end));
|
|
943
|
-
});
|
|
944
|
-
// --- Execution ---
|
|
945
|
-
callCommand = this.RULE("callCommand", () => {
|
|
946
|
-
this.CONSUME(Cmd_call);
|
|
947
|
-
this.SUBRULE(this.expression);
|
|
948
|
-
});
|
|
949
|
-
jsCommand = this.RULE("jsCommand", () => {
|
|
950
|
-
this.CONSUME(Cmd_js);
|
|
951
|
-
this.MANY(() => {
|
|
952
|
-
this.OR([
|
|
953
|
-
{ ALT: () => this.CONSUME(Identifier) },
|
|
954
|
-
{ ALT: () => this.CONSUME(StringLiteral) },
|
|
955
|
-
{ ALT: () => this.CONSUME(NumberLiteral) },
|
|
956
|
-
{ ALT: () => this.CONSUME(CSSSelector) },
|
|
957
|
-
{ ALT: () => this.CONSUME(LocalVariable) },
|
|
958
|
-
{ ALT: () => this.CONSUME(LParen) },
|
|
959
|
-
{ ALT: () => this.CONSUME(RParen) },
|
|
960
|
-
{ ALT: () => this.CONSUME(Comma) },
|
|
961
|
-
{ ALT: () => this.CONSUME(Dot) }
|
|
962
|
-
]);
|
|
963
|
-
});
|
|
964
|
-
this.CONSUME(Kw_end);
|
|
965
|
-
});
|
|
966
|
-
returnCommand = this.RULE("returnCommand", () => {
|
|
967
|
-
this.CONSUME(Cmd_return);
|
|
968
|
-
this.OPTION(() => this.SUBRULE(this.expression));
|
|
969
|
-
});
|
|
970
|
-
throwCommand = this.RULE("throwCommand", () => {
|
|
971
|
-
this.CONSUME(Cmd_throw);
|
|
972
|
-
this.SUBRULE(this.expression);
|
|
973
|
-
});
|
|
974
|
-
haltCommand = this.RULE("haltCommand", () => {
|
|
975
|
-
this.CONSUME(Cmd_halt);
|
|
976
|
-
});
|
|
977
|
-
// -------------------------------------------------------------------------
|
|
978
|
-
// Behavior definitions
|
|
979
|
-
// -------------------------------------------------------------------------
|
|
980
|
-
behaviorDef = this.RULE("behaviorDef", () => {
|
|
981
|
-
this.CONSUME(Cmd_behavior);
|
|
982
|
-
this.CONSUME(Identifier, { LABEL: "behaviorName" });
|
|
983
|
-
this.OPTION(() => {
|
|
984
|
-
this.CONSUME(LParen);
|
|
985
|
-
this.AT_LEAST_ONE_SEP({
|
|
986
|
-
SEP: Comma,
|
|
987
|
-
DEF: () => this.CONSUME2(Identifier, { LABEL: "param" })
|
|
988
|
-
});
|
|
989
|
-
this.CONSUME(RParen);
|
|
426
|
+
severity: DiagnosticSeverity.Error,
|
|
427
|
+
code: "unmatched-quote",
|
|
428
|
+
source: "lokascript",
|
|
429
|
+
message: `Unmatched ${quoteType} quote`
|
|
990
430
|
});
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
this.MANY(() => {
|
|
1000
|
-
this.OR([
|
|
1001
|
-
{
|
|
1002
|
-
ALT: () => {
|
|
1003
|
-
this.OR2([
|
|
1004
|
-
{ ALT: () => this.CONSUME(Kw_is) },
|
|
1005
|
-
{ ALT: () => this.CONSUME(Kw_and) },
|
|
1006
|
-
{ ALT: () => this.CONSUME(Kw_or) }
|
|
1007
|
-
]);
|
|
1008
|
-
this.SUBRULE2(this.primaryExpression);
|
|
1009
|
-
}
|
|
1010
|
-
},
|
|
1011
|
-
{
|
|
1012
|
-
ALT: () => {
|
|
1013
|
-
this.CONSUME(Dot);
|
|
1014
|
-
this.CONSUME(Identifier);
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
]);
|
|
431
|
+
}
|
|
432
|
+
if (parenDepth !== 0) {
|
|
433
|
+
diagnostics.push({
|
|
434
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
|
|
435
|
+
severity: DiagnosticSeverity.Error,
|
|
436
|
+
code: "unbalanced-parens",
|
|
437
|
+
source: "lokascript",
|
|
438
|
+
message: `Unbalanced parentheses`
|
|
1018
439
|
});
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
440
|
+
}
|
|
441
|
+
if (bracketDepth !== 0) {
|
|
442
|
+
diagnostics.push({
|
|
443
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
|
|
444
|
+
severity: DiagnosticSeverity.Error,
|
|
445
|
+
code: "unbalanced-brackets",
|
|
446
|
+
source: "lokascript",
|
|
447
|
+
message: `Unbalanced brackets`
|
|
1027
448
|
});
|
|
1028
|
-
this.OR2([
|
|
1029
|
-
{ ALT: () => this.CONSUME(StringLiteral) },
|
|
1030
|
-
{ ALT: () => this.CONSUME(NumberLiteral) },
|
|
1031
|
-
{ ALT: () => this.CONSUME(CSSSelector) },
|
|
1032
|
-
{ ALT: () => this.CONSUME(AttributeSelector) },
|
|
1033
|
-
{ ALT: () => this.CONSUME(HTMLSelector) },
|
|
1034
|
-
{ ALT: () => this.CONSUME(LocalVariable) },
|
|
1035
|
-
{ ALT: () => this.CONSUME(GlobalVariable) },
|
|
1036
|
-
{ ALT: () => this.CONSUME(Kw_me) },
|
|
1037
|
-
{ ALT: () => this.CONSUME(Kw_it) },
|
|
1038
|
-
{ ALT: () => this.CONSUME(Kw_true) },
|
|
1039
|
-
{ ALT: () => this.CONSUME(Kw_false) },
|
|
1040
|
-
{ ALT: () => this.CONSUME(Identifier) },
|
|
1041
|
-
{
|
|
1042
|
-
ALT: () => {
|
|
1043
|
-
this.CONSUME(LParen);
|
|
1044
|
-
this.SUBRULE(this.expression);
|
|
1045
|
-
this.CONSUME(RParen);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
]);
|
|
1049
|
-
});
|
|
1050
|
-
// -------------------------------------------------------------------------
|
|
1051
|
-
// Selectors or expressions
|
|
1052
|
-
// -------------------------------------------------------------------------
|
|
1053
|
-
selectorOrExpression = this.RULE("selectorOrExpression", () => {
|
|
1054
|
-
this.OR([
|
|
1055
|
-
{ ALT: () => this.CONSUME(CSSSelector) },
|
|
1056
|
-
{ ALT: () => this.CONSUME(AttributeSelector) },
|
|
1057
|
-
{ ALT: () => this.CONSUME(HTMLSelector) },
|
|
1058
|
-
{ ALT: () => this.CONSUME(Kw_me) },
|
|
1059
|
-
{ ALT: () => this.CONSUME(Kw_it) },
|
|
1060
|
-
{ ALT: () => this.CONSUME(LocalVariable) },
|
|
1061
|
-
{ ALT: () => this.CONSUME(GlobalVariable) },
|
|
1062
|
-
{ ALT: () => this.CONSUME(Identifier) }
|
|
1063
|
-
]);
|
|
1064
|
-
});
|
|
1065
|
-
};
|
|
1066
|
-
var parserInstance = new HyperscriptParser();
|
|
1067
|
-
function parseWithRecovery(code) {
|
|
1068
|
-
const lexResult = hyperscriptLexer.tokenize(code);
|
|
1069
|
-
const diagnostics = lexResult.errors.map((err) => ({
|
|
1070
|
-
message: err.message,
|
|
1071
|
-
severity: "error",
|
|
1072
|
-
startOffset: err.offset,
|
|
1073
|
-
endOffset: err.offset + (err.length || 1),
|
|
1074
|
-
startLine: err.line ?? 1,
|
|
1075
|
-
startColumn: err.column ?? 1,
|
|
1076
|
-
code: "lex-error"
|
|
1077
|
-
}));
|
|
1078
|
-
const nonWS = lexResult.tokens.filter((t) => t.tokenType !== WhiteSpace);
|
|
1079
|
-
parserInstance.input = nonWS;
|
|
1080
|
-
const cst = parserInstance.program();
|
|
1081
|
-
for (const err of parserInstance.errors) {
|
|
1082
|
-
diagnostics.push(recognitionExceptionToDiagnostic(err));
|
|
1083
449
|
}
|
|
1084
|
-
return
|
|
1085
|
-
cst,
|
|
1086
|
-
diagnostics,
|
|
1087
|
-
tokens: lexResult.tokens
|
|
1088
|
-
};
|
|
450
|
+
return diagnostics;
|
|
1089
451
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
452
|
+
var DIRECTIVE_IF = /^#if\b/;
|
|
453
|
+
var DIRECTIVE_FOR = /^#for\b/;
|
|
454
|
+
var DIRECTIVE_ELSE = /^#else\s*$/;
|
|
455
|
+
var DIRECTIVE_END = /^#end\s*$/;
|
|
456
|
+
var DIRECTIVE_CONTINUE = /^#continue\s*$/;
|
|
457
|
+
var DIRECTIVE_FOR_FORM = /^#for\s+([A-Za-z_$][\w$]*)\s+in\s+.+$/;
|
|
458
|
+
function runDirectiveDiagnostics(code) {
|
|
459
|
+
const diagnostics = [];
|
|
460
|
+
const lines = code.split("\n");
|
|
461
|
+
const stack = [];
|
|
462
|
+
for (let i = 0; i < lines.length; i++) {
|
|
463
|
+
const raw = lines[i];
|
|
464
|
+
const trimmed = raw.trim();
|
|
465
|
+
if (!trimmed.startsWith("#")) continue;
|
|
466
|
+
const indent = raw.indexOf(trimmed.charAt(0));
|
|
467
|
+
const range = {
|
|
468
|
+
start: { line: i, character: Math.max(0, indent) },
|
|
469
|
+
end: { line: i, character: raw.length }
|
|
470
|
+
};
|
|
471
|
+
if (DIRECTIVE_IF.test(trimmed)) {
|
|
472
|
+
stack.push({ kind: "if", line: i, hasElse: false });
|
|
473
|
+
} else if (DIRECTIVE_FOR.test(trimmed)) {
|
|
474
|
+
if (!DIRECTIVE_FOR_FORM.test(trimmed)) {
|
|
475
|
+
diagnostics.push({
|
|
476
|
+
range,
|
|
477
|
+
severity: DiagnosticSeverity.Error,
|
|
478
|
+
code: "directive-malformed-for",
|
|
479
|
+
source: "lokascript",
|
|
480
|
+
message: "#for must be `#for <name> in <expr>`"
|
|
481
|
+
});
|
|
1113
482
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
483
|
+
stack.push({ kind: "for", line: i, hasElse: false });
|
|
484
|
+
} else if (DIRECTIVE_ELSE.test(trimmed)) {
|
|
485
|
+
const top = stack[stack.length - 1];
|
|
486
|
+
if (!top) {
|
|
487
|
+
diagnostics.push({
|
|
488
|
+
range,
|
|
489
|
+
severity: DiagnosticSeverity.Error,
|
|
490
|
+
code: "directive-stray-else",
|
|
491
|
+
source: "lokascript",
|
|
492
|
+
message: "#else outside of #if or #for block"
|
|
493
|
+
});
|
|
494
|
+
} else if (top.hasElse) {
|
|
495
|
+
diagnostics.push({
|
|
496
|
+
range,
|
|
497
|
+
severity: DiagnosticSeverity.Error,
|
|
498
|
+
code: "directive-duplicate-else",
|
|
499
|
+
source: "lokascript",
|
|
500
|
+
message: "duplicate #else in this block"
|
|
501
|
+
});
|
|
502
|
+
} else {
|
|
503
|
+
top.hasElse = true;
|
|
504
|
+
}
|
|
505
|
+
} else if (DIRECTIVE_END.test(trimmed)) {
|
|
506
|
+
if (stack.length === 0) {
|
|
507
|
+
diagnostics.push({
|
|
508
|
+
range,
|
|
509
|
+
severity: DiagnosticSeverity.Error,
|
|
510
|
+
code: "directive-stray-end",
|
|
511
|
+
source: "lokascript",
|
|
512
|
+
message: "#end without matching #if or #for"
|
|
513
|
+
});
|
|
514
|
+
} else {
|
|
515
|
+
stack.pop();
|
|
516
|
+
}
|
|
517
|
+
} else if (DIRECTIVE_CONTINUE.test(trimmed)) {
|
|
518
|
+
const inFor = stack.some((b) => b.kind === "for");
|
|
519
|
+
if (!inFor) {
|
|
520
|
+
diagnostics.push({
|
|
521
|
+
range,
|
|
522
|
+
severity: DiagnosticSeverity.Error,
|
|
523
|
+
code: "directive-stray-continue",
|
|
524
|
+
source: "lokascript",
|
|
525
|
+
message: "#continue outside of #for block"
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
1122
529
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const names = m.slice(1, -1).split(",").map((n) => n.trim());
|
|
1134
|
-
return names.slice(0, 5).join(", ") + (names.length > 5 ? "..." : "");
|
|
530
|
+
for (const open of stack) {
|
|
531
|
+
diagnostics.push({
|
|
532
|
+
range: {
|
|
533
|
+
start: { line: open.line, character: 0 },
|
|
534
|
+
end: { line: open.line, character: lines[open.line]?.length ?? 0 }
|
|
535
|
+
},
|
|
536
|
+
severity: DiagnosticSeverity.Error,
|
|
537
|
+
code: "directive-unclosed",
|
|
538
|
+
source: "lokascript",
|
|
539
|
+
message: `Unclosed #${open.kind} \u2014 missing #end`
|
|
1135
540
|
});
|
|
1136
|
-
message = `Expected ${expected}`;
|
|
1137
|
-
if (token.image) {
|
|
1138
|
-
message += ` but found '${token.image}'`;
|
|
1139
|
-
}
|
|
1140
541
|
}
|
|
1141
|
-
return
|
|
1142
|
-
message,
|
|
1143
|
-
severity: "error",
|
|
1144
|
-
startOffset,
|
|
1145
|
-
endOffset,
|
|
1146
|
-
startLine: token.startLine ?? 1,
|
|
1147
|
-
startColumn: token.startColumn ?? 1,
|
|
1148
|
-
code: "parse-error"
|
|
1149
|
-
};
|
|
542
|
+
return diagnostics;
|
|
1150
543
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
544
|
+
|
|
545
|
+
// src/symbol-table.ts
|
|
546
|
+
function buildSymbolTable(regions, getKeywordVariants2) {
|
|
547
|
+
const symbols = /* @__PURE__ */ new Map();
|
|
548
|
+
const locationIndex = [];
|
|
549
|
+
const variants = (eng) => getKeywordVariants2 ? getKeywordVariants2(eng) : [eng];
|
|
550
|
+
const behaviorKws = variants("behavior");
|
|
551
|
+
const defKws = variants("def");
|
|
552
|
+
const setKws = variants("set");
|
|
553
|
+
const defaultKws = variants("default");
|
|
554
|
+
const installKws = variants("install");
|
|
555
|
+
const callKws = variants("call");
|
|
556
|
+
const behaviorDefPat = new RegExp(`\\b(${behaviorKws.join("|")})\\s+(\\w+)`, "gi");
|
|
557
|
+
const funcDefPat = new RegExp(`\\b(${defKws.join("|")})\\s+(\\w+)`, "gi");
|
|
558
|
+
const varDefPat = new RegExp(`\\b(${[...setKws, ...defaultKws].join("|")})\\s+([:$]\\w+)`, "gi");
|
|
559
|
+
const installPat = new RegExp(`\\b(${installKws.join("|")})\\s+(\\w+)`, "gi");
|
|
560
|
+
const callPat = new RegExp(`\\b(${callKws.join("|")})\\s+(\\w+)`, "gi");
|
|
561
|
+
const varRefPat = /[:$]\w+/g;
|
|
562
|
+
const funcCallPat = /\b(\w+)\s*\(/g;
|
|
563
|
+
const caretWritePat = new RegExp(`\\b(${setKws.join("|")})\\s+\\^([A-Za-z_]\\w*)`, "gi");
|
|
564
|
+
const caretRefPat = /\^([A-Za-z_]\w*)/g;
|
|
565
|
+
const attrsRefPat = /\battrs\.([A-Za-z_]\w*)/g;
|
|
566
|
+
function getOrCreate(name, kind, scope) {
|
|
567
|
+
let entry = symbols.get(name);
|
|
568
|
+
if (!entry) {
|
|
569
|
+
entry = { name, kind, scope, definitions: [], usages: [] };
|
|
570
|
+
symbols.set(name, entry);
|
|
571
|
+
}
|
|
572
|
+
return entry;
|
|
573
|
+
}
|
|
574
|
+
function toDocLine(region, localLine) {
|
|
575
|
+
return region.startLine + localLine;
|
|
576
|
+
}
|
|
577
|
+
function toDocChar(region, localLine, localChar) {
|
|
578
|
+
return localLine === 0 ? region.startChar + localChar : localChar;
|
|
579
|
+
}
|
|
580
|
+
function addLocation(entry, kind, region, localLine, localChar, length) {
|
|
581
|
+
const loc = {
|
|
582
|
+
line: toDocLine(region, localLine),
|
|
583
|
+
character: toDocChar(region, localLine, localChar),
|
|
584
|
+
length
|
|
585
|
+
};
|
|
586
|
+
if (kind === "def") {
|
|
587
|
+
entry.definitions.push(loc);
|
|
1156
588
|
} else {
|
|
1157
|
-
|
|
589
|
+
entry.usages.push(loc);
|
|
1158
590
|
}
|
|
591
|
+
locationIndex.push({
|
|
592
|
+
line: loc.line,
|
|
593
|
+
start: loc.character,
|
|
594
|
+
end: loc.character + length,
|
|
595
|
+
name: entry.name
|
|
596
|
+
});
|
|
1159
597
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
const type = tokenBefore.tokenType;
|
|
1165
|
-
if (type === Kw_on) return "after-on";
|
|
1166
|
-
if (type === Kw_then) return "after-then";
|
|
1167
|
-
if (type.name.startsWith("Cmd_")) return "after-command";
|
|
1168
|
-
const markers = [Kw_to, Kw_from, Kw_into, Kw_in, Kw_of, Kw_on, Kw_as, Kw_with];
|
|
1169
|
-
if (markers.includes(type)) return "after-marker";
|
|
1170
|
-
return "general";
|
|
1171
|
-
}
|
|
1172
|
-
function getCommandSuggestions() {
|
|
1173
|
-
return COMMAND_SCHEMAS.map((schema) => ({
|
|
1174
|
-
label: schema.action,
|
|
1175
|
-
kind: "command",
|
|
1176
|
-
detail: getCategoryLabel(schema.category),
|
|
1177
|
-
insertText: schema.action + " ",
|
|
1178
|
-
category: schema.category
|
|
1179
|
-
}));
|
|
1180
|
-
}
|
|
1181
|
-
function getEventSuggestions() {
|
|
1182
|
-
const events = [
|
|
1183
|
-
"click",
|
|
1184
|
-
"dblclick",
|
|
1185
|
-
"mousedown",
|
|
1186
|
-
"mouseup",
|
|
1187
|
-
"mousemove",
|
|
1188
|
-
"mouseenter",
|
|
1189
|
-
"mouseleave",
|
|
1190
|
-
"keydown",
|
|
1191
|
-
"keyup",
|
|
1192
|
-
"keypress",
|
|
1193
|
-
"input",
|
|
1194
|
-
"change",
|
|
1195
|
-
"submit",
|
|
1196
|
-
"focus",
|
|
1197
|
-
"blur",
|
|
1198
|
-
"load",
|
|
1199
|
-
"scroll",
|
|
1200
|
-
"resize",
|
|
1201
|
-
"mutation",
|
|
1202
|
-
"intersection",
|
|
1203
|
-
"every"
|
|
1204
|
-
];
|
|
1205
|
-
return events.map((e) => ({
|
|
1206
|
-
label: e,
|
|
1207
|
-
kind: "event",
|
|
1208
|
-
detail: `${e} event`,
|
|
1209
|
-
insertText: e + " "
|
|
1210
|
-
}));
|
|
1211
|
-
}
|
|
1212
|
-
function getRoleMarkerSuggestions(commandToken) {
|
|
1213
|
-
const cmdName = commandToken.tokenType.name.replace("Cmd_", "");
|
|
1214
|
-
const schema = COMMAND_SCHEMAS.find((s) => s.action === cmdName);
|
|
1215
|
-
if (!schema) return [];
|
|
1216
|
-
const suggestions = [];
|
|
1217
|
-
for (const role of schema.roles) {
|
|
1218
|
-
if (role.role === schema.primaryRole) continue;
|
|
1219
|
-
const markers = getRoleMarkers(role.role);
|
|
1220
|
-
for (const marker of markers) {
|
|
1221
|
-
suggestions.push({
|
|
1222
|
-
label: marker,
|
|
1223
|
-
kind: "keyword",
|
|
1224
|
-
detail: `${role.role} marker`,
|
|
1225
|
-
insertText: marker + " "
|
|
1226
|
-
});
|
|
598
|
+
function findLocalLine(code, offset) {
|
|
599
|
+
let line = 0;
|
|
600
|
+
for (let i = 0; i < offset && i < code.length; i++) {
|
|
601
|
+
if (code[i] === "\n") line++;
|
|
1227
602
|
}
|
|
603
|
+
return line;
|
|
1228
604
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
{ label: ".", kind: "selector", detail: "CSS class selector", insertText: "." },
|
|
1236
|
-
{ label: "#", kind: "selector", detail: "CSS ID selector", insertText: "#" },
|
|
1237
|
-
{ label: "@", kind: "selector", detail: "Attribute selector", insertText: "@" },
|
|
1238
|
-
{ label: ":", kind: "variable", detail: "Local variable", insertText: ":" },
|
|
1239
|
-
{ label: "$", kind: "variable", detail: "Global variable", insertText: "$" }
|
|
1240
|
-
];
|
|
1241
|
-
}
|
|
1242
|
-
function getRoleMarkers(role) {
|
|
1243
|
-
switch (role) {
|
|
1244
|
-
case "destination":
|
|
1245
|
-
return ["to", "into"];
|
|
1246
|
-
case "source":
|
|
1247
|
-
return ["from"];
|
|
1248
|
-
case "target":
|
|
1249
|
-
return ["on"];
|
|
1250
|
-
case "container":
|
|
1251
|
-
return ["in"];
|
|
1252
|
-
case "instrument":
|
|
1253
|
-
return ["with"];
|
|
1254
|
-
case "beneficiary":
|
|
1255
|
-
return ["for"];
|
|
1256
|
-
default:
|
|
1257
|
-
return [];
|
|
605
|
+
function findLocalChar(code, offset) {
|
|
606
|
+
let lastNewline = -1;
|
|
607
|
+
for (let i = 0; i < offset && i < code.length; i++) {
|
|
608
|
+
if (code[i] === "\n") lastNewline = i;
|
|
609
|
+
}
|
|
610
|
+
return offset - lastNewline - 1;
|
|
1258
611
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
612
|
+
const defVarOffsets = /* @__PURE__ */ new Map();
|
|
613
|
+
const caretDefOffsets = /* @__PURE__ */ new Map();
|
|
614
|
+
for (const region of regions) {
|
|
615
|
+
const code = region.code;
|
|
616
|
+
const regionDefOffsets = /* @__PURE__ */ new Set();
|
|
617
|
+
defVarOffsets.set(region, regionDefOffsets);
|
|
618
|
+
const regionCaretDefOffsets = /* @__PURE__ */ new Set();
|
|
619
|
+
caretDefOffsets.set(region, regionCaretDefOffsets);
|
|
620
|
+
for (const m of code.matchAll(behaviorDefPat)) {
|
|
621
|
+
const name = m[2];
|
|
622
|
+
const nameOffset = (m.index ?? 0) + m[0].indexOf(name);
|
|
623
|
+
const localLine = findLocalLine(code, nameOffset);
|
|
624
|
+
const localChar = findLocalChar(code, nameOffset);
|
|
625
|
+
addLocation(
|
|
626
|
+
getOrCreate(name, "behavior", "global"),
|
|
627
|
+
"def",
|
|
628
|
+
region,
|
|
629
|
+
localLine,
|
|
630
|
+
localChar,
|
|
631
|
+
name.length
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
for (const m of code.matchAll(funcDefPat)) {
|
|
635
|
+
const name = m[2];
|
|
636
|
+
const nameOffset = (m.index ?? 0) + m[0].indexOf(name);
|
|
637
|
+
const localLine = findLocalLine(code, nameOffset);
|
|
638
|
+
const localChar = findLocalChar(code, nameOffset);
|
|
639
|
+
addLocation(
|
|
640
|
+
getOrCreate(name, "function", "global"),
|
|
641
|
+
"def",
|
|
642
|
+
region,
|
|
643
|
+
localLine,
|
|
644
|
+
localChar,
|
|
645
|
+
name.length
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
for (const m of code.matchAll(varDefPat)) {
|
|
649
|
+
const varName = m[2];
|
|
650
|
+
const varOffset = (m.index ?? 0) + m[0].indexOf(varName);
|
|
651
|
+
const localLine = findLocalLine(code, varOffset);
|
|
652
|
+
const localChar = findLocalChar(code, varOffset);
|
|
653
|
+
const scope = varName.startsWith("$") ? "global" : "local";
|
|
654
|
+
addLocation(
|
|
655
|
+
getOrCreate(varName, "variable", scope),
|
|
656
|
+
"def",
|
|
657
|
+
region,
|
|
658
|
+
localLine,
|
|
659
|
+
localChar,
|
|
660
|
+
varName.length
|
|
661
|
+
);
|
|
662
|
+
regionDefOffsets.add(varOffset);
|
|
663
|
+
}
|
|
664
|
+
for (const m of code.matchAll(installPat)) {
|
|
665
|
+
const name = m[2];
|
|
666
|
+
const nameOffset = (m.index ?? 0) + m[0].indexOf(name);
|
|
667
|
+
const localLine = findLocalLine(code, nameOffset);
|
|
668
|
+
const localChar = findLocalChar(code, nameOffset);
|
|
669
|
+
const entry = getOrCreate(name, "behavior", "global");
|
|
670
|
+
addLocation(entry, "use", region, localLine, localChar, name.length);
|
|
671
|
+
}
|
|
672
|
+
for (const m of code.matchAll(callPat)) {
|
|
673
|
+
const name = m[2];
|
|
674
|
+
const nameOffset = (m.index ?? 0) + m[0].indexOf(name);
|
|
675
|
+
const localLine = findLocalLine(code, nameOffset);
|
|
676
|
+
const localChar = findLocalChar(code, nameOffset);
|
|
677
|
+
const entry = getOrCreate(name, "function", "global");
|
|
678
|
+
addLocation(entry, "use", region, localLine, localChar, name.length);
|
|
679
|
+
}
|
|
680
|
+
for (const m of code.matchAll(varRefPat)) {
|
|
681
|
+
const varName = m[0];
|
|
682
|
+
const varOffset = m.index ?? 0;
|
|
683
|
+
if (regionDefOffsets.has(varOffset)) continue;
|
|
684
|
+
const localLine = findLocalLine(code, varOffset);
|
|
685
|
+
const localChar = findLocalChar(code, varOffset);
|
|
686
|
+
const scope = varName.startsWith("$") ? "global" : "local";
|
|
687
|
+
const entry = getOrCreate(varName, "variable", scope);
|
|
688
|
+
addLocation(entry, "use", region, localLine, localChar, varName.length);
|
|
689
|
+
}
|
|
690
|
+
for (const m of code.matchAll(caretWritePat)) {
|
|
691
|
+
const name = m[2];
|
|
692
|
+
const caretOffset = (m.index ?? 0) + m[0].lastIndexOf("^");
|
|
693
|
+
const localLine = findLocalLine(code, caretOffset);
|
|
694
|
+
const localChar = findLocalChar(code, caretOffset);
|
|
695
|
+
const symbolName = `^${name}`;
|
|
696
|
+
addLocation(
|
|
697
|
+
getOrCreate(symbolName, "caretVar", "caret"),
|
|
698
|
+
"def",
|
|
699
|
+
region,
|
|
700
|
+
localLine,
|
|
701
|
+
localChar,
|
|
702
|
+
symbolName.length
|
|
703
|
+
);
|
|
704
|
+
regionCaretDefOffsets.add(caretOffset);
|
|
705
|
+
}
|
|
706
|
+
for (const m of code.matchAll(caretRefPat)) {
|
|
707
|
+
const caretOffset = m.index ?? 0;
|
|
708
|
+
if (regionCaretDefOffsets.has(caretOffset)) continue;
|
|
709
|
+
const name = m[1];
|
|
710
|
+
const localLine = findLocalLine(code, caretOffset);
|
|
711
|
+
const localChar = findLocalChar(code, caretOffset);
|
|
712
|
+
const symbolName = `^${name}`;
|
|
713
|
+
addLocation(
|
|
714
|
+
getOrCreate(symbolName, "caretVar", "caret"),
|
|
715
|
+
"use",
|
|
716
|
+
region,
|
|
717
|
+
localLine,
|
|
718
|
+
localChar,
|
|
719
|
+
symbolName.length
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
for (const m of code.matchAll(attrsRefPat)) {
|
|
723
|
+
const name = m[1];
|
|
724
|
+
const attrsStart = m.index ?? 0;
|
|
725
|
+
const localLine = findLocalLine(code, attrsStart);
|
|
726
|
+
const localChar = findLocalChar(code, attrsStart);
|
|
727
|
+
const fullLength = m[0].length;
|
|
728
|
+
addLocation(
|
|
729
|
+
getOrCreate(`attrs.${name}`, "componentAttr", "attr"),
|
|
730
|
+
"use",
|
|
731
|
+
region,
|
|
732
|
+
localLine,
|
|
733
|
+
localChar,
|
|
734
|
+
fullLength
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
const keywords = /* @__PURE__ */ new Set([
|
|
738
|
+
"if",
|
|
739
|
+
"else",
|
|
740
|
+
"end",
|
|
741
|
+
"on",
|
|
742
|
+
"then",
|
|
743
|
+
"repeat",
|
|
744
|
+
"for",
|
|
745
|
+
"while",
|
|
746
|
+
"return",
|
|
747
|
+
"set",
|
|
748
|
+
"get",
|
|
749
|
+
"put",
|
|
750
|
+
"add",
|
|
751
|
+
"remove",
|
|
752
|
+
"toggle",
|
|
753
|
+
"show",
|
|
754
|
+
"hide",
|
|
755
|
+
"fetch",
|
|
756
|
+
"send",
|
|
757
|
+
"trigger",
|
|
758
|
+
"wait",
|
|
759
|
+
"log",
|
|
760
|
+
"call",
|
|
761
|
+
"default",
|
|
762
|
+
"behavior",
|
|
763
|
+
"def",
|
|
764
|
+
"install",
|
|
765
|
+
"init",
|
|
766
|
+
"async",
|
|
767
|
+
...behaviorKws,
|
|
768
|
+
...defKws,
|
|
769
|
+
...setKws,
|
|
770
|
+
...installKws,
|
|
771
|
+
...callKws
|
|
772
|
+
]);
|
|
773
|
+
for (const m of code.matchAll(funcCallPat)) {
|
|
774
|
+
const name = m[1];
|
|
775
|
+
if (keywords.has(name.toLowerCase())) continue;
|
|
776
|
+
const nameOffset = m.index ?? 0;
|
|
777
|
+
const localLine = findLocalLine(code, nameOffset);
|
|
778
|
+
const localChar = findLocalChar(code, nameOffset);
|
|
779
|
+
const entry = getOrCreate(name, "function", "global");
|
|
780
|
+
addLocation(entry, "use", region, localLine, localChar, name.length);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return {
|
|
784
|
+
symbols,
|
|
785
|
+
symbolAt(line, character) {
|
|
786
|
+
for (const loc of locationIndex) {
|
|
787
|
+
if (loc.line === line && character >= loc.start && character < loc.end) {
|
|
788
|
+
return symbols.get(loc.name);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return void 0;
|
|
792
|
+
}
|
|
1270
793
|
};
|
|
1271
|
-
|
|
794
|
+
}
|
|
795
|
+
var cache = /* @__PURE__ */ new Map();
|
|
796
|
+
function getSymbolTable(uri, version, regions, getKeywordVariants2) {
|
|
797
|
+
const cached = cache.get(uri);
|
|
798
|
+
if (cached && cached.version === version) {
|
|
799
|
+
return cached.table;
|
|
800
|
+
}
|
|
801
|
+
const table = buildSymbolTable(regions, getKeywordVariants2);
|
|
802
|
+
cache.set(uri, { version, table });
|
|
803
|
+
return table;
|
|
1272
804
|
}
|
|
1273
805
|
|
|
1274
806
|
// src/localized-descriptions.ts
|
|
@@ -1326,7 +858,41 @@ var DESCRIPTIONS = {
|
|
|
1326
858
|
documentation: "Waits for CSS transitions/animations to complete."
|
|
1327
859
|
},
|
|
1328
860
|
go: { detail: "Navigate", documentation: "Navigates to a URL or page location." },
|
|
1329
|
-
throw: { detail: "Throw error", documentation: "Throws an error with a specified message." }
|
|
861
|
+
throw: { detail: "Throw error", documentation: "Throws an error with a specified message." },
|
|
862
|
+
// v2 / v2.1 component + reactive (English only — localized translations
|
|
863
|
+
// can be added as language coverage rolls out).
|
|
864
|
+
"^": {
|
|
865
|
+
detail: "Reactive variable (^var)",
|
|
866
|
+
documentation: 'Reads or writes a DOM-scoped reactive variable. `^name` walks ancestors until it finds an owner, or stops at the nearest `dom-scope="isolated"` boundary.'
|
|
867
|
+
},
|
|
868
|
+
attrs: {
|
|
869
|
+
detail: "Component attributes",
|
|
870
|
+
documentation: 'Proxy over the component element\'s HTML attributes. `attrs.fooBar` reads `foo-bar="..."`, with number/boolean coercion. Available inside `<template _="...">` init scripts and `${...}` interpolation.'
|
|
871
|
+
},
|
|
872
|
+
"dom-scope": {
|
|
873
|
+
detail: "Reactive scope boundary",
|
|
874
|
+
documentation: '`dom-scope="isolated"` on a host element bounds `^var` lookup. Set automatically on `<template component>` instances; set manually for ad-hoc isolation.'
|
|
875
|
+
},
|
|
876
|
+
"#if": {
|
|
877
|
+
detail: "Conditional template block",
|
|
878
|
+
documentation: "Line-oriented template directive. Renders the contained block when the condition is truthy. Optional `#else` branch; closes with `#end`."
|
|
879
|
+
},
|
|
880
|
+
"#else": {
|
|
881
|
+
detail: "Alternative branch",
|
|
882
|
+
documentation: "Else branch of a `#if`, or empty-collection fallback for a `#for`."
|
|
883
|
+
},
|
|
884
|
+
"#end": {
|
|
885
|
+
detail: "Close directive block",
|
|
886
|
+
documentation: "Closes the most recent `#if` or `#for`. Must occupy its own line."
|
|
887
|
+
},
|
|
888
|
+
"#for": {
|
|
889
|
+
detail: "Iteration template block",
|
|
890
|
+
documentation: "`#for <name> in <expr>` iterates over `<expr>` with `<name>` bound to each element. Optional `#else` branch fires when the collection is empty."
|
|
891
|
+
},
|
|
892
|
+
"#continue": {
|
|
893
|
+
detail: "Skip iteration",
|
|
894
|
+
documentation: "Skip to the next iteration of the enclosing `#for`. Available v2.1+."
|
|
895
|
+
}
|
|
1330
896
|
},
|
|
1331
897
|
ja: {
|
|
1332
898
|
toggle: {
|
|
@@ -1571,7 +1137,7 @@ try {
|
|
|
1571
1137
|
}
|
|
1572
1138
|
var frameworkIR = null;
|
|
1573
1139
|
try {
|
|
1574
|
-
const fw = await import("./dist-
|
|
1140
|
+
const fw = await import("./dist-NRPF26M4.js");
|
|
1575
1141
|
frameworkIR = { fromInterchangeNode: fw.fromInterchangeNode, renderExplicit: fw.renderExplicit };
|
|
1576
1142
|
console.error(
|
|
1577
1143
|
"[lokascript-ls] @lokascript/framework loaded \u2014 LSE bracket notation in hover enabled"
|
|
@@ -1724,6 +1290,91 @@ var FALLBACK_HOVER_DOCS = {
|
|
|
1724
1290
|
title: "result",
|
|
1725
1291
|
description: "Alias for it - the result of the last expression.",
|
|
1726
1292
|
example: "call myFunction() then log result"
|
|
1293
|
+
},
|
|
1294
|
+
increment: {
|
|
1295
|
+
title: "increment",
|
|
1296
|
+
description: "Increases a numeric value by 1 (or a specified amount).",
|
|
1297
|
+
example: "increment #count"
|
|
1298
|
+
},
|
|
1299
|
+
decrement: {
|
|
1300
|
+
title: "decrement",
|
|
1301
|
+
description: "Decreases a numeric value by 1 (or a specified amount).",
|
|
1302
|
+
example: "decrement #count"
|
|
1303
|
+
},
|
|
1304
|
+
append: {
|
|
1305
|
+
title: "append",
|
|
1306
|
+
description: "Appends content to an element.",
|
|
1307
|
+
example: 'append "<li>item</li>" to #list'
|
|
1308
|
+
},
|
|
1309
|
+
prepend: {
|
|
1310
|
+
title: "prepend",
|
|
1311
|
+
description: "Prepends content to an element.",
|
|
1312
|
+
example: 'prepend "<li>first</li>" to #list'
|
|
1313
|
+
},
|
|
1314
|
+
take: {
|
|
1315
|
+
title: "take",
|
|
1316
|
+
description: "Takes a class from sibling elements (exclusive toggle).",
|
|
1317
|
+
example: "take .active from .tabs"
|
|
1318
|
+
},
|
|
1319
|
+
transition: {
|
|
1320
|
+
title: "transition",
|
|
1321
|
+
description: "Applies a CSS transition to an element.",
|
|
1322
|
+
example: "transition opacity to 0 over 500ms"
|
|
1323
|
+
},
|
|
1324
|
+
settle: {
|
|
1325
|
+
title: "settle",
|
|
1326
|
+
description: "Waits for CSS transitions to complete.",
|
|
1327
|
+
example: "add .fade then settle"
|
|
1328
|
+
},
|
|
1329
|
+
halt: {
|
|
1330
|
+
title: "halt",
|
|
1331
|
+
description: "Stops the current event from propagating.",
|
|
1332
|
+
example: "halt the event"
|
|
1333
|
+
},
|
|
1334
|
+
throw: {
|
|
1335
|
+
title: "throw",
|
|
1336
|
+
description: "Throws an error.",
|
|
1337
|
+
example: 'throw "Something went wrong"'
|
|
1338
|
+
},
|
|
1339
|
+
return: {
|
|
1340
|
+
title: "return",
|
|
1341
|
+
description: "Returns a value from a function or behavior.",
|
|
1342
|
+
example: "return 42"
|
|
1343
|
+
},
|
|
1344
|
+
install: {
|
|
1345
|
+
title: "install",
|
|
1346
|
+
description: "Installs a behavior on an element.",
|
|
1347
|
+
example: "install Draggable(snap: true)"
|
|
1348
|
+
},
|
|
1349
|
+
default: {
|
|
1350
|
+
title: "default",
|
|
1351
|
+
description: "Sets a variable to a value only if it is not already set.",
|
|
1352
|
+
example: "default :count to 0"
|
|
1353
|
+
},
|
|
1354
|
+
then: {
|
|
1355
|
+
title: "then",
|
|
1356
|
+
description: "Chains commands together sequentially.",
|
|
1357
|
+
example: "add .active then wait 1s then remove .active"
|
|
1358
|
+
},
|
|
1359
|
+
end: {
|
|
1360
|
+
title: "end",
|
|
1361
|
+
description: "Closes a block (event handler, behavior, if, repeat).",
|
|
1362
|
+
example: "on click\\n toggle .active\\nend"
|
|
1363
|
+
},
|
|
1364
|
+
async: {
|
|
1365
|
+
title: "async",
|
|
1366
|
+
description: "Runs a command asynchronously without blocking.",
|
|
1367
|
+
example: "async fetch /api/data"
|
|
1368
|
+
},
|
|
1369
|
+
tell: {
|
|
1370
|
+
title: "tell",
|
|
1371
|
+
description: "Sets the default target for subsequent commands.",
|
|
1372
|
+
example: "tell #modal then add .visible"
|
|
1373
|
+
},
|
|
1374
|
+
copy: {
|
|
1375
|
+
title: "copy",
|
|
1376
|
+
description: "Copies text to the clipboard.",
|
|
1377
|
+
example: 'copy "Hello" to the clipboard'
|
|
1727
1378
|
}
|
|
1728
1379
|
};
|
|
1729
1380
|
var FALLBACK_EVENT_NAMES = [
|
|
@@ -1744,7 +1395,7 @@ function resolveMode(settings) {
|
|
|
1744
1395
|
if (settings.mode === "hyperscript") return "hyperscript";
|
|
1745
1396
|
if (settings.mode === "hyperscript-i18n") return "hyperscript-i18n";
|
|
1746
1397
|
if (settings.mode === "lokascript") return "lokascript";
|
|
1747
|
-
return semanticPackage ? "lokascript" : "hyperscript";
|
|
1398
|
+
return semanticPackage?.getRegisteredLanguages ? "lokascript" : "hyperscript";
|
|
1748
1399
|
}
|
|
1749
1400
|
function getBranding(mode) {
|
|
1750
1401
|
return mode === "lokascript" ? "lokascript" : "hyperscript";
|
|
@@ -1763,7 +1414,29 @@ var cachedAnalyzer = null;
|
|
|
1763
1414
|
function getSemanticAnalyzer() {
|
|
1764
1415
|
if (!semanticPackage) return null;
|
|
1765
1416
|
if (!cachedAnalyzer) {
|
|
1766
|
-
cachedAnalyzer =
|
|
1417
|
+
cachedAnalyzer = {
|
|
1418
|
+
analyze(code, language) {
|
|
1419
|
+
if (!semanticPackage.isLanguageRegistered(language)) {
|
|
1420
|
+
return {
|
|
1421
|
+
confidence: 0,
|
|
1422
|
+
errors: [`Language '${language}' is not supported for semantic parsing`]
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
const result = semanticPackage.parseSemantic(code, language);
|
|
1426
|
+
const out = { confidence: result.confidence };
|
|
1427
|
+
if (result.node) {
|
|
1428
|
+
out.command = { name: result.node.action, roles: result.node.roles };
|
|
1429
|
+
}
|
|
1430
|
+
if (result.error) out.errors = [result.error];
|
|
1431
|
+
return out;
|
|
1432
|
+
},
|
|
1433
|
+
supportsLanguage(language) {
|
|
1434
|
+
return semanticPackage.isLanguageRegistered(language);
|
|
1435
|
+
},
|
|
1436
|
+
supportedLanguages() {
|
|
1437
|
+
return semanticPackage.getRegisteredLanguages();
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1767
1440
|
}
|
|
1768
1441
|
return cachedAnalyzer;
|
|
1769
1442
|
}
|
|
@@ -1786,6 +1459,7 @@ connection.onInitialize((params) => {
|
|
|
1786
1459
|
},
|
|
1787
1460
|
definitionProvider: true,
|
|
1788
1461
|
referencesProvider: true,
|
|
1462
|
+
renameProvider: { prepareProvider: true },
|
|
1789
1463
|
documentFormattingProvider: true
|
|
1790
1464
|
}
|
|
1791
1465
|
};
|
|
@@ -1818,7 +1492,10 @@ async function validateDocument(document) {
|
|
|
1818
1492
|
if (isHtmlDocument(uri, text)) {
|
|
1819
1493
|
const regions = extractHyperscriptRegions(text);
|
|
1820
1494
|
for (const region of regions) {
|
|
1821
|
-
const diagnostics =
|
|
1495
|
+
const diagnostics = region.type === "template" ? runDirectiveDiagnostics(region.code).map((d) => ({
|
|
1496
|
+
...d,
|
|
1497
|
+
source: getBranding(resolvedMode)
|
|
1498
|
+
})) : await getDiagnostics(region.code, globalSettings.language);
|
|
1822
1499
|
for (const diag of diagnostics) {
|
|
1823
1500
|
allDiagnostics.push({
|
|
1824
1501
|
...diag,
|
|
@@ -1851,7 +1528,7 @@ async function getDiagnostics(code, language) {
|
|
|
1851
1528
|
start: { line: 0, character: 0 },
|
|
1852
1529
|
end: { line: 0, character: code.split("\n")[0]?.length || code.length }
|
|
1853
1530
|
},
|
|
1854
|
-
severity:
|
|
1531
|
+
severity: DiagnosticSeverity2.Error,
|
|
1855
1532
|
code: "lokascript-only",
|
|
1856
1533
|
source: brand,
|
|
1857
1534
|
message: `${feature.description} (not compatible with _hyperscript)`
|
|
@@ -1886,7 +1563,7 @@ async function getDiagnostics(code, language) {
|
|
|
1886
1563
|
start: { line: 0, character: 0 },
|
|
1887
1564
|
end: { line: 0, character: code.split("\n")[0]?.length || code.length }
|
|
1888
1565
|
},
|
|
1889
|
-
severity:
|
|
1566
|
+
severity: DiagnosticSeverity2.Error,
|
|
1890
1567
|
code: "semantic-error",
|
|
1891
1568
|
source: brand,
|
|
1892
1569
|
message: err
|
|
@@ -1899,7 +1576,7 @@ async function getDiagnostics(code, language) {
|
|
|
1899
1576
|
start: { line: 0, character: 0 },
|
|
1900
1577
|
end: { line: 0, character: code.length }
|
|
1901
1578
|
},
|
|
1902
|
-
severity:
|
|
1579
|
+
severity: DiagnosticSeverity2.Warning,
|
|
1903
1580
|
code: "low-confidence",
|
|
1904
1581
|
source: brand,
|
|
1905
1582
|
message: `Code could not be fully parsed (${(result.confidence * 100).toFixed(0)}% confidence)`
|
|
@@ -1911,7 +1588,7 @@ async function getDiagnostics(code, language) {
|
|
|
1911
1588
|
if (cmd === "toggle" && !roles.has("patient")) {
|
|
1912
1589
|
diagnostics.push({
|
|
1913
1590
|
range: { start: { line: 0, character: 0 }, end: { line: 0, character: code.length } },
|
|
1914
|
-
severity:
|
|
1591
|
+
severity: DiagnosticSeverity2.Warning,
|
|
1915
1592
|
code: "missing-role",
|
|
1916
1593
|
source: brand,
|
|
1917
1594
|
message: "toggle command missing target (add .class, @attr, or selector)"
|
|
@@ -1920,7 +1597,7 @@ async function getDiagnostics(code, language) {
|
|
|
1920
1597
|
if (cmd === "put" && !roles.has("destination")) {
|
|
1921
1598
|
diagnostics.push({
|
|
1922
1599
|
range: { start: { line: 0, character: 0 }, end: { line: 0, character: code.length } },
|
|
1923
|
-
severity:
|
|
1600
|
+
severity: DiagnosticSeverity2.Warning,
|
|
1924
1601
|
code: "missing-role",
|
|
1925
1602
|
source: brand,
|
|
1926
1603
|
message: 'put command missing destination (add "into #element")'
|
|
@@ -1931,108 +1608,58 @@ async function getDiagnostics(code, language) {
|
|
|
1931
1608
|
connection.console.log(`[${brand}-ls] Semantic parsing failed, trying fallback: ${e}`);
|
|
1932
1609
|
}
|
|
1933
1610
|
}
|
|
1934
|
-
if (
|
|
1611
|
+
if (parseFunction && fromCoreASTFn) {
|
|
1935
1612
|
try {
|
|
1936
|
-
const
|
|
1937
|
-
if (
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1613
|
+
const parseResult = parseFunction(code);
|
|
1614
|
+
if (parseResult.errors?.length) {
|
|
1615
|
+
for (const err of parseResult.errors) {
|
|
1616
|
+
diagnostics.push({
|
|
1617
|
+
range: {
|
|
1618
|
+
start: {
|
|
1619
|
+
line: Math.max(0, (err.line ?? 1) - 1),
|
|
1620
|
+
character: Math.max(0, err.column ?? 0)
|
|
1621
|
+
},
|
|
1622
|
+
end: {
|
|
1623
|
+
line: Math.max(0, (err.line ?? 1) - 1),
|
|
1624
|
+
character: Math.max(0, err.column ?? 0) + 10
|
|
1625
|
+
}
|
|
1626
|
+
},
|
|
1627
|
+
severity: DiagnosticSeverity2.Error,
|
|
1628
|
+
code: "parse-error",
|
|
1629
|
+
source: brand,
|
|
1630
|
+
message: err.message
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (parseResult.node && interchangeLSP) {
|
|
1635
|
+
const interchange = fromCoreASTFn(parseResult.node);
|
|
1636
|
+
if (interchange && interchange.type !== "literal") {
|
|
1637
|
+
const nodes = Array.isArray(interchange) ? interchange : [interchange];
|
|
1638
|
+
const astDiagnostics = interchangeLSP.interchangeToLSPDiagnostics(nodes, {
|
|
1639
|
+
source: brand
|
|
1640
|
+
});
|
|
1641
|
+
diagnostics.push(...astDiagnostics);
|
|
1642
|
+
}
|
|
1942
1643
|
}
|
|
1943
1644
|
} catch (parseError) {
|
|
1944
1645
|
diagnostics.push({
|
|
1945
1646
|
range: {
|
|
1946
1647
|
start: { line: 0, character: 0 },
|
|
1947
|
-
end: { line: 0, character: code.length }
|
|
1648
|
+
end: { line: 0, character: Math.min(code.length, 80) }
|
|
1948
1649
|
},
|
|
1949
|
-
severity:
|
|
1650
|
+
severity: DiagnosticSeverity2.Error,
|
|
1950
1651
|
code: "parse-error",
|
|
1951
1652
|
source: brand,
|
|
1952
1653
|
message: parseError.message || "Failed to parse"
|
|
1953
1654
|
});
|
|
1954
1655
|
}
|
|
1955
1656
|
}
|
|
1956
|
-
if (diagnostics.length === 0) {
|
|
1957
|
-
try {
|
|
1958
|
-
const chevrotainResult = parseWithRecovery(code);
|
|
1959
|
-
for (const diag of chevrotainResult.diagnostics) {
|
|
1960
|
-
const lines = code.split("\n");
|
|
1961
|
-
const { line: startLine, character: startChar } = offsetToLineChar(code, diag.startOffset);
|
|
1962
|
-
const { line: endLine, character: endChar } = offsetToLineChar(code, diag.endOffset);
|
|
1963
|
-
diagnostics.push({
|
|
1964
|
-
range: {
|
|
1965
|
-
start: { line: startLine, character: startChar },
|
|
1966
|
-
end: { line: endLine, character: endChar }
|
|
1967
|
-
},
|
|
1968
|
-
severity: diag.severity === "error" ? DiagnosticSeverity.Error : diag.severity === "warning" ? DiagnosticSeverity.Warning : DiagnosticSeverity.Information,
|
|
1969
|
-
code: diag.code,
|
|
1970
|
-
source: brand,
|
|
1971
|
-
message: diag.message
|
|
1972
|
-
});
|
|
1973
|
-
}
|
|
1974
|
-
} catch (e) {
|
|
1975
|
-
connection.console.log(`[${brand}-ls] Chevrotain parsing failed: ${e}`);
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
1657
|
if (diagnostics.length === 0) {
|
|
1979
1658
|
const simpleDiagnostics = runSimpleDiagnostics(code, language);
|
|
1980
1659
|
diagnostics.push(...simpleDiagnostics.map((d) => ({ ...d, source: brand })));
|
|
1981
1660
|
}
|
|
1982
1661
|
return diagnostics.slice(0, globalSettings.maxDiagnostics);
|
|
1983
1662
|
}
|
|
1984
|
-
function offsetToLineChar(text, offset) {
|
|
1985
|
-
let line = 0;
|
|
1986
|
-
let col = 0;
|
|
1987
|
-
for (let i = 0; i < offset && i < text.length; i++) {
|
|
1988
|
-
if (text[i] === "\n") {
|
|
1989
|
-
line++;
|
|
1990
|
-
col = 0;
|
|
1991
|
-
} else {
|
|
1992
|
-
col++;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
return { line, character: col };
|
|
1996
|
-
}
|
|
1997
|
-
function runSimpleDiagnostics(code, _language) {
|
|
1998
|
-
const diagnostics = [];
|
|
1999
|
-
const lines = code.split("\n");
|
|
2000
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2001
|
-
const line = lines[i];
|
|
2002
|
-
const singleQuotes = (line.match(/'/g) || []).length;
|
|
2003
|
-
const doubleQuotes = (line.match(/"/g) || []).length;
|
|
2004
|
-
if (singleQuotes % 2 !== 0) {
|
|
2005
|
-
diagnostics.push({
|
|
2006
|
-
range: { start: { line: i, character: 0 }, end: { line: i, character: line.length } },
|
|
2007
|
-
severity: DiagnosticSeverity.Error,
|
|
2008
|
-
code: "unmatched-quote",
|
|
2009
|
-
source: "lokascript",
|
|
2010
|
-
message: "Unmatched single quote"
|
|
2011
|
-
});
|
|
2012
|
-
}
|
|
2013
|
-
if (doubleQuotes % 2 !== 0) {
|
|
2014
|
-
diagnostics.push({
|
|
2015
|
-
range: { start: { line: i, character: 0 }, end: { line: i, character: line.length } },
|
|
2016
|
-
severity: DiagnosticSeverity.Error,
|
|
2017
|
-
code: "unmatched-quote",
|
|
2018
|
-
source: "lokascript",
|
|
2019
|
-
message: "Unmatched double quote"
|
|
2020
|
-
});
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
const openParens = (code.match(/\(/g) || []).length;
|
|
2024
|
-
const closeParens = (code.match(/\)/g) || []).length;
|
|
2025
|
-
if (openParens !== closeParens) {
|
|
2026
|
-
diagnostics.push({
|
|
2027
|
-
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
|
|
2028
|
-
severity: DiagnosticSeverity.Error,
|
|
2029
|
-
code: "unbalanced-parens",
|
|
2030
|
-
source: "lokascript",
|
|
2031
|
-
message: `Unbalanced parentheses: ${openParens} open, ${closeParens} close`
|
|
2032
|
-
});
|
|
2033
|
-
}
|
|
2034
|
-
return diagnostics;
|
|
2035
|
-
}
|
|
2036
1663
|
connection.onCompletion((params) => {
|
|
2037
1664
|
const document = documents.get(params.textDocument.uri);
|
|
2038
1665
|
if (!document) return [];
|
|
@@ -2055,31 +1682,110 @@ connection.onCompletion((params) => {
|
|
|
2055
1682
|
beforeCursor += regionLines[i] + "\n";
|
|
2056
1683
|
}
|
|
2057
1684
|
}
|
|
1685
|
+
if (found.region.type === "template") {
|
|
1686
|
+
const currentLine = regionLines[found.localLine] ?? "";
|
|
1687
|
+
const linePrefix = currentLine.slice(0, found.localChar);
|
|
1688
|
+
if (/^\s*#\w*$/.test(linePrefix)) {
|
|
1689
|
+
return getDirectiveCompletions();
|
|
1690
|
+
}
|
|
1691
|
+
const sigilContext = inferContext(beforeCursor);
|
|
1692
|
+
if (sigilContext === "caret") {
|
|
1693
|
+
return getCaretVarCompletions(document);
|
|
1694
|
+
}
|
|
1695
|
+
if (sigilContext === "attrs-property") {
|
|
1696
|
+
return getAttrsCompletions();
|
|
1697
|
+
}
|
|
1698
|
+
return [];
|
|
1699
|
+
}
|
|
2058
1700
|
const context = inferContext(beforeCursor);
|
|
1701
|
+
if (context === "caret") return getCaretVarCompletions(document);
|
|
1702
|
+
if (context === "attrs-property") return getAttrsCompletions();
|
|
2059
1703
|
return getContextualCompletions(context, language);
|
|
2060
1704
|
} else {
|
|
2061
1705
|
const offset = document.offsetAt(position);
|
|
2062
1706
|
const beforeCursor = text.slice(0, offset);
|
|
2063
1707
|
const context = inferContext(beforeCursor);
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
const existingLabels = new Set(completions.map((c) => c.label));
|
|
2068
|
-
for (const s of chevrotainSuggestions) {
|
|
2069
|
-
if (existingLabels.has(s.label)) continue;
|
|
2070
|
-
completions.push({
|
|
2071
|
-
label: s.label,
|
|
2072
|
-
kind: s.kind === "command" ? CompletionItemKind.Method : s.kind === "event" ? CompletionItemKind.Event : s.kind === "selector" ? CompletionItemKind.Field : s.kind === "variable" ? CompletionItemKind.Variable : CompletionItemKind.Keyword,
|
|
2073
|
-
detail: s.detail,
|
|
2074
|
-
insertText: s.insertText
|
|
2075
|
-
});
|
|
2076
|
-
}
|
|
2077
|
-
} catch {
|
|
2078
|
-
}
|
|
2079
|
-
return completions;
|
|
1708
|
+
if (context === "caret") return getCaretVarCompletions(document);
|
|
1709
|
+
if (context === "attrs-property") return getAttrsCompletions();
|
|
1710
|
+
return getContextualCompletions(context, language);
|
|
2080
1711
|
}
|
|
2081
1712
|
});
|
|
1713
|
+
function getDirectiveCompletions() {
|
|
1714
|
+
return [
|
|
1715
|
+
{
|
|
1716
|
+
label: "#if",
|
|
1717
|
+
kind: CompletionItemKind.Snippet,
|
|
1718
|
+
detail: "Conditional template block",
|
|
1719
|
+
insertText: "#if ${1:condition}\n ${2}\n#end"
|
|
1720
|
+
},
|
|
1721
|
+
{
|
|
1722
|
+
label: "#if/#else",
|
|
1723
|
+
kind: CompletionItemKind.Snippet,
|
|
1724
|
+
detail: "Conditional with else branch",
|
|
1725
|
+
insertText: "#if ${1:condition}\n ${2}\n#else\n ${3}\n#end"
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
label: "#for",
|
|
1729
|
+
kind: CompletionItemKind.Snippet,
|
|
1730
|
+
detail: "Iteration template block",
|
|
1731
|
+
insertText: "#for ${1:item} in ${2:collection}\n ${3}\n#end"
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
label: "#else",
|
|
1735
|
+
kind: CompletionItemKind.Keyword,
|
|
1736
|
+
detail: "Else branch"
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
label: "#end",
|
|
1740
|
+
kind: CompletionItemKind.Keyword,
|
|
1741
|
+
detail: "Close #if or #for block"
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
label: "#continue",
|
|
1745
|
+
kind: CompletionItemKind.Keyword,
|
|
1746
|
+
detail: "Skip to next #for iteration (v2.1+)"
|
|
1747
|
+
}
|
|
1748
|
+
];
|
|
1749
|
+
}
|
|
1750
|
+
function getCaretVarCompletions(document) {
|
|
1751
|
+
const items = [];
|
|
1752
|
+
try {
|
|
1753
|
+
const { table } = getDocSymbolTable(document);
|
|
1754
|
+
for (const [name, entry] of table.symbols) {
|
|
1755
|
+
if (entry.kind !== "caretVar") continue;
|
|
1756
|
+
const bare = name.startsWith("^") ? name.slice(1) : name;
|
|
1757
|
+
items.push({
|
|
1758
|
+
label: bare,
|
|
1759
|
+
kind: CompletionItemKind.Variable,
|
|
1760
|
+
detail: "Reactive variable (^var)",
|
|
1761
|
+
insertText: bare
|
|
1762
|
+
});
|
|
1763
|
+
}
|
|
1764
|
+
} catch {
|
|
1765
|
+
}
|
|
1766
|
+
if (items.length === 0) {
|
|
1767
|
+
items.push({
|
|
1768
|
+
label: "name",
|
|
1769
|
+
kind: CompletionItemKind.Snippet,
|
|
1770
|
+
detail: "New reactive variable",
|
|
1771
|
+
insertText: "${1:name}"
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
return items;
|
|
1775
|
+
}
|
|
1776
|
+
function getAttrsCompletions() {
|
|
1777
|
+
return [
|
|
1778
|
+
{
|
|
1779
|
+
label: "attribute",
|
|
1780
|
+
kind: CompletionItemKind.Snippet,
|
|
1781
|
+
detail: "Component attribute (attrs.X)",
|
|
1782
|
+
insertText: "${1:propertyName}"
|
|
1783
|
+
}
|
|
1784
|
+
];
|
|
1785
|
+
}
|
|
2082
1786
|
function inferContext(beforeCursor) {
|
|
1787
|
+
if (/\^\w*$/.test(beforeCursor)) return "caret";
|
|
1788
|
+
if (/\battrs\.\w*$/.test(beforeCursor)) return "attrs-property";
|
|
2083
1789
|
const trimmed = beforeCursor.trim();
|
|
2084
1790
|
if (/\bon\s*$/.test(trimmed)) return "event";
|
|
2085
1791
|
if (/\bthen\s*$/.test(trimmed)) return "command";
|
|
@@ -2097,7 +1803,7 @@ function getContextualCompletions(context, language) {
|
|
|
2097
1803
|
return eng;
|
|
2098
1804
|
}
|
|
2099
1805
|
try {
|
|
2100
|
-
const profile = semanticPackage.
|
|
1806
|
+
const profile = semanticPackage.tryGetProfile(effectiveLanguage);
|
|
2101
1807
|
if (profile?.keywords?.[eng]?.primary) {
|
|
2102
1808
|
return profile.keywords[eng].primary;
|
|
2103
1809
|
}
|
|
@@ -2236,6 +1942,24 @@ function getContextualCompletions(context, language) {
|
|
|
2236
1942
|
}
|
|
2237
1943
|
);
|
|
2238
1944
|
}
|
|
1945
|
+
completions.push(
|
|
1946
|
+
{ label: getKeyword("to"), kind: CompletionItemKind.Keyword, detail: "Target destination" },
|
|
1947
|
+
{ label: getKeyword("from"), kind: CompletionItemKind.Keyword, detail: "Source element" },
|
|
1948
|
+
{
|
|
1949
|
+
label: getKeyword("into"),
|
|
1950
|
+
kind: CompletionItemKind.Keyword,
|
|
1951
|
+
detail: "Insert destination"
|
|
1952
|
+
},
|
|
1953
|
+
{ label: getKeyword("on"), kind: CompletionItemKind.Keyword, detail: "Target element" },
|
|
1954
|
+
{ label: getKeyword("with"), kind: CompletionItemKind.Keyword, detail: "Options/data" },
|
|
1955
|
+
{ label: getKeyword("by"), kind: CompletionItemKind.Keyword, detail: "Amount/method" },
|
|
1956
|
+
{ label: getKeyword("as"), kind: CompletionItemKind.Keyword, detail: "Type/format" },
|
|
1957
|
+
{
|
|
1958
|
+
label: getKeyword("then"),
|
|
1959
|
+
kind: CompletionItemKind.Keyword,
|
|
1960
|
+
detail: "Chain next command"
|
|
1961
|
+
}
|
|
1962
|
+
);
|
|
2239
1963
|
break;
|
|
2240
1964
|
case "expression":
|
|
2241
1965
|
completions.push(
|
|
@@ -2305,15 +2029,25 @@ connection.onHover((params) => {
|
|
|
2305
2029
|
if (!currentLine2) return null;
|
|
2306
2030
|
const word2 = getWordAtPosition(currentLine2, found.localChar);
|
|
2307
2031
|
if (!word2) return null;
|
|
2308
|
-
|
|
2032
|
+
const expanded2 = expandSigilPrefix(currentLine2, word2);
|
|
2033
|
+
let lookupText2 = expanded2?.text ?? word2.text;
|
|
2034
|
+
let lookupStart2 = expanded2?.start ?? word2.start;
|
|
2035
|
+
let doc2 = getHoverDocumentation(lookupText2, globalSettings.language);
|
|
2036
|
+
if (!doc2) {
|
|
2037
|
+
lookupText2 = word2.text;
|
|
2038
|
+
lookupStart2 = word2.start;
|
|
2039
|
+
doc2 = getHoverDocumentation(lookupText2, globalSettings.language);
|
|
2040
|
+
}
|
|
2309
2041
|
if (!doc2) return null;
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2042
|
+
if (found.region.type !== "template") {
|
|
2043
|
+
const lse2 = tryGetLSE(found.region.code);
|
|
2044
|
+
if (lse2) {
|
|
2045
|
+
doc2 += `
|
|
2313
2046
|
|
|
2314
2047
|
**LSE:** \`${lse2}\``;
|
|
2048
|
+
}
|
|
2315
2049
|
}
|
|
2316
|
-
const startChar = found.localLine === 0 ? found.region.startChar +
|
|
2050
|
+
const startChar = found.localLine === 0 ? found.region.startChar + lookupStart2 : lookupStart2;
|
|
2317
2051
|
const endChar = found.localLine === 0 ? found.region.startChar + word2.end : word2.end;
|
|
2318
2052
|
return {
|
|
2319
2053
|
contents: { kind: MarkupKind.Markdown, value: doc2 },
|
|
@@ -2328,7 +2062,15 @@ connection.onHover((params) => {
|
|
|
2328
2062
|
if (!currentLine) return null;
|
|
2329
2063
|
const word = getWordAtPosition(currentLine, position.character);
|
|
2330
2064
|
if (!word) return null;
|
|
2331
|
-
|
|
2065
|
+
const expanded = expandSigilPrefix(currentLine, word);
|
|
2066
|
+
let lookupText = expanded?.text ?? word.text;
|
|
2067
|
+
let lookupStart = expanded?.start ?? word.start;
|
|
2068
|
+
let doc = getHoverDocumentation(lookupText, globalSettings.language);
|
|
2069
|
+
if (!doc) {
|
|
2070
|
+
lookupText = word.text;
|
|
2071
|
+
lookupStart = word.start;
|
|
2072
|
+
doc = getHoverDocumentation(lookupText, globalSettings.language);
|
|
2073
|
+
}
|
|
2332
2074
|
if (!doc) return null;
|
|
2333
2075
|
const lse = tryGetLSE(text);
|
|
2334
2076
|
if (lse) {
|
|
@@ -2339,7 +2081,7 @@ connection.onHover((params) => {
|
|
|
2339
2081
|
return {
|
|
2340
2082
|
contents: { kind: MarkupKind.Markdown, value: doc },
|
|
2341
2083
|
range: {
|
|
2342
|
-
start: { line: position.line, character:
|
|
2084
|
+
start: { line: position.line, character: lookupStart },
|
|
2343
2085
|
end: { line: position.line, character: word.end }
|
|
2344
2086
|
}
|
|
2345
2087
|
};
|
|
@@ -2351,9 +2093,9 @@ connection.onHover((params) => {
|
|
|
2351
2093
|
function tryGetLSE(code) {
|
|
2352
2094
|
if (!interchangeLSP || !parseFunction || !fromCoreASTFn || !frameworkIR) return null;
|
|
2353
2095
|
try {
|
|
2354
|
-
const
|
|
2355
|
-
if (!
|
|
2356
|
-
const interchange = fromCoreASTFn(
|
|
2096
|
+
const parseResult = parseFunction(code);
|
|
2097
|
+
if (!parseResult.node) return null;
|
|
2098
|
+
const interchange = fromCoreASTFn(parseResult.node);
|
|
2357
2099
|
if (!interchange) return null;
|
|
2358
2100
|
const semanticNode = frameworkIR.fromInterchangeNode(interchange);
|
|
2359
2101
|
return frameworkIR.renderExplicit(semanticNode);
|
|
@@ -2361,7 +2103,30 @@ function tryGetLSE(code) {
|
|
|
2361
2103
|
return null;
|
|
2362
2104
|
}
|
|
2363
2105
|
}
|
|
2106
|
+
function expandSigilPrefix(line, word) {
|
|
2107
|
+
if (word.start === 0) return null;
|
|
2108
|
+
const prev = line[word.start - 1];
|
|
2109
|
+
if (prev !== "^" && prev !== "#") return null;
|
|
2110
|
+
return { text: prev + word.text, start: word.start - 1 };
|
|
2111
|
+
}
|
|
2364
2112
|
function getHoverDocumentation(word, language) {
|
|
2113
|
+
const docsPre = lspMetadata?.HOVER_DOCS ?? FALLBACK_HOVER_DOCS;
|
|
2114
|
+
if (word.startsWith("^")) {
|
|
2115
|
+
if (docsPre["^"]) {
|
|
2116
|
+
return formatHoverEntry(docsPre["^"], word, language);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (word.startsWith("#")) {
|
|
2120
|
+
const direct = docsPre[word.toLowerCase()];
|
|
2121
|
+
if (direct) {
|
|
2122
|
+
return formatHoverEntry(direct, word, language);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
if (word.startsWith("attrs.")) {
|
|
2126
|
+
if (docsPre.attrs) {
|
|
2127
|
+
return formatHoverEntry(docsPre.attrs, "attrs", language);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2365
2130
|
const wordLower = word.toLowerCase();
|
|
2366
2131
|
const canonicalKey = isMultilingualEnabled(resolvedMode) ? resolveCanonicalKeyword(wordLower, language) : wordLower;
|
|
2367
2132
|
const docs = lspMetadata?.HOVER_DOCS ?? FALLBACK_HOVER_DOCS;
|
|
@@ -2391,10 +2156,25 @@ ${doc.example}
|
|
|
2391
2156
|
}
|
|
2392
2157
|
return content;
|
|
2393
2158
|
}
|
|
2159
|
+
function formatHoverEntry(doc, displayName, _language) {
|
|
2160
|
+
let content = `**${doc.title}**`;
|
|
2161
|
+
if (displayName !== doc.title) {
|
|
2162
|
+
content = `**${displayName}** (${doc.title})`;
|
|
2163
|
+
}
|
|
2164
|
+
content += `
|
|
2165
|
+
|
|
2166
|
+
${doc.description}`;
|
|
2167
|
+
content += `
|
|
2168
|
+
|
|
2169
|
+
\`\`\`hyperscript
|
|
2170
|
+
${doc.example}
|
|
2171
|
+
\`\`\``;
|
|
2172
|
+
return content;
|
|
2173
|
+
}
|
|
2394
2174
|
var translationCache = null;
|
|
2395
2175
|
function buildTranslationCache() {
|
|
2396
|
-
const
|
|
2397
|
-
if (!semanticPackage) return
|
|
2176
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2177
|
+
if (!semanticPackage) return cache2;
|
|
2398
2178
|
const langMap = {
|
|
2399
2179
|
es: "Spanish",
|
|
2400
2180
|
ja: "Japanese",
|
|
@@ -2431,7 +2211,7 @@ function buildTranslationCache() {
|
|
|
2431
2211
|
const translations = [];
|
|
2432
2212
|
for (const [lang, langName] of Object.entries(langMap)) {
|
|
2433
2213
|
try {
|
|
2434
|
-
const profile = semanticPackage.
|
|
2214
|
+
const profile = semanticPackage.tryGetProfile(lang);
|
|
2435
2215
|
const kw = profile?.keywords?.[canonicalKey];
|
|
2436
2216
|
if (kw) {
|
|
2437
2217
|
const trans = kw;
|
|
@@ -2444,10 +2224,10 @@ function buildTranslationCache() {
|
|
|
2444
2224
|
}
|
|
2445
2225
|
}
|
|
2446
2226
|
if (translations.length > 0) {
|
|
2447
|
-
|
|
2227
|
+
cache2.set(canonicalKey, translations.join(" \xB7 "));
|
|
2448
2228
|
}
|
|
2449
2229
|
}
|
|
2450
|
-
return
|
|
2230
|
+
return cache2;
|
|
2451
2231
|
}
|
|
2452
2232
|
function getKeywordTranslations(canonicalKey) {
|
|
2453
2233
|
if (!translationCache) {
|
|
@@ -2457,21 +2237,21 @@ function getKeywordTranslations(canonicalKey) {
|
|
|
2457
2237
|
}
|
|
2458
2238
|
var reverseKeywordCache = null;
|
|
2459
2239
|
function buildReverseKeywordCache() {
|
|
2460
|
-
const
|
|
2461
|
-
if (!semanticPackage) return
|
|
2240
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2241
|
+
if (!semanticPackage) return cache2;
|
|
2462
2242
|
const languagesToTry = getOtherLanguages();
|
|
2463
2243
|
for (const lang of languagesToTry) {
|
|
2464
2244
|
try {
|
|
2465
|
-
const profile = semanticPackage.
|
|
2245
|
+
const profile = semanticPackage.tryGetProfile(lang);
|
|
2466
2246
|
if (profile?.keywords) {
|
|
2467
2247
|
for (const [canonicalKey, translation] of Object.entries(profile.keywords)) {
|
|
2468
2248
|
const trans = translation;
|
|
2469
2249
|
if (trans.primary) {
|
|
2470
|
-
|
|
2250
|
+
cache2.set(trans.primary.toLowerCase(), canonicalKey);
|
|
2471
2251
|
}
|
|
2472
2252
|
if (trans.alternatives) {
|
|
2473
2253
|
for (const alt of trans.alternatives) {
|
|
2474
|
-
|
|
2254
|
+
cache2.set(alt.toLowerCase(), canonicalKey);
|
|
2475
2255
|
}
|
|
2476
2256
|
}
|
|
2477
2257
|
}
|
|
@@ -2479,7 +2259,7 @@ function buildReverseKeywordCache() {
|
|
|
2479
2259
|
if (profile?.references) {
|
|
2480
2260
|
for (const [engRef, translatedRef] of Object.entries(profile.references)) {
|
|
2481
2261
|
if (typeof translatedRef === "string") {
|
|
2482
|
-
|
|
2262
|
+
cache2.set(translatedRef.toLowerCase(), engRef);
|
|
2483
2263
|
}
|
|
2484
2264
|
}
|
|
2485
2265
|
}
|
|
@@ -2487,7 +2267,7 @@ function buildReverseKeywordCache() {
|
|
|
2487
2267
|
connection.console.log(`[lokascript-ls] Reverse keyword cache: profile '${lang}' not found`);
|
|
2488
2268
|
}
|
|
2489
2269
|
}
|
|
2490
|
-
return
|
|
2270
|
+
return cache2;
|
|
2491
2271
|
}
|
|
2492
2272
|
function resolveCanonicalKeyword(word, _language) {
|
|
2493
2273
|
const canonicalKeywords = lspMetadata?.ALL_KEYWORDS ?? FALLBACK_KEYWORDS;
|
|
@@ -2497,120 +2277,12 @@ function resolveCanonicalKeyword(word, _language) {
|
|
|
2497
2277
|
}
|
|
2498
2278
|
return reverseKeywordCache.get(word) || word;
|
|
2499
2279
|
}
|
|
2500
|
-
connection.onDefinition((params) => {
|
|
2501
|
-
try {
|
|
2502
|
-
const document = documents.get(params.textDocument.uri);
|
|
2503
|
-
if (!document) return null;
|
|
2504
|
-
const text = document.getText();
|
|
2505
|
-
const uri = document.uri;
|
|
2506
|
-
const position = params.position;
|
|
2507
|
-
let targetWord = null;
|
|
2508
|
-
if (isHtmlDocument(uri, text)) {
|
|
2509
|
-
const regions = extractHyperscriptRegions(text);
|
|
2510
|
-
const found = findRegionAtPosition(regions, position.line, position.character);
|
|
2511
|
-
if (!found) {
|
|
2512
|
-
return null;
|
|
2513
|
-
}
|
|
2514
|
-
const regionLines = found.region.code.split("\n");
|
|
2515
|
-
const currentLine = regionLines[found.localLine];
|
|
2516
|
-
if (!currentLine) return null;
|
|
2517
|
-
const word = getWordAtPosition(currentLine, found.localChar);
|
|
2518
|
-
if (!word) return null;
|
|
2519
|
-
targetWord = word.text;
|
|
2520
|
-
} else {
|
|
2521
|
-
const lines = text.split("\n");
|
|
2522
|
-
const currentLine = lines[position.line];
|
|
2523
|
-
if (!currentLine) return null;
|
|
2524
|
-
const word = getWordAtPosition(currentLine, position.character);
|
|
2525
|
-
if (!word) return null;
|
|
2526
|
-
targetWord = word.text;
|
|
2527
|
-
}
|
|
2528
|
-
if (!targetWord) return null;
|
|
2529
|
-
const locations = [];
|
|
2530
|
-
const behaviorVariants = getKeywordVariants("behavior");
|
|
2531
|
-
const defVariants = getKeywordVariants("def");
|
|
2532
|
-
const behaviorPattern = new RegExp(
|
|
2533
|
-
`\\b(${behaviorVariants.join("|")})\\s+(${escapeRegExp(targetWord)})\\b`,
|
|
2534
|
-
"gi"
|
|
2535
|
-
);
|
|
2536
|
-
const defPattern = new RegExp(
|
|
2537
|
-
`\\b(${defVariants.join("|")})\\s+(${escapeRegExp(targetWord)})\\b`,
|
|
2538
|
-
"gi"
|
|
2539
|
-
);
|
|
2540
|
-
if (isHtmlDocument(uri, text)) {
|
|
2541
|
-
const regions = extractHyperscriptRegions(text);
|
|
2542
|
-
for (const region of regions) {
|
|
2543
|
-
for (const match of region.code.matchAll(behaviorPattern)) {
|
|
2544
|
-
const matchLine = findLineInRegion(region.code, match.index ?? 0);
|
|
2545
|
-
const matchChar = findCharInLine(region.code, match.index ?? 0);
|
|
2546
|
-
locations.push({
|
|
2547
|
-
uri: params.textDocument.uri,
|
|
2548
|
-
range: {
|
|
2549
|
-
start: {
|
|
2550
|
-
line: region.startLine + matchLine,
|
|
2551
|
-
character: matchLine === 0 ? region.startChar + matchChar : matchChar
|
|
2552
|
-
},
|
|
2553
|
-
end: {
|
|
2554
|
-
line: region.startLine + matchLine,
|
|
2555
|
-
character: (matchLine === 0 ? region.startChar + matchChar : matchChar) + match[0].length
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
});
|
|
2559
|
-
}
|
|
2560
|
-
for (const match of region.code.matchAll(defPattern)) {
|
|
2561
|
-
const matchLine = findLineInRegion(region.code, match.index ?? 0);
|
|
2562
|
-
const matchChar = findCharInLine(region.code, match.index ?? 0);
|
|
2563
|
-
locations.push({
|
|
2564
|
-
uri: params.textDocument.uri,
|
|
2565
|
-
range: {
|
|
2566
|
-
start: {
|
|
2567
|
-
line: region.startLine + matchLine,
|
|
2568
|
-
character: matchLine === 0 ? region.startChar + matchChar : matchChar
|
|
2569
|
-
},
|
|
2570
|
-
end: {
|
|
2571
|
-
line: region.startLine + matchLine,
|
|
2572
|
-
character: (matchLine === 0 ? region.startChar + matchChar : matchChar) + match[0].length
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
});
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
} else {
|
|
2579
|
-
for (const match of text.matchAll(behaviorPattern)) {
|
|
2580
|
-
const pos = offsetToPosition(text, match.index ?? 0);
|
|
2581
|
-
locations.push({
|
|
2582
|
-
uri: params.textDocument.uri,
|
|
2583
|
-
range: {
|
|
2584
|
-
start: pos,
|
|
2585
|
-
end: { line: pos.line, character: pos.character + match[0].length }
|
|
2586
|
-
}
|
|
2587
|
-
});
|
|
2588
|
-
}
|
|
2589
|
-
for (const match of text.matchAll(defPattern)) {
|
|
2590
|
-
const pos = offsetToPosition(text, match.index ?? 0);
|
|
2591
|
-
locations.push({
|
|
2592
|
-
uri: params.textDocument.uri,
|
|
2593
|
-
range: {
|
|
2594
|
-
start: pos,
|
|
2595
|
-
end: { line: pos.line, character: pos.character + match[0].length }
|
|
2596
|
-
}
|
|
2597
|
-
});
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
if (locations.length === 0) return null;
|
|
2601
|
-
if (locations.length === 1) return locations[0];
|
|
2602
|
-
return locations;
|
|
2603
|
-
} catch (error) {
|
|
2604
|
-
connection.console.error(`Go to Definition error: ${error}`);
|
|
2605
|
-
return null;
|
|
2606
|
-
}
|
|
2607
|
-
});
|
|
2608
2280
|
function getKeywordVariants(eng) {
|
|
2609
2281
|
const variants = [eng];
|
|
2610
2282
|
if (!semanticPackage) return variants;
|
|
2611
2283
|
for (const lang of getOtherLanguages()) {
|
|
2612
2284
|
try {
|
|
2613
|
-
const profile = semanticPackage.
|
|
2285
|
+
const profile = semanticPackage.tryGetProfile(lang);
|
|
2614
2286
|
if (profile?.keywords?.[eng]) {
|
|
2615
2287
|
const trans = profile.keywords[eng];
|
|
2616
2288
|
if (trans.primary && !variants.includes(trans.primary)) {
|
|
@@ -2629,71 +2301,94 @@ function getKeywordVariants(eng) {
|
|
|
2629
2301
|
}
|
|
2630
2302
|
return variants;
|
|
2631
2303
|
}
|
|
2304
|
+
function getDocSymbolTable(document) {
|
|
2305
|
+
const text = document.getText();
|
|
2306
|
+
const uri = document.uri;
|
|
2307
|
+
const regions = isHtmlDocument(uri, text) ? extractHyperscriptRegions(text) : [{ code: text, startLine: 0, startChar: 0, endLine: 0, endChar: 0, type: "script" }];
|
|
2308
|
+
return { table: getSymbolTable(uri, document.version, regions, getKeywordVariants), uri };
|
|
2309
|
+
}
|
|
2310
|
+
function symbolLocToLocation(uri, loc) {
|
|
2311
|
+
return {
|
|
2312
|
+
uri,
|
|
2313
|
+
range: {
|
|
2314
|
+
start: { line: loc.line, character: loc.character },
|
|
2315
|
+
end: { line: loc.line, character: loc.character + loc.length }
|
|
2316
|
+
}
|
|
2317
|
+
};
|
|
2318
|
+
}
|
|
2319
|
+
connection.onDefinition((params) => {
|
|
2320
|
+
try {
|
|
2321
|
+
const document = documents.get(params.textDocument.uri);
|
|
2322
|
+
if (!document) return null;
|
|
2323
|
+
const { table, uri } = getDocSymbolTable(document);
|
|
2324
|
+
const entry = table.symbolAt(params.position.line, params.position.character);
|
|
2325
|
+
if (!entry || entry.definitions.length === 0) return null;
|
|
2326
|
+
const locations = entry.definitions.map((loc) => symbolLocToLocation(uri, loc));
|
|
2327
|
+
return locations.length === 1 ? locations[0] : locations;
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
connection.console.error(`Go to Definition error: ${error}`);
|
|
2330
|
+
return null;
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2632
2333
|
connection.onReferences((params) => {
|
|
2633
2334
|
try {
|
|
2634
2335
|
const document = documents.get(params.textDocument.uri);
|
|
2635
2336
|
if (!document) return null;
|
|
2636
|
-
const
|
|
2637
|
-
const
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
if (
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
if (!targetWord) return null;
|
|
2659
|
-
const locations = [];
|
|
2660
|
-
const wordPattern = new RegExp(`\\b${escapeRegExp(targetWord)}\\b`, "gi");
|
|
2661
|
-
if (isHtmlDocument(uri, text)) {
|
|
2662
|
-
const regions = extractHyperscriptRegions(text);
|
|
2663
|
-
for (const region of regions) {
|
|
2664
|
-
for (const match of region.code.matchAll(wordPattern)) {
|
|
2665
|
-
const matchLine = findLineInRegion(region.code, match.index ?? 0);
|
|
2666
|
-
const matchChar = findCharInLine(region.code, match.index ?? 0);
|
|
2667
|
-
locations.push({
|
|
2668
|
-
uri: params.textDocument.uri,
|
|
2669
|
-
range: {
|
|
2670
|
-
start: {
|
|
2671
|
-
line: region.startLine + matchLine,
|
|
2672
|
-
character: matchLine === 0 ? region.startChar + matchChar : matchChar
|
|
2673
|
-
},
|
|
2674
|
-
end: {
|
|
2675
|
-
line: region.startLine + matchLine,
|
|
2676
|
-
character: (matchLine === 0 ? region.startChar + matchChar : matchChar) + match[0].length
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
});
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
} else {
|
|
2683
|
-
for (const match of text.matchAll(wordPattern)) {
|
|
2684
|
-
const pos = offsetToPosition(text, match.index ?? 0);
|
|
2685
|
-
locations.push({
|
|
2686
|
-
uri: params.textDocument.uri,
|
|
2337
|
+
const { table, uri } = getDocSymbolTable(document);
|
|
2338
|
+
const entry = table.symbolAt(params.position.line, params.position.character);
|
|
2339
|
+
if (!entry) return null;
|
|
2340
|
+
const allLocs = [...entry.definitions, ...entry.usages];
|
|
2341
|
+
if (allLocs.length === 0) return null;
|
|
2342
|
+
return allLocs.map((loc) => symbolLocToLocation(uri, loc));
|
|
2343
|
+
} catch (error) {
|
|
2344
|
+
connection.console.error(`Find References error: ${error}`);
|
|
2345
|
+
return null;
|
|
2346
|
+
}
|
|
2347
|
+
});
|
|
2348
|
+
connection.onPrepareRename((params) => {
|
|
2349
|
+
try {
|
|
2350
|
+
const document = documents.get(params.textDocument.uri);
|
|
2351
|
+
if (!document) return null;
|
|
2352
|
+
const { table } = getDocSymbolTable(document);
|
|
2353
|
+
const entry = table.symbolAt(params.position.line, params.position.character);
|
|
2354
|
+
if (!entry) return null;
|
|
2355
|
+
const allLocs = [...entry.definitions, ...entry.usages];
|
|
2356
|
+
for (const loc of allLocs) {
|
|
2357
|
+
if (loc.line === params.position.line && params.position.character >= loc.character && params.position.character < loc.character + loc.length) {
|
|
2358
|
+
return {
|
|
2687
2359
|
range: {
|
|
2688
|
-
start:
|
|
2689
|
-
end: { line:
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2360
|
+
start: { line: loc.line, character: loc.character },
|
|
2361
|
+
end: { line: loc.line, character: loc.character + loc.length }
|
|
2362
|
+
},
|
|
2363
|
+
placeholder: entry.name
|
|
2364
|
+
};
|
|
2692
2365
|
}
|
|
2693
2366
|
}
|
|
2694
|
-
return
|
|
2367
|
+
return null;
|
|
2695
2368
|
} catch (error) {
|
|
2696
|
-
connection.console.error(`
|
|
2369
|
+
connection.console.error(`Prepare Rename error: ${error}`);
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
connection.onRenameRequest((params) => {
|
|
2374
|
+
try {
|
|
2375
|
+
const document = documents.get(params.textDocument.uri);
|
|
2376
|
+
if (!document) return null;
|
|
2377
|
+
const { table, uri } = getDocSymbolTable(document);
|
|
2378
|
+
const entry = table.symbolAt(params.position.line, params.position.character);
|
|
2379
|
+
if (!entry) return null;
|
|
2380
|
+
const allLocs = [...entry.definitions, ...entry.usages];
|
|
2381
|
+
if (allLocs.length === 0) return null;
|
|
2382
|
+
const edits = allLocs.map((loc) => ({
|
|
2383
|
+
range: {
|
|
2384
|
+
start: { line: loc.line, character: loc.character },
|
|
2385
|
+
end: { line: loc.line, character: loc.character + loc.length }
|
|
2386
|
+
},
|
|
2387
|
+
newText: params.newName
|
|
2388
|
+
}));
|
|
2389
|
+
return { changes: { [uri]: edits } };
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
connection.console.error(`Rename error: ${error}`);
|
|
2697
2392
|
return null;
|
|
2698
2393
|
}
|
|
2699
2394
|
});
|
|
@@ -2776,7 +2471,7 @@ function extractSymbols(code, language) {
|
|
|
2776
2471
|
const variants = [eng];
|
|
2777
2472
|
if (!semanticPackage || language === "en") return variants;
|
|
2778
2473
|
try {
|
|
2779
|
-
const profile = semanticPackage.
|
|
2474
|
+
const profile = semanticPackage.tryGetProfile(language);
|
|
2780
2475
|
if (profile?.keywords?.[eng]) {
|
|
2781
2476
|
const trans = profile.keywords[eng];
|
|
2782
2477
|
if (trans.primary) variants.push(trans.primary);
|