@fluixi/ts-plugin 0.1.0-alpha.1 → 0.1.0-alpha.2

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.
Files changed (2) hide show
  1. package/dist/index.js +332 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -423,26 +423,54 @@ function templatePieces(ts, tpl, sf) {
423
423
  });
424
424
  return pieces;
425
425
  }
426
- function templateAt(ts, sf, pos) {
427
- let found;
426
+
427
+ // src/cache.ts
428
+ var parsed = /* @__PURE__ */ new WeakMap();
429
+ function parseCached(ts, sf, tpl) {
430
+ let byTemplate = parsed.get(sf);
431
+ if (!byTemplate) {
432
+ byTemplate = /* @__PURE__ */ new Map();
433
+ parsed.set(sf, byTemplate);
434
+ }
435
+ const key = tpl.getStart(sf);
436
+ const hit = byTemplate.get(key);
437
+ if (hit) return hit;
438
+ const result = k(templatePieces(ts, tpl, sf));
439
+ byTemplate.set(key, result);
440
+ return result;
441
+ }
442
+ var templates = /* @__PURE__ */ new WeakMap();
443
+ function templatesIn(ts, sf) {
444
+ const hit = templates.get(sf);
445
+ if (hit) return hit;
446
+ const found = [];
428
447
  const visit = (node) => {
429
- if (found) return;
430
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg") && pos >= node.template.getStart(sf) && pos <= node.template.getEnd()) {
431
- found = node;
432
- return;
448
+ if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
449
+ found.push(node);
433
450
  }
434
451
  ts.forEachChild(node, visit);
435
452
  };
436
453
  visit(sf);
454
+ found.sort((a, b2) => a.getStart(sf) - b2.getStart(sf));
455
+ templates.set(sf, found);
437
456
  return found;
438
457
  }
458
+ function templateAtCached(ts, sf, pos) {
459
+ let match;
460
+ for (const tpl of templatesIn(ts, sf)) {
461
+ const start = tpl.template.getStart(sf);
462
+ if (start > pos) break;
463
+ if (pos <= tpl.template.getEnd()) match = tpl;
464
+ }
465
+ return match;
466
+ }
439
467
 
440
468
  // src/hover.ts
441
469
  function componentQuickInfo(ts, ls, fileName, position) {
442
470
  const program = ls.getProgram();
443
471
  const sf = program?.getSourceFile(fileName);
444
472
  if (!program || !sf) return void 0;
445
- const template = templateAt(ts, sf, position);
473
+ const template = templateAtCached(ts, sf, position);
446
474
  if (!template) return void 0;
447
475
  const hit = componentTagAt(template, sf, position, ts);
448
476
  if (!hit) return void 0;
@@ -457,7 +485,7 @@ function componentQuickInfo(ts, ls, fileName, position) {
457
485
  return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
458
486
  }
459
487
  function componentTagAt(template, sf, position, ts) {
460
- const { root } = k(templatePieces(ts, template.template, sf));
488
+ const { root } = parseCached(ts, sf, template.template);
461
489
  let hit;
462
490
  const walk = (nodes) => {
463
491
  for (const node of nodes) {
@@ -621,14 +649,14 @@ function resolveElementType(ts, checker, location, tag) {
621
649
  return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
622
650
  }
623
651
  function inferParamAt(ts, program, sf, position) {
624
- const template = templateAt(ts, sf, position);
652
+ const template = templateAtCached(ts, sf, position);
625
653
  if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
626
654
  const spans = template.template.templateSpans;
627
655
  const holeExprs = spans.map((s) => s.expression);
628
656
  const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
629
657
  if (holeIndex === -1) return void 0;
630
658
  const roles = /* @__PURE__ */ new Map();
631
- holeRoles(k(templatePieces(ts, template.template, sf)).root.children, roles);
659
+ holeRoles(parseCached(ts, sf, template.template).root.children, roles);
632
660
  const role = roles.get(holeIndex);
633
661
  if (!role) return void 0;
634
662
  const fn = holeExprs[holeIndex];
@@ -706,7 +734,7 @@ function componentPropCompletions(ts, ls, fileName, position) {
706
734
  const program = ls.getProgram();
707
735
  const sf = program?.getSourceFile(fileName);
708
736
  if (!program || !sf) return void 0;
709
- const template = templateAt(ts, sf, position);
737
+ const template = templateAtCached(ts, sf, position);
710
738
  if (!template) return void 0;
711
739
  const comp = componentTagAtAttrArea(ts, sf, template.template, position);
712
740
  if (!comp || !comp.tag) return void 0;
@@ -735,7 +763,7 @@ function componentPropHover(ts, ls, fileName, position) {
735
763
  const program = ls.getProgram();
736
764
  const sf = program?.getSourceFile(fileName);
737
765
  if (!program || !sf) return void 0;
738
- const template = templateAt(ts, sf, position);
766
+ const template = templateAtCached(ts, sf, position);
739
767
  if (!template) return void 0;
740
768
  const hit = propNameAt(ts, sf, template.template, position);
741
769
  if (!hit) return void 0;
@@ -771,7 +799,7 @@ function componentPropHover(ts, ls, fileName, position) {
771
799
  };
772
800
  }
773
801
  function propNameAt(ts, sf, tpl, position) {
774
- const { root } = k(templatePieces(ts, tpl, sf));
802
+ const { root } = parseCached(ts, sf, tpl);
775
803
  let hit;
776
804
  const walk = (nodes) => {
777
805
  for (const node of nodes) {
@@ -846,7 +874,7 @@ function firstParamType(ts, checker, type) {
846
874
  return void 0;
847
875
  }
848
876
  function componentTagAtAttrArea(ts, sf, tpl, position) {
849
- const { root } = k(templatePieces(ts, tpl, sf));
877
+ const { root } = parseCached(ts, sf, tpl);
850
878
  let hit;
851
879
  const walk = (nodes) => {
852
880
  for (const node of nodes) {
@@ -921,6 +949,175 @@ function propertyAccessAt(ts, sf, position) {
921
949
  return found;
922
950
  }
923
951
 
952
+ // src/elements.ts
953
+ var ATTRIBUTES_MODULE = "@fluixi/dom/attributes";
954
+ var perProgram = /* @__PURE__ */ new WeakMap();
955
+ function intrinsics(ts, program, host, sf) {
956
+ const hit = perProgram.get(program);
957
+ if (hit) return hit;
958
+ let type;
959
+ try {
960
+ const checker = program.getTypeChecker();
961
+ const declared = (file) => {
962
+ if (!file) return void 0;
963
+ const moduleSymbol = checker.getSymbolAtLocation(file);
964
+ const symbol = moduleSymbol?.exports?.get(ts.escapeLeadingUnderscores("IntrinsicElements"));
965
+ return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : void 0;
966
+ };
967
+ const resolved = ts.resolveModuleName(
968
+ ATTRIBUTES_MODULE,
969
+ sf.fileName,
970
+ program.getCompilerOptions(),
971
+ host
972
+ );
973
+ type = declared(
974
+ resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName)
975
+ );
976
+ if (!type) {
977
+ for (const file of program.getSourceFiles()) {
978
+ if (!file.isDeclarationFile && file.fileName.indexOf("@fluixi/dom") === -1) continue;
979
+ if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
980
+ type = declared(file);
981
+ if (type) break;
982
+ }
983
+ }
984
+ } catch {
985
+ type = void 0;
986
+ }
987
+ const entry = { type, byTag: /* @__PURE__ */ new Map() };
988
+ perProgram.set(program, entry);
989
+ return entry;
990
+ }
991
+ function attributesFor(ts, program, host, sf, tag) {
992
+ const state = intrinsics(ts, program, host, sf);
993
+ if (!state.type) return void 0;
994
+ if (state.byTag.has(tag)) return state.byTag.get(tag);
995
+ let props;
996
+ const checker = program.getTypeChecker();
997
+ const tagSymbol = state.type.getProperty(tag);
998
+ if (tagSymbol) {
999
+ const decl = tagSymbol.valueDeclaration ?? tagSymbol.declarations?.[0];
1000
+ if (decl) props = checker.getTypeOfSymbolAtLocation(tagSymbol, decl).getProperties();
1001
+ }
1002
+ state.byTag.set(tag, props);
1003
+ return props;
1004
+ }
1005
+ function elementAtAttributeArea(ts, sf, tpl, position) {
1006
+ const { root } = parseCached(ts, sf, tpl);
1007
+ let hit;
1008
+ const walk = (nodes) => {
1009
+ for (const node of nodes) {
1010
+ if (node.kind === "Element") {
1011
+ const el = node;
1012
+ const openStart = el.loc.start + 1 + el.tag.length;
1013
+ const openEnd = firstAttributeEnd(el);
1014
+ if (position > openStart && position <= openEnd) {
1015
+ const present = /* @__PURE__ */ new Set();
1016
+ for (const a of el.attributes) {
1017
+ if ("loc" in a && a.loc) present.add(writtenName(sf.text, a.loc.start));
1018
+ }
1019
+ hit = { tag: el.tag, present };
1020
+ }
1021
+ }
1022
+ if (!hit && "children" in node && node.children) walk(node.children);
1023
+ if (hit) return;
1024
+ }
1025
+ };
1026
+ walk(root.children);
1027
+ return hit;
1028
+ }
1029
+ function writtenName(text, start) {
1030
+ let end = start;
1031
+ while (end < text.length && !/[\s=/>]/.test(text[end])) end++;
1032
+ return text.slice(start, end);
1033
+ }
1034
+ function firstAttributeEnd(el) {
1035
+ let end = el.loc.start + 1 + el.tag.length;
1036
+ for (const a of el.attributes) {
1037
+ if ("loc" in a && a.loc) end = Math.max(end, a.loc.end);
1038
+ }
1039
+ return end + 1;
1040
+ }
1041
+ function elementAttributeCompletions(ts, ls, host, fileName, position) {
1042
+ const program = ls.getProgram();
1043
+ const sf = program?.getSourceFile(fileName);
1044
+ if (!program || !sf) return void 0;
1045
+ const tpl = templateAtCached(ts, sf, position);
1046
+ if (!tpl) return void 0;
1047
+ const hit = elementAtAttributeArea(ts, sf, tpl.template, position);
1048
+ if (!hit) return void 0;
1049
+ const props = attributesFor(ts, program, host, sf, hit.tag);
1050
+ if (!props) return void 0;
1051
+ const entries = [];
1052
+ for (const p2 of props) {
1053
+ const name = p2.getName();
1054
+ if (name === "children" || hit.present.has(name)) continue;
1055
+ entries.push({
1056
+ name,
1057
+ kind: ts.ScriptElementKind.memberVariableElement,
1058
+ kindModifiers: "",
1059
+ sortText: name.startsWith("on") ? "1" : "0"
1060
+ });
1061
+ }
1062
+ return entries.length ? entries : void 0;
1063
+ }
1064
+ function attributeNameAt(ts, sf, tpl, position) {
1065
+ const { root } = parseCached(ts, sf, tpl);
1066
+ let hit;
1067
+ const walk = (nodes) => {
1068
+ for (const node of nodes) {
1069
+ if (node.kind === "Element") {
1070
+ const el = node;
1071
+ for (const a of el.attributes) {
1072
+ if (!("loc" in a) || !a.loc) continue;
1073
+ const start = a.loc.start;
1074
+ const name = writtenName(sf.text, start);
1075
+ if (name && position >= start && position <= start + name.length) {
1076
+ hit = { tag: el.tag, name, start };
1077
+ }
1078
+ }
1079
+ }
1080
+ if (!hit && "children" in node && node.children) walk(node.children);
1081
+ if (hit) return;
1082
+ }
1083
+ };
1084
+ walk(root.children);
1085
+ return hit;
1086
+ }
1087
+ function elementAttributeHover(ts, ls, host, fileName, position) {
1088
+ const program = ls.getProgram();
1089
+ const sf = program?.getSourceFile(fileName);
1090
+ if (!program || !sf) return void 0;
1091
+ const tpl = templateAtCached(ts, sf, position);
1092
+ if (!tpl) return void 0;
1093
+ const hit = attributeNameAt(ts, sf, tpl.template, position);
1094
+ if (!hit) return void 0;
1095
+ const props = attributesFor(ts, program, host, sf, hit.tag);
1096
+ const symbol = props?.find((p2) => p2.getName() === hit.name);
1097
+ if (!symbol) return void 0;
1098
+ const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
1099
+ if (!decl) return void 0;
1100
+ const checker = program.getTypeChecker();
1101
+ const typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, decl));
1102
+ return {
1103
+ kind: ts.ScriptElementKind.memberVariableElement,
1104
+ kindModifiers: "",
1105
+ textSpan: { start: hit.start, length: hit.name.length },
1106
+ displayParts: [
1107
+ { text: "(", kind: "punctuation" },
1108
+ { text: "attribute", kind: "text" },
1109
+ { text: ")", kind: "punctuation" },
1110
+ { text: " ", kind: "space" },
1111
+ { text: `${hit.tag}.`, kind: "text" },
1112
+ { text: hit.name, kind: "propertyName" },
1113
+ { text: ":", kind: "punctuation" },
1114
+ { text: " ", kind: "space" },
1115
+ { text: typeText, kind: "text" }
1116
+ ],
1117
+ documentation: symbol.getDocumentationComment(checker)
1118
+ };
1119
+ }
1120
+
924
1121
  // src/virtual.ts
925
1122
  var EVENT_MAP_KEYS = new Set(Object.keys({
926
1123
  click: 0,
@@ -985,12 +1182,6 @@ function inHole(segments, pos) {
985
1182
  function inKey(segments, pos) {
986
1183
  return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
987
1184
  }
988
- function toGenerated(segments, pos) {
989
- for (const s of segments) {
990
- if (pos >= s.srcStart && pos <= s.srcEnd) return s.genStart + (pos - s.srcStart);
991
- }
992
- return -1;
993
- }
994
1185
  function toSource(segments, pos) {
995
1186
  for (const s of segments) {
996
1187
  if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
@@ -998,8 +1189,8 @@ function toSource(segments, pos) {
998
1189
  return -1;
999
1190
  }
1000
1191
  function buildVirtual(ts, sf) {
1001
- const templates = collectTemplates(ts, sf);
1002
- if (templates.length === 0) return void 0;
1192
+ const templates2 = collectTemplates(ts, sf);
1193
+ if (templates2.length === 0) return void 0;
1003
1194
  const srcText = sf.text;
1004
1195
  const segments = [];
1005
1196
  let gen = "";
@@ -1012,7 +1203,7 @@ function buildVirtual(ts, sf) {
1012
1203
  src = to;
1013
1204
  };
1014
1205
  gen += PRELUDE;
1015
- for (const tpl of templates) {
1206
+ for (const tpl of templates2) {
1016
1207
  copy(tpl.node.getStart(sf));
1017
1208
  gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
1018
1209
  src = tpl.node.getEnd();
@@ -1207,81 +1398,105 @@ function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
1207
1398
  return gen;
1208
1399
  }
1209
1400
 
1210
- // src/virtual-service.ts
1211
- var VirtualService = class {
1401
+ // src/shadow.ts
1402
+ var SUFFIX = ".__fx.ts";
1403
+ function isShadowPath(fileName) {
1404
+ return fileName.endsWith(SUFFIX);
1405
+ }
1406
+ function shadowPathFor(fileName) {
1407
+ return fileName + SUFFIX;
1408
+ }
1409
+ function sourcePathFor(shadow) {
1410
+ return shadow.slice(0, -SUFFIX.length);
1411
+ }
1412
+ var ShadowFiles = class {
1212
1413
  constructor(ts, host) {
1213
1414
  this.ts = ts;
1214
1415
  this.host = host;
1416
+ this.patchHost();
1417
+ }
1418
+ built = /* @__PURE__ */ new Map();
1419
+ /** Source files known to contain no templates, so we skip building a shadow. */
1420
+ barren = /* @__PURE__ */ new Map();
1421
+ /**
1422
+ * Teach the existing host about shadow paths. The language service holds this
1423
+ * host object, so overriding its methods in place is enough — no second service.
1424
+ */
1425
+ patchHost() {
1426
+ const ts = this.ts;
1427
+ const host = this.host;
1215
1428
  const self = this;
1216
- const virtualHost = new Proxy(host, {
1217
- get(target, prop, receiver) {
1218
- if (prop === "getScriptSnapshot") {
1219
- return (fileName) => {
1220
- const v2 = self.virtual(fileName);
1221
- return v2 ? self.ts.ScriptSnapshot.fromString(v2.text) : target.getScriptSnapshot(fileName);
1222
- };
1223
- }
1224
- const value = Reflect.get(target, prop, receiver);
1225
- return typeof value === "function" ? value.bind(target) : value;
1429
+ const originalNames = host.getScriptFileNames.bind(host);
1430
+ host.getScriptFileNames = () => {
1431
+ const names = originalNames();
1432
+ const out = names.slice();
1433
+ for (const name of names) {
1434
+ if (!isShadowPath(name) && self.build(name)) out.push(shadowPathFor(name));
1226
1435
  }
1227
- });
1228
- this.ls = ts.createLanguageService(virtualHost, ts.createDocumentRegistry());
1436
+ return out;
1437
+ };
1438
+ const originalSnapshot = host.getScriptSnapshot.bind(host);
1439
+ host.getScriptSnapshot = (fileName) => {
1440
+ if (!isShadowPath(fileName)) return originalSnapshot(fileName);
1441
+ const built = self.build(sourcePathFor(fileName));
1442
+ return built ? ts.ScriptSnapshot.fromString(built.text) : void 0;
1443
+ };
1444
+ const originalVersion = host.getScriptVersion.bind(host);
1445
+ host.getScriptVersion = (fileName) => isShadowPath(fileName) ? originalVersion(sourcePathFor(fileName)) : originalVersion(fileName);
1446
+ if (typeof host.fileExists === "function") {
1447
+ const originalExists = host.fileExists.bind(host);
1448
+ host.fileExists = (fileName) => isShadowPath(fileName) ? !!self.build(sourcePathFor(fileName)) : originalExists(fileName);
1449
+ }
1229
1450
  }
1230
- ls;
1231
- /** Cached per file: the virtual text + segments, keyed by the file's version. */
1232
- cache = /* @__PURE__ */ new Map();
1233
- /** The virtual doc for a file (built + cached per source version), if it has templates. */
1234
- virtual(fileName) {
1451
+ /** Build (and cache per source version) the shadow text for a file. */
1452
+ build(fileName) {
1235
1453
  const version = this.host.getScriptVersion(fileName);
1236
- const hit = this.cache.get(fileName);
1237
- if (hit && hit.version === version) return hit.text ? hit : void 0;
1238
- const snap = this.host.getScriptSnapshot(fileName);
1239
- if (!snap) return void 0;
1240
- const src = snap.getText(0, snap.getLength());
1241
- const sf = this.ts.createSourceFile(fileName, src, this.ts.ScriptTarget.Latest, true, scriptKind(this.ts, fileName));
1242
- const v2 = buildVirtual(this.ts, sf);
1243
- this.cache.set(fileName, { version, text: v2?.text ?? "", segments: v2?.segments ?? [] });
1244
- return v2;
1245
- }
1246
- segmentsFor(fileName) {
1247
- return this.virtual(fileName)?.segments;
1454
+ if (this.barren.get(fileName) === version) return void 0;
1455
+ const hit = this.built.get(fileName);
1456
+ if (hit && hit.version === version) return hit;
1457
+ const snapshot = this.host.getScriptSnapshot(fileName);
1458
+ if (!snapshot) return void 0;
1459
+ const text = snapshot.getText(0, snapshot.getLength());
1460
+ if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) {
1461
+ this.barren.set(fileName, version);
1462
+ this.built.delete(fileName);
1463
+ return void 0;
1464
+ }
1465
+ const sf = this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.Latest, true);
1466
+ const virtual = buildVirtual(this.ts, sf);
1467
+ if (!virtual) {
1468
+ this.barren.set(fileName, version);
1469
+ this.built.delete(fileName);
1470
+ return void 0;
1471
+ }
1472
+ const entry = {
1473
+ version,
1474
+ text: virtual.text + "\nexport {};\n",
1475
+ segments: virtual.segments
1476
+ };
1477
+ this.built.set(fileName, entry);
1478
+ return entry;
1248
1479
  }
1249
- service() {
1250
- return this.ls;
1480
+ /** The shadow path for a source file, when it has one. */
1481
+ shadowFor(fileName) {
1482
+ return this.build(fileName) ? shadowPathFor(fileName) : void 0;
1251
1483
  }
1252
- toGen(fileName, pos) {
1253
- const segs = this.virtual(fileName)?.segments;
1254
- return segs ? toGenerated(segs, pos) : -1;
1484
+ segmentsFor(fileName) {
1485
+ return this.build(fileName)?.segments;
1255
1486
  }
1256
- toSrc(fileName, pos) {
1257
- const segs = this.virtual(fileName)?.segments;
1258
- return segs ? toSource(segs, pos) : -1;
1487
+ toSrc(fileName, position) {
1488
+ const segments = this.segmentsFor(fileName);
1489
+ return segments ? toSource(segments, position) : -1;
1259
1490
  }
1260
- /** True when a SOURCE position lies inside an embedded `${…}` hole. */
1261
- inHole(fileName, pos) {
1262
- const segs = this.virtual(fileName)?.segments;
1263
- return segs ? inHole(segs, pos) : false;
1491
+ inHole(fileName, position) {
1492
+ const segments = this.segmentsFor(fileName);
1493
+ return !!segments && inHole(segments, position);
1264
1494
  }
1265
- /** True when a SOURCE position lies on a mapped component prop-name key. */
1266
- inKey(fileName, pos) {
1267
- const segs = this.virtual(fileName)?.segments;
1268
- return segs ? inKey(segs, pos) : false;
1495
+ inKey(fileName, position) {
1496
+ const segments = this.segmentsFor(fileName);
1497
+ return !!segments && inKey(segments, position);
1269
1498
  }
1270
1499
  };
1271
- function scriptKind(ts, fileName) {
1272
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
1273
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
1274
- if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) return ts.ScriptKind.JS;
1275
- return ts.ScriptKind.TS;
1276
- }
1277
-
1278
- // src/bridge.ts
1279
- function mapDiagnostic(vs, fileName, d) {
1280
- if (d.start === void 0) return d;
1281
- const start = vs.toSrc(fileName, d.start);
1282
- if (start === -1) return void 0;
1283
- return { ...d, start };
1284
- }
1285
1500
 
1286
1501
  // src/index.ts
1287
1502
  function init(modules) {
@@ -1289,11 +1504,12 @@ function init(modules) {
1289
1504
  return {
1290
1505
  create(info) {
1291
1506
  const ls = info.languageService;
1292
- let vs;
1507
+ const host = info.languageServiceHost;
1508
+ let shadows;
1293
1509
  try {
1294
- vs = new VirtualService(ts, info.languageServiceHost);
1510
+ shadows = new ShadowFiles(ts, info.languageServiceHost);
1295
1511
  } catch {
1296
- vs = void 0;
1512
+ shadows = void 0;
1297
1513
  }
1298
1514
  const proxy = /* @__PURE__ */ Object.create(null);
1299
1515
  for (const key of Object.keys(ls)) {
@@ -1318,17 +1534,18 @@ function init(modules) {
1318
1534
  return true;
1319
1535
  });
1320
1536
  };
1321
- const holeDiagnostics = (fileName, get) => {
1322
- if (!vs || !vs.segmentsFor(fileName)) return [];
1537
+ const holeDiagnostics = (fileName) => {
1538
+ const shadow = shadows?.shadowFor(fileName);
1539
+ if (!shadow) return [];
1323
1540
  try {
1324
1541
  const sourceFile = ls.getProgram()?.getSourceFile(fileName);
1325
1542
  const out = [];
1326
- for (const d of get(fileName)) {
1543
+ for (const d of ls.getSemanticDiagnostics(shadow)) {
1327
1544
  if (d.start === void 0) continue;
1328
- const m2 = mapDiagnostic(vs, fileName, d);
1329
- if (m2 && m2.start !== void 0 && (vs.inHole(fileName, m2.start) || vs.inKey(fileName, m2.start))) {
1330
- out.push({ ...m2, file: sourceFile });
1331
- }
1545
+ const start = shadows.toSrc(fileName, d.start);
1546
+ if (start === -1) continue;
1547
+ if (!shadows.inHole(fileName, start) && !shadows.inKey(fileName, start)) continue;
1548
+ out.push({ ...d, start, file: sourceFile });
1332
1549
  }
1333
1550
  return out;
1334
1551
  } catch {
@@ -1336,16 +1553,30 @@ function init(modules) {
1336
1553
  }
1337
1554
  };
1338
1555
  proxy.getSemanticDiagnostics = (fileName) => {
1556
+ if (isShadowPath(fileName)) return [];
1339
1557
  const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
1340
- return base.concat(holeDiagnostics(fileName, (f) => vs.service().getSemanticDiagnostics(f)));
1558
+ return base.concat(holeDiagnostics(fileName));
1341
1559
  };
1342
- proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1560
+ proxy.getSuggestionDiagnostics = (fileName) => isShadowPath(fileName) ? [] : reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
1561
+ proxy.getSyntacticDiagnostics = (fileName) => isShadowPath(fileName) ? [] : ls.getSyntacticDiagnostics(fileName);
1562
+ const notShadow = (items) => items?.filter((i) => !isShadowPath(i.fileName));
1563
+ proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => notShadow(ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs));
1564
+ proxy.getReferencesAtPosition = (fileName, position) => notShadow(ls.getReferencesAtPosition(fileName, position));
1565
+ proxy.getDefinitionAtPosition = (fileName, position) => notShadow(ls.getDefinitionAtPosition(fileName, position));
1566
+ proxy.getImplementationAtPosition = (fileName, position) => notShadow(ls.getImplementationAtPosition(fileName, position));
1567
+ proxy.getDocumentHighlights = (fileName, position, filesToSearch) => ls.getDocumentHighlights(fileName, position, filesToSearch)?.filter((h) => !isShadowPath(h.fileName));
1568
+ proxy.findReferences = (fileName, position) => ls.findReferences(fileName, position)?.filter((r) => !isShadowPath(r.definition.fileName)).map((r) => ({ ...r, references: r.references.filter((x2) => !isShadowPath(x2.fileName)) }));
1343
1569
  proxy.getQuickInfoAtPosition = (fileName, position) => {
1344
1570
  try {
1345
1571
  const prop = componentPropHover(ts, ls, fileName, position);
1346
1572
  if (prop) return prop;
1347
1573
  } catch {
1348
1574
  }
1575
+ try {
1576
+ const attr = elementAttributeHover(ts, ls, host, fileName, position);
1577
+ if (attr) return attr;
1578
+ } catch {
1579
+ }
1349
1580
  try {
1350
1581
  const inferred = paramQuickInfo(ts, ls, fileName, position);
1351
1582
  if (inferred) return inferred;
@@ -1359,6 +1590,11 @@ function init(modules) {
1359
1590
  if (props) return completionList(props);
1360
1591
  } catch {
1361
1592
  }
1593
+ try {
1594
+ const attrs = elementAttributeCompletions(ts, ls, host, fileName, position);
1595
+ if (attrs) return completionList(attrs);
1596
+ } catch {
1597
+ }
1362
1598
  try {
1363
1599
  const members = paramMemberCompletions(ts, ls, fileName, position);
1364
1600
  if (members) return completionList(members);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluixi/ts-plugin",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "publishConfig": {