@marko/language-server 0.12.10 → 0.12.13

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/index.mjs CHANGED
@@ -571,7 +571,7 @@ function loadCompilerInfo(dir) {
571
571
  const rootDir = lassoPackageRoot.getRootDir(dir);
572
572
  const pkgPath = rootDir && resolveFrom.silent(rootDir, "@marko/compiler/package.json");
573
573
  const pkg = pkgPath && __require(pkgPath);
574
- const cache3 = /* @__PURE__ */ new Map();
574
+ const cache4 = /* @__PURE__ */ new Map();
575
575
  let translator = builtinTranslator;
576
576
  let compiler = builtinCompiler;
577
577
  if (pkg && /^5\./.test(pkg.version)) {
@@ -588,16 +588,16 @@ function loadCompilerInfo(dir) {
588
588
  }
589
589
  }
590
590
  return {
591
- cache: cache3,
591
+ cache: cache4,
592
592
  get lookup() {
593
- let lookup = cache3.get(lookupKey);
593
+ let lookup = cache4.get(lookupKey);
594
594
  if (lookup === void 0) {
595
595
  try {
596
596
  lookup = compiler.taglib.buildLookup(dir, translator);
597
597
  } catch {
598
598
  lookup = builtinInfo.lookup;
599
599
  }
600
- cache3.set(lookupKey, lookup);
600
+ cache4.set(lookupKey, lookup);
601
601
  }
602
602
  return lookup;
603
603
  },
@@ -674,76 +674,113 @@ ${closingTagStr}`
674
674
  }
675
675
  }
676
676
 
677
- // src/service/marko/complete/OpenTagName.ts
677
+ // src/service/marko/util/get-tag-name-completion.ts
678
678
  import path2 from "path";
679
- import { URI as URI2 } from "vscode-uri";
680
679
  import {
681
680
  CompletionItemKind as CompletionItemKind2,
681
+ CompletionItemTag,
682
682
  InsertTextFormat as InsertTextFormat2,
683
683
  MarkupKind,
684
684
  TextEdit as TextEdit2
685
685
  } from "vscode-languageserver";
686
+ import { URI as URI2 } from "vscode-uri";
687
+ var deprecated = [CompletionItemTag.Deprecated];
688
+ function getTagNameCompletion({
689
+ tag,
690
+ range,
691
+ showAutoComplete,
692
+ importer
693
+ }) {
694
+ var _a;
695
+ let label = tag.isNestedTag ? `@${tag.name}` : tag.name;
696
+ const fileForTag = tag.template || tag.renderer || tag.filePath;
697
+ const fileURIForTag = URI2.file(fileForTag).toString();
698
+ const nodeModuleMatch = /\/node_modules\/((?:@[^/]+\/)?[^/]+)/.exec(fileForTag);
699
+ const nodeModuleName = nodeModuleMatch && nodeModuleMatch[1];
700
+ const isCoreTag = /^@?marko[/-]/.test(tag.taglibId) || nodeModuleName === "marko";
701
+ const documentation = {
702
+ kind: MarkupKind.Markdown,
703
+ value: tag.html ? `Built in [<${tag.name}>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${tag.name}) HTML tag.` : isCoreTag ? `Core Marko <${tag.name}> tag.` : nodeModuleName ? `Custom Marko tag discovered from the ["${nodeModuleName}"](${fileURIForTag}) npm package.` : `Custom Marko tag discovered from:
704
+
705
+ [${importer ? path2.relative(importer, fileForTag) : fileForTag}](${fileURIForTag})`
706
+ };
707
+ if (tag.description) {
708
+ documentation.value += `
709
+
710
+ ${tag.description}`;
711
+ }
712
+ const autocomplete = showAutoComplete ? (_a = tag.autocomplete) == null ? void 0 : _a[0] : void 0;
713
+ if (autocomplete) {
714
+ if (autocomplete.displayText) {
715
+ label = autocomplete.displayText;
716
+ }
717
+ if (autocomplete.description) {
718
+ documentation.value += `
719
+
720
+ ${autocomplete.description}`;
721
+ }
722
+ if (autocomplete.descriptionMoreURL) {
723
+ documentation.value += `
724
+
725
+ [More Info](${autocomplete.descriptionMoreURL})`;
726
+ }
727
+ }
728
+ return {
729
+ label,
730
+ documentation,
731
+ tags: tag.deprecated ? deprecated : void 0,
732
+ insertTextFormat: autocomplete ? InsertTextFormat2.Snippet : void 0,
733
+ kind: tag.html ? CompletionItemKind2.Property : CompletionItemKind2.Class,
734
+ textEdit: range && TextEdit2.replace(range, (autocomplete == null ? void 0 : autocomplete.snippet) || label)
735
+ };
736
+ }
737
+
738
+ // src/service/marko/complete/OpenTagName.ts
686
739
  function OpenTagName({
687
740
  document,
688
741
  lookup,
689
742
  parsed,
690
743
  node
691
744
  }) {
692
- const currentTemplateFilePath = getDocFile(document);
745
+ var _a;
746
+ const importer = getDocFile(document);
693
747
  const tag = node.parent;
694
- const tagNameLocation = parsed.locationAt(node);
695
- let tags;
696
- if (tag.type === 14 /* AttrTag */) {
748
+ const range = parsed.locationAt(node);
749
+ const isAttrTag = tag.type === 14 /* AttrTag */;
750
+ const result = [];
751
+ if (isAttrTag) {
697
752
  let parentTag = tag.owner;
698
753
  while ((parentTag == null ? void 0 : parentTag.type) === 14 /* AttrTag */)
699
754
  parentTag = parentTag.owner;
700
755
  const parentTagDef = parentTag && parentTag.nameText && lookup.getTag(parentTag.nameText);
701
- tags = parentTagDef && parentTagDef.nestedTags && Object.values(parentTagDef.nestedTags) || [];
702
- } else {
703
- tags = lookup.getTagsSorted().filter((it) => !it.isNestedTag);
704
- }
705
- return tags.filter((it) => !it.deprecated).filter((it) => it.name !== "*").filter((it) => /^[^_]/.test(it.name) || !/\/node_modules\//.test(it.filePath)).map((it) => {
706
- let label = it.isNestedTag ? `@${it.name}` : it.name;
707
- const fileForTag = it.template || it.renderer || it.filePath;
708
- const fileURIForTag = URI2.file(fileForTag).toString();
709
- const nodeModuleMatch = /\/node_modules\/((?:@[^/]+\/)?[^/]+)/.exec(fileForTag);
710
- const nodeModuleName = nodeModuleMatch && nodeModuleMatch[1];
711
- const isCoreTag = nodeModuleName === "marko";
712
- const documentation = {
713
- kind: MarkupKind.Markdown,
714
- value: it.html ? `Built in [<${it.name}>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/${it.name}) HTML tag.` : nodeModuleName ? isCoreTag ? `Core Marko [<${it.name}>](${fileURIForTag}) tag.` : `Custom Marko tag discovered from the ["${nodeModuleName}"](${fileURIForTag}) npm package.` : `Custom Marko tag discovered from:
715
-
716
- [${currentTemplateFilePath ? path2.relative(currentTemplateFilePath, fileForTag) : currentTemplateFilePath}](${fileURIForTag})`
717
- };
718
- if (it.description) {
719
- documentation.value += `
720
-
721
- ${it.description}`;
722
- }
723
- const autocomplete = it.autocomplete && it.autocomplete[0];
724
- if (autocomplete) {
725
- if (autocomplete.displayText) {
726
- label = autocomplete.displayText;
727
- }
728
- if (autocomplete.description) {
729
- documentation.value += `
730
-
731
- ${autocomplete.description}`;
756
+ if (parentTagDef) {
757
+ const { nestedTags } = parentTagDef;
758
+ for (const key in nestedTags) {
759
+ if (key !== "*") {
760
+ const tag2 = nestedTags[key];
761
+ result.push(getTagNameCompletion({
762
+ tag: tag2,
763
+ range,
764
+ importer,
765
+ showAutoComplete: true
766
+ }));
767
+ }
732
768
  }
733
- if (autocomplete.descriptionMoreURL) {
734
- documentation.value += `
735
-
736
- [More Info](${autocomplete.descriptionMoreURL})`;
769
+ }
770
+ } else {
771
+ const skipStatements = !(tag.concise && tag.parent.type === 0 /* Program */);
772
+ for (const tag2 of lookup.getTagsSorted()) {
773
+ if (!(tag2.name === "*" || tag2.isNestedTag || skipStatements && ((_a = tag2.parseOptions) == null ? void 0 : _a.statement) || tag2.name[0] === "_" && /^@?marko[/-]|[\\/]node_modules[\\/]/.test(tag2.filePath))) {
774
+ result.push(getTagNameCompletion({
775
+ tag: tag2,
776
+ range,
777
+ importer,
778
+ showAutoComplete: true
779
+ }));
737
780
  }
738
781
  }
739
- return {
740
- label,
741
- documentation,
742
- kind: CompletionItemKind2.Class,
743
- insertTextFormat: InsertTextFormat2.Snippet,
744
- textEdit: TextEdit2.replace(tagNameLocation, autocomplete && autocomplete.snippet || label)
745
- };
746
- });
782
+ }
783
+ return result;
747
784
  }
748
785
 
749
786
  // src/service/marko/complete/AttrName.ts
@@ -857,7 +894,7 @@ function AttrName({
857
894
  }
858
895
 
859
896
  // src/service/marko/complete/AttrValue.ts
860
- import path4 from "path";
897
+ import path3 from "path";
861
898
  import {
862
899
  CompletionItemKind as CompletionItemKind4,
863
900
  Range as Range2,
@@ -890,20 +927,20 @@ function isDocumentLinkAttr(doc, tag, attr) {
890
927
  }
891
928
 
892
929
  // src/utils/file-system.ts
893
- import path3 from "path";
894
930
  import fs from "fs/promises";
895
931
  import { FileType } from "vscode-css-languageservice";
932
+ import { fileURLToPath } from "url";
896
933
  var file_system_default = {
897
934
  stat,
898
935
  readDirectory
899
936
  };
900
- async function stat(fileName) {
901
- const stat2 = await fs.stat(fileName).catch(() => null);
937
+ async function stat(uri) {
902
938
  let type = FileType.Unknown;
903
- let ctime = 0;
904
- let mtime = 0;
905
- let size = 0;
906
- if (stat2) {
939
+ let ctime = -1;
940
+ let mtime = -1;
941
+ let size = -1;
942
+ try {
943
+ const stat2 = await fs.stat(fileURLToPath(uri));
907
944
  if (stat2.isDirectory())
908
945
  type = FileType.Directory;
909
946
  else if (stat2.isFile())
@@ -911,6 +948,7 @@ async function stat(fileName) {
911
948
  ctime = stat2.ctimeMs;
912
949
  mtime = stat2.mtimeMs;
913
950
  size = stat2.size;
951
+ } catch {
914
952
  }
915
953
  return {
916
954
  type,
@@ -919,20 +957,22 @@ async function stat(fileName) {
919
957
  size
920
958
  };
921
959
  }
922
- async function readDirectory(dir) {
923
- return (await Promise.all((await fs.readdir(dir).catch(() => [])).map(async (entry) => [entry, (await stat(path3.join(dir, entry))).type]))).filter(([, type]) => type !== FileType.Unknown);
960
+ async function readDirectory(uri) {
961
+ try {
962
+ const entries = await fs.readdir(fileURLToPath(uri));
963
+ const base = uri.at(-1) === "/" ? uri : `${uri}/`;
964
+ return (await Promise.all(entries.map(async (entry) => [entry, (await stat(new URL(entry, base).toString())).type]))).filter(([, type]) => type !== FileType.Unknown);
965
+ } catch {
966
+ return [];
967
+ }
924
968
  }
925
969
 
926
970
  // src/utils/resolve-url.ts
927
971
  function resolveUrl(to, base) {
928
972
  try {
929
- const baseUrl = new URL(base, "file://");
930
- const resolved = new URL(to, baseUrl);
931
- const { origin, protocol } = baseUrl;
932
- if (resolved.origin === origin && resolved.protocol === protocol) {
933
- return resolved.pathname + resolved.search + resolved.hash;
934
- }
935
- return resolved.toString();
973
+ const url = new URL(to, base);
974
+ if (url.protocol === "file:")
975
+ return url.toString();
936
976
  } catch {
937
977
  return void 0;
938
978
  }
@@ -957,28 +997,29 @@ async function AttrValue({
957
997
  start,
958
998
  end
959
999
  });
960
- let segmentStart = rawValue.lastIndexOf("/", relativeOffset);
1000
+ const segmentStart = rawValue.lastIndexOf("/", relativeOffset);
961
1001
  if (segmentStart === -1)
962
- segmentStart = relativeOffset;
963
- const resolveRequest = rawValue.slice(0, segmentStart) || ".";
964
- const dir = resolveUrl(resolveRequest, document.uri);
965
- if ((dir == null ? void 0 : dir[0]) === "/") {
1002
+ return;
1003
+ const req = rawValue.slice(0, segmentStart);
1004
+ const uri = resolveUrl(req, document.uri);
1005
+ if (uri) {
966
1006
  const result = [];
967
- const curDir = resolveRequest === "." ? dir : resolveUrl(".", document.uri);
968
- const curFile = curDir === dir ? path4.basename(document.uri) : void 0;
1007
+ const curFile = req === "." ? path3.basename(document.uri) : void 0;
969
1008
  const replaceRange = Range2.create(document.positionAt(start + segmentStart + 1), document.positionAt(start + rawValue.length));
970
- for (const [entry, type] of await file_system_default.readDirectory(dir)) {
1009
+ for (const [entry, type] of await file_system_default.readDirectory(uri)) {
971
1010
  if (entry[0] !== "." && entry !== curFile) {
972
- const isDir = type === FileType.Directory;
973
- const label = isDir ? `${entry}/` : entry;
974
- result.push({
975
- label,
976
- kind: isDir ? CompletionItemKind4.Folder : CompletionItemKind4.File,
977
- textEdit: TextEdit4.replace(replaceRange, label),
978
- command: isDir ? {
1011
+ result.push(type === FileType.Directory ? {
1012
+ label: `${entry}/`,
1013
+ kind: CompletionItemKind4.Folder,
1014
+ textEdit: TextEdit4.replace(replaceRange, `${entry}/`),
1015
+ command: {
979
1016
  title: "Suggest",
980
1017
  command: "editor.action.triggerSuggest"
981
- } : void 0
1018
+ }
1019
+ } : {
1020
+ label: entry,
1021
+ kind: CompletionItemKind4.File,
1022
+ textEdit: TextEdit4.replace(replaceRange, entry)
982
1023
  });
983
1024
  }
984
1025
  }
@@ -987,12 +1028,52 @@ async function AttrValue({
987
1028
  }
988
1029
  }
989
1030
 
1031
+ // src/service/marko/complete/Statement.ts
1032
+ import { TextEdit as TextEdit5 } from "vscode-languageserver";
1033
+ var importTagReg = /(['"])<((?:[^\1\\>]+|\\.)*)>?\1/g;
1034
+ function Statement({
1035
+ code,
1036
+ node,
1037
+ parsed,
1038
+ lookup,
1039
+ document
1040
+ }) {
1041
+ var _a;
1042
+ if (code[node.start] === "i") {
1043
+ importTagReg.lastIndex = 0;
1044
+ const value = parsed.read(node);
1045
+ const match = importTagReg.exec(value);
1046
+ if (match) {
1047
+ const importer = getDocFile(document);
1048
+ const [{ length }] = match;
1049
+ const range = parsed.locationAt({
1050
+ start: node.start + match.index + 1,
1051
+ end: node.start + match.index + length - 1
1052
+ });
1053
+ const result = [];
1054
+ for (const tag of lookup.getTagsSorted()) {
1055
+ if ((tag.template || tag.renderer) && !(tag.html || tag.parser || tag.translator || tag.isNestedTag || tag.name === "*" || ((_a = tag.parseOptions) == null ? void 0 : _a.statement) || /^@?marko[/-]/.test(tag.taglibId) || tag.name[0] === "_" && /[\\/]node_modules[\\/]/.test(tag.filePath))) {
1056
+ const completion = getTagNameCompletion({
1057
+ tag,
1058
+ importer
1059
+ });
1060
+ completion.label = `<${completion.label}>`;
1061
+ completion.textEdit = TextEdit5.replace(range, completion.label);
1062
+ result.push(completion);
1063
+ }
1064
+ }
1065
+ return result;
1066
+ }
1067
+ }
1068
+ }
1069
+
990
1070
  // src/service/marko/complete/index.ts
991
1071
  var handlers = {
992
1072
  Tag,
993
1073
  OpenTagName,
994
1074
  AttrName,
995
- AttrValue
1075
+ AttrValue,
1076
+ Statement
996
1077
  };
997
1078
  var doComplete = async (doc, params) => {
998
1079
  var _a;
@@ -1016,10 +1097,10 @@ var markoErrorRegExp = /^(.+?)(?:\((\d+)(?:\s*,\s*(\d+))?\))?: (.*)$/gm;
1016
1097
  var doValidate = (doc) => {
1017
1098
  const fsPath = getDocFile(doc);
1018
1099
  const diagnostics = [];
1019
- const { compiler, translator, cache: cache3 } = getCompilerInfo(doc);
1100
+ const { compiler, translator, cache: cache4 } = getCompilerInfo(doc);
1020
1101
  try {
1021
1102
  compiler.compileSync(doc.getText(), fsPath || "untitled.marko", {
1022
- cache: cache3,
1103
+ cache: cache4,
1023
1104
  translator,
1024
1105
  code: false,
1025
1106
  output: "source",
@@ -1037,8 +1118,64 @@ var doValidate = (doc) => {
1037
1118
  return diagnostics;
1038
1119
  };
1039
1120
 
1121
+ // src/utils/utils.ts
1122
+ import fs2 from "fs";
1123
+ import { URI as URI3 } from "vscode-uri";
1124
+ import { Position, Range as Range4 } from "vscode-languageserver";
1125
+ import { TextDocument } from "vscode-languageserver-textdocument";
1126
+ var START_OF_FILE = Range4.create(Position.create(0, 0), Position.create(0, 0));
1127
+ function createTextDocument(filename) {
1128
+ const uri = URI3.file(filename).toString();
1129
+ const content = fs2.readFileSync(filename, "utf-8");
1130
+ return TextDocument.create(uri, "plaintext", 0, content);
1131
+ }
1132
+
1133
+ // src/service/marko/hover/OpenTagName.ts
1134
+ function OpenTagName2({
1135
+ document,
1136
+ lookup,
1137
+ parsed,
1138
+ node
1139
+ }) {
1140
+ const importer = getDocFile(document);
1141
+ const tag = node.parent;
1142
+ const range = parsed.locationAt(node);
1143
+ const tagDef = tag.nameText && lookup.getTag(tag.nameText);
1144
+ if (tagDef) {
1145
+ const completion = getTagNameCompletion({
1146
+ tag: tagDef,
1147
+ range: START_OF_FILE,
1148
+ importer
1149
+ });
1150
+ return {
1151
+ range,
1152
+ contents: completion.documentation
1153
+ };
1154
+ }
1155
+ }
1156
+
1157
+ // src/service/marko/hover/index.ts
1158
+ var handlers2 = {
1159
+ OpenTagName: OpenTagName2
1160
+ };
1161
+ var doHover = async (doc, params) => {
1162
+ var _a;
1163
+ const parsed = parse2(doc);
1164
+ const offset = doc.offsetAt(params.position);
1165
+ const node = parsed.nodeAt(offset);
1166
+ return await ((_a = handlers2[NodeType[node.type]]) == null ? void 0 : _a.call(handlers2, {
1167
+ document: doc,
1168
+ params,
1169
+ parsed,
1170
+ offset,
1171
+ node,
1172
+ code: doc.getText(),
1173
+ ...getCompilerInfo(doc)
1174
+ }));
1175
+ };
1176
+
1040
1177
  // src/service/marko/definition/OpenTagName.ts
1041
- import path5 from "path";
1178
+ import path4 from "path";
1042
1179
  import { URI as URI4 } from "vscode-uri";
1043
1180
  import { Range as Range5, LocationLink } from "vscode-languageserver";
1044
1181
 
@@ -1066,20 +1203,8 @@ function escape(val) {
1066
1203
  return String(val).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
1067
1204
  }
1068
1205
 
1069
- // src/utils/utils.ts
1070
- import fs2 from "fs";
1071
- import { URI as URI3 } from "vscode-uri";
1072
- import { Position, Range as Range4 } from "vscode-languageserver";
1073
- import { TextDocument } from "vscode-languageserver-textdocument";
1074
- var START_OF_FILE = Range4.create(Position.create(0, 0), Position.create(0, 0));
1075
- function createTextDocument(filename) {
1076
- const uri = URI3.file(filename).toString();
1077
- const content = fs2.readFileSync(filename, "utf-8");
1078
- return TextDocument.create(uri, "plaintext", 0, content);
1079
- }
1080
-
1081
1206
  // src/service/marko/definition/OpenTagName.ts
1082
- function OpenTagName2({
1207
+ function OpenTagName3({
1083
1208
  lookup,
1084
1209
  parsed,
1085
1210
  node
@@ -1099,7 +1224,7 @@ function OpenTagName2({
1099
1224
  return;
1100
1225
  }
1101
1226
  const tagEntryFile = tagDef.template || tagDef.renderer || tagDef.filePath;
1102
- if (!path5.isAbsolute(tagEntryFile)) {
1227
+ if (!path4.isAbsolute(tagEntryFile)) {
1103
1228
  return;
1104
1229
  }
1105
1230
  if (/\/marko(?:-tag)?\.json$/.test(tagEntryFile)) {
@@ -1149,8 +1274,8 @@ function AttrName2({
1149
1274
  }
1150
1275
 
1151
1276
  // src/service/marko/definition/index.ts
1152
- var handlers2 = {
1153
- OpenTagName: OpenTagName2,
1277
+ var handlers3 = {
1278
+ OpenTagName: OpenTagName3,
1154
1279
  AttrName: AttrName2
1155
1280
  };
1156
1281
  var findDefinition = async (doc, params) => {
@@ -1158,7 +1283,7 @@ var findDefinition = async (doc, params) => {
1158
1283
  const parsed = parse2(doc);
1159
1284
  const offset = doc.offsetAt(params.position);
1160
1285
  const node = parsed.nodeAt(offset);
1161
- return await ((_a = handlers2[NodeType[node.type]]) == null ? void 0 : _a.call(handlers2, {
1286
+ return await ((_a = handlers3[NodeType[node.type]]) == null ? void 0 : _a.call(handlers3, {
1162
1287
  document: doc,
1163
1288
  params,
1164
1289
  parsed,
@@ -1172,7 +1297,7 @@ var findDefinition = async (doc, params) => {
1172
1297
  // src/service/marko/document-links/extract.ts
1173
1298
  import { DocumentLink } from "vscode-languageserver";
1174
1299
  import { URI as URI6 } from "vscode-uri";
1175
- var importTagReg = /(['"])<((?:[^\1\\>]+|\\.)*)>?\1/g;
1300
+ var importTagReg2 = /(['"])<((?:[^\1\\>]+|\\.)*)>?\1/g;
1176
1301
  function extractDocumentLinks(doc, parsed, lookup) {
1177
1302
  if (URI6.parse(doc.uri).scheme === "untitled") {
1178
1303
  return [];
@@ -1183,14 +1308,18 @@ function extractDocumentLinks(doc, parsed, lookup) {
1183
1308
  const read = (range) => code.slice(range.start, range.end);
1184
1309
  const visit = (node) => {
1185
1310
  switch (node.type) {
1311
+ case 14 /* AttrTag */:
1312
+ if (node.body) {
1313
+ for (const child of node.body) {
1314
+ visit(child);
1315
+ }
1316
+ }
1317
+ break;
1186
1318
  case 1 /* Tag */:
1187
1319
  if (node.attrs && node.nameText) {
1188
1320
  for (const attr of node.attrs) {
1189
1321
  if (isDocumentLinkAttr(doc, node, attr)) {
1190
- links.push(DocumentLink.create({
1191
- start: parsed.positionAt(attr.value.value.start),
1192
- end: parsed.positionAt(attr.value.value.end)
1193
- }, resolveUrl(read(attr.value.value).slice(1, -1), doc.uri)));
1322
+ links.push(DocumentLink.create(parsed.locationAt(attr.value.value), resolveUrl(read(attr.value.value).slice(1, -1), doc.uri)));
1194
1323
  }
1195
1324
  }
1196
1325
  }
@@ -1204,18 +1333,18 @@ function extractDocumentLinks(doc, parsed, lookup) {
1204
1333
  };
1205
1334
  for (const item of program.static) {
1206
1335
  if (item.type === 20 /* Statement */ && code[item.start] === "i") {
1207
- importTagReg.lastIndex = 0;
1336
+ importTagReg2.lastIndex = 0;
1208
1337
  const value = parsed.read(item);
1209
- const match = importTagReg.exec(value);
1338
+ const match = importTagReg2.exec(value);
1210
1339
  if (match) {
1211
1340
  const [{ length }, , tagName] = match;
1212
1341
  const tagDef = lookup.getTag(tagName);
1213
1342
  const fileForTag = tagDef && (tagDef.template || tagDef.renderer);
1214
1343
  if (fileForTag) {
1215
- links.push(DocumentLink.create({
1216
- start: parsed.positionAt(item.start + match.index),
1217
- end: parsed.positionAt(item.start + match.index + length)
1218
- }, fileForTag));
1344
+ links.push(DocumentLink.create(parsed.locationAt({
1345
+ start: item.start + match.index,
1346
+ end: item.start + match.index + length
1347
+ }), fileForTag));
1219
1348
  }
1220
1349
  }
1221
1350
  }
@@ -1238,14 +1367,55 @@ var findDocumentLinks = async (doc) => {
1238
1367
  return result;
1239
1368
  };
1240
1369
 
1241
- // src/service/marko/format.ts
1242
- import { Range as Range7, TextEdit as TextEdit5 } from "vscode-languageserver";
1370
+ // src/service/marko/document-symbols/extract.ts
1243
1371
  import { URI as URI7 } from "vscode-uri";
1372
+ import { SymbolInformation, SymbolKind } from "vscode-languageserver";
1373
+ function extractDocumentSymbols(doc, parsed, lookup) {
1374
+ if (URI7.parse(doc.uri).scheme === "untitled") {
1375
+ return [];
1376
+ }
1377
+ const symbols = [];
1378
+ const { program } = parsed;
1379
+ const visit = (node) => {
1380
+ var _a, _b;
1381
+ switch (node.type) {
1382
+ case 1 /* Tag */:
1383
+ case 14 /* AttrTag */:
1384
+ symbols.push(SymbolInformation.create((node.type === 14 /* AttrTag */ ? (_a = node.nameText) == null ? void 0 : _a.slice(node.nameText.indexOf("@")) : node.nameText) || "<${...}>", node.nameText && ((_b = lookup.getTag(node.nameText)) == null ? void 0 : _b.html) && SymbolKind.Property || SymbolKind.Class, parsed.locationAt(node), doc.uri));
1385
+ if (node.body) {
1386
+ for (const child of node.body) {
1387
+ visit(child);
1388
+ }
1389
+ }
1390
+ break;
1391
+ }
1392
+ };
1393
+ for (const item of program.body) {
1394
+ visit(item);
1395
+ }
1396
+ return symbols;
1397
+ }
1398
+
1399
+ // src/service/marko/document-symbols/index.ts
1400
+ var cache2 = /* @__PURE__ */ new WeakMap();
1401
+ var findDocumentSymbols = async (doc) => {
1402
+ const parsed = parse2(doc);
1403
+ let result = cache2.get(parsed);
1404
+ if (!result) {
1405
+ result = extractDocumentSymbols(doc, parsed, getCompilerInfo(doc).lookup);
1406
+ cache2.set(parsed, result);
1407
+ }
1408
+ return result;
1409
+ };
1410
+
1411
+ // src/service/marko/format.ts
1412
+ import { Range as Range7, TextEdit as TextEdit6 } from "vscode-languageserver";
1413
+ import { URI as URI8 } from "vscode-uri";
1244
1414
  import * as prettier from "prettier";
1245
1415
  import * as markoPrettier from "prettier-plugin-marko";
1246
1416
  var format2 = async (doc, params, cancel) => {
1247
1417
  try {
1248
- const { fsPath, scheme } = URI7.parse(doc.uri);
1418
+ const { fsPath, scheme } = URI8.parse(doc.uri);
1249
1419
  const text = doc.getText();
1250
1420
  const options = {
1251
1421
  parser: "marko",
@@ -1260,7 +1430,7 @@ var format2 = async (doc, params, cancel) => {
1260
1430
  if (cancel.isCancellationRequested)
1261
1431
  return;
1262
1432
  return [
1263
- TextEdit5.replace(Range7.create(doc.positionAt(0), doc.positionAt(text.length)), prettier.format(text, options))
1433
+ TextEdit6.replace(Range7.create(doc.positionAt(0), doc.positionAt(text.length)), prettier.format(text, options))
1264
1434
  ];
1265
1435
  } catch (e) {
1266
1436
  displayError(e);
@@ -1271,8 +1441,10 @@ var format2 = async (doc, params, cancel) => {
1271
1441
  var marko_default = {
1272
1442
  doComplete,
1273
1443
  doValidate,
1444
+ doHover,
1274
1445
  findDefinition,
1275
1446
  findDocumentLinks,
1447
+ findDocumentSymbols,
1276
1448
  format: format2
1277
1449
  };
1278
1450
 
@@ -1391,6 +1563,13 @@ function extractStyleSheets(code, program, lookup) {
1391
1563
  const visit = (node) => {
1392
1564
  var _a, _b;
1393
1565
  switch (node.type) {
1566
+ case 14 /* AttrTag */:
1567
+ if (node.body) {
1568
+ for (const child of node.body) {
1569
+ visit(child);
1570
+ }
1571
+ }
1572
+ break;
1394
1573
  case 1 /* Tag */:
1395
1574
  if (node.nameText === "style" && node.concise && node.attrs) {
1396
1575
  const block = node.attrs.at(-1);
@@ -1447,7 +1626,7 @@ function extractStyleSheets(code, program, lookup) {
1447
1626
  }
1448
1627
 
1449
1628
  // src/service/stylesheet/index.ts
1450
- var cache2 = /* @__PURE__ */ new WeakMap();
1629
+ var cache3 = /* @__PURE__ */ new WeakMap();
1451
1630
  var services = {
1452
1631
  css: getCSSLanguageService,
1453
1632
  less: getLESSLanguageService,
@@ -1535,15 +1714,39 @@ var StyleSheetService = {
1535
1714
  return result.length ? result : void 0;
1536
1715
  }
1537
1716
  },
1538
- findDocumentLinks(doc) {
1717
+ findDocumentSymbols(doc) {
1539
1718
  const infoByExt = getStyleSheetInfo(doc);
1540
1719
  const result = [];
1541
1720
  for (const ext in infoByExt) {
1542
1721
  const info = infoByExt[ext];
1543
1722
  const { service: service2, virtualDoc } = info;
1544
- for (const link of service2.findDocumentLinks(virtualDoc, info.parsed, {
1545
- resolveReference: resolveUrl
1546
- })) {
1723
+ for (const symbol of service2.findDocumentSymbols(virtualDoc, info.parsed)) {
1724
+ if (symbol.location.uri === doc.uri) {
1725
+ const range = getSourceRange(doc, info, symbol.location.range);
1726
+ if (range) {
1727
+ result.push({
1728
+ kind: symbol.kind,
1729
+ name: symbol.name,
1730
+ tags: symbol.tags,
1731
+ deprecated: symbol.deprecated,
1732
+ containerName: symbol.containerName,
1733
+ location: { uri: doc.uri, range }
1734
+ });
1735
+ }
1736
+ } else {
1737
+ result.push(symbol);
1738
+ }
1739
+ }
1740
+ }
1741
+ return result.length ? result : void 0;
1742
+ },
1743
+ async findDocumentLinks(doc) {
1744
+ const infoByExt = getStyleSheetInfo(doc);
1745
+ const result = [];
1746
+ for (const ext in infoByExt) {
1747
+ const info = infoByExt[ext];
1748
+ const { service: service2, virtualDoc } = info;
1749
+ for (const link of await service2.findDocumentLinks2(virtualDoc, info.parsed, { resolveReference: resolveUrl })) {
1547
1750
  const range = getSourceRange(doc, info, link.range);
1548
1751
  if (range) {
1549
1752
  result.push({
@@ -1776,7 +1979,7 @@ function getSourceRange(doc, info, range) {
1776
1979
  function getStyleSheetInfo(doc) {
1777
1980
  var _a;
1778
1981
  const parsed = parse2(doc);
1779
- let cached = cache2.get(parsed);
1982
+ let cached = cache3.get(parsed);
1780
1983
  if (!cached) {
1781
1984
  const results = extractStyleSheets(doc.getText(), parsed.program, getCompilerInfo(doc).lookup);
1782
1985
  cached = {};
@@ -1797,7 +2000,7 @@ function getStyleSheetInfo(doc) {
1797
2000
  parsed: service2.parseStylesheet(virtualDoc)
1798
2001
  };
1799
2002
  }
1800
- cache2.set(parsed, cached);
2003
+ cache3.set(parsed, cached);
1801
2004
  }
1802
2005
  return cached;
1803
2006
  }
@@ -1815,51 +2018,47 @@ var service = {
1815
2018
  }));
1816
2019
  },
1817
2020
  async doComplete(doc, params, cancel) {
1818
- const result = CompletionList3.create([], false);
2021
+ let items;
2022
+ let isIncomplete = false;
1819
2023
  try {
1820
- const requests = plugins.map((plugin) => {
2024
+ for (const pending of plugins.map((plugin) => {
1821
2025
  var _a;
1822
2026
  return (_a = plugin.doComplete) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1823
- });
1824
- for (const pending of requests) {
2027
+ })) {
1825
2028
  const cur = await pending;
1826
2029
  if (cancel.isCancellationRequested)
1827
2030
  return;
1828
2031
  if (cur) {
1829
- let items;
2032
+ let curItems;
1830
2033
  if (Array.isArray(cur)) {
1831
- items = cur;
2034
+ curItems = cur;
1832
2035
  } else {
1833
- items = cur.items;
1834
- result.isIncomplete || (result.isIncomplete = cur.isIncomplete);
2036
+ curItems = cur.items;
2037
+ isIncomplete || (isIncomplete = cur.isIncomplete);
1835
2038
  }
1836
- result.items.push(...items);
2039
+ items = items ? items.concat(curItems) : curItems;
1837
2040
  }
1838
2041
  }
1839
2042
  } catch (err) {
1840
- result.isIncomplete = true;
2043
+ isIncomplete = true;
1841
2044
  displayError(err);
1842
2045
  }
1843
- return result;
2046
+ if (items) {
2047
+ return CompletionList3.create(items, isIncomplete);
2048
+ }
1844
2049
  },
1845
2050
  async findDefinition(doc, params, cancel) {
1846
- const result = [];
2051
+ let result;
1847
2052
  try {
1848
- const requests = plugins.map((plugin) => {
2053
+ for (const pending of plugins.map((plugin) => {
1849
2054
  var _a;
1850
2055
  return (_a = plugin.findDefinition) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1851
- });
1852
- for (const pending of requests) {
2056
+ })) {
1853
2057
  const cur = await pending;
1854
2058
  if (cancel.isCancellationRequested)
1855
2059
  return;
1856
- if (cur) {
1857
- if (Array.isArray(cur)) {
1858
- result.push(...cur);
1859
- } else {
1860
- result.push(cur);
1861
- }
1862
- }
2060
+ if (cur)
2061
+ result = (result || []).concat(cur);
1863
2062
  }
1864
2063
  } catch (err) {
1865
2064
  displayError(err);
@@ -1869,21 +2068,33 @@ var service = {
1869
2068
  async findReferences(doc, params, cancel) {
1870
2069
  let result;
1871
2070
  try {
1872
- const requests = plugins.map((plugin) => {
2071
+ for (const pending of plugins.map((plugin) => {
1873
2072
  var _a;
1874
2073
  return (_a = plugin.findReferences) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1875
- });
1876
- for (const pending of requests) {
2074
+ })) {
1877
2075
  const cur = await pending;
1878
2076
  if (cancel.isCancellationRequested)
1879
2077
  return;
1880
- if (cur) {
1881
- if (result) {
1882
- result.push(...cur);
1883
- } else {
1884
- result = cur;
1885
- }
1886
- }
2078
+ if (cur)
2079
+ result = result ? result.concat(cur) : cur;
2080
+ }
2081
+ } catch (err) {
2082
+ displayError(err);
2083
+ }
2084
+ return result;
2085
+ },
2086
+ async findDocumentSymbols(doc, params, cancel) {
2087
+ let result;
2088
+ try {
2089
+ for (const pending of plugins.map((plugin) => {
2090
+ var _a;
2091
+ return (_a = plugin.findDocumentSymbols) == null ? void 0 : _a.call(plugin, doc, params, cancel);
2092
+ })) {
2093
+ const cur = await pending;
2094
+ if (cancel.isCancellationRequested)
2095
+ return;
2096
+ if (cur)
2097
+ result = result ? result.concat(cur) : cur;
1887
2098
  }
1888
2099
  } catch (err) {
1889
2100
  displayError(err);
@@ -1893,21 +2104,15 @@ var service = {
1893
2104
  async findDocumentLinks(doc, params, cancel) {
1894
2105
  let result;
1895
2106
  try {
1896
- const requests = plugins.map((plugin) => {
2107
+ for (const pending of plugins.map((plugin) => {
1897
2108
  var _a;
1898
2109
  return (_a = plugin.findDocumentLinks) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1899
- });
1900
- for (const pending of requests) {
2110
+ })) {
1901
2111
  const cur = await pending;
1902
2112
  if (cancel.isCancellationRequested)
1903
2113
  return;
1904
- if (cur) {
1905
- if (result) {
1906
- result.push(...cur);
1907
- } else {
1908
- result = cur;
1909
- }
1910
- }
2114
+ if (cur)
2115
+ result = result ? result.concat(cur) : cur;
1911
2116
  }
1912
2117
  } catch (err) {
1913
2118
  displayError(err);
@@ -1917,21 +2122,15 @@ var service = {
1917
2122
  async findDocumentHighlights(doc, params, cancel) {
1918
2123
  let result;
1919
2124
  try {
1920
- const requests = plugins.map((plugin) => {
2125
+ for (const pending of plugins.map((plugin) => {
1921
2126
  var _a;
1922
2127
  return (_a = plugin.findDocumentHighlights) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1923
- });
1924
- for (const pending of requests) {
2128
+ })) {
1925
2129
  const cur = await pending;
1926
2130
  if (cancel.isCancellationRequested)
1927
2131
  return;
1928
- if (cur) {
1929
- if (result) {
1930
- result.push(...cur);
1931
- } else {
1932
- result = cur;
1933
- }
1934
- }
2132
+ if (cur)
2133
+ result = result ? result.concat(cur) : cur;
1935
2134
  }
1936
2135
  } catch (err) {
1937
2136
  displayError(err);
@@ -1941,21 +2140,15 @@ var service = {
1941
2140
  async findDocumentColors(doc, params, cancel) {
1942
2141
  let result;
1943
2142
  try {
1944
- const requests = plugins.map((plugin) => {
2143
+ for (const pending of plugins.map((plugin) => {
1945
2144
  var _a;
1946
2145
  return (_a = plugin.findDocumentColors) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1947
- });
1948
- for (const pending of requests) {
2146
+ })) {
1949
2147
  const cur = await pending;
1950
2148
  if (cancel.isCancellationRequested)
1951
2149
  return;
1952
- if (cur) {
1953
- if (result) {
1954
- result.push(...cur);
1955
- } else {
1956
- result = cur;
1957
- }
1958
- }
2150
+ if (cur)
2151
+ result = result ? result.concat(cur) : cur;
1959
2152
  }
1960
2153
  } catch (err) {
1961
2154
  displayError(err);
@@ -1965,21 +2158,15 @@ var service = {
1965
2158
  async getColorPresentations(doc, params, cancel) {
1966
2159
  let result;
1967
2160
  try {
1968
- const requests = plugins.map((plugin) => {
2161
+ for (const pending of plugins.map((plugin) => {
1969
2162
  var _a;
1970
2163
  return (_a = plugin.getColorPresentations) == null ? void 0 : _a.call(plugin, doc, params, cancel);
1971
- });
1972
- for (const pending of requests) {
2164
+ })) {
1973
2165
  const cur = await pending;
1974
2166
  if (cancel.isCancellationRequested)
1975
2167
  return;
1976
- if (cur) {
1977
- if (result) {
1978
- result.push(...cur);
1979
- } else {
1980
- result = cur;
1981
- }
1982
- }
2168
+ if (cur)
2169
+ result = result ? result.concat(cur) : cur;
1983
2170
  }
1984
2171
  } catch (err) {
1985
2172
  displayError(err);
@@ -2005,41 +2192,32 @@ var service = {
2005
2192
  let changeAnnotations;
2006
2193
  let documentChanges;
2007
2194
  try {
2008
- const requests = plugins.map((plugin) => {
2195
+ for (const pending of plugins.map((plugin) => {
2009
2196
  var _a;
2010
2197
  return (_a = plugin.doRename) == null ? void 0 : _a.call(plugin, doc, params, cancel);
2011
- });
2012
- for (const pending of requests) {
2198
+ })) {
2013
2199
  const cur = await pending;
2014
2200
  if (cancel.isCancellationRequested)
2015
2201
  return;
2016
2202
  if (cur) {
2017
2203
  if (cur.changes) {
2018
2204
  if (changes) {
2205
+ changes = { ...changes };
2019
2206
  for (const uri in cur.changes) {
2020
- if (changes[uri]) {
2021
- changes[uri].push(...cur.changes[uri]);
2022
- } else {
2023
- changes[uri] = cur.changes[uri];
2024
- }
2207
+ changes[uri] = changes[uri] ? changes[uri].concat(cur.changes[uri]) : cur.changes[uri];
2025
2208
  }
2026
2209
  } else {
2027
2210
  changes = cur.changes;
2028
2211
  }
2029
2212
  }
2030
2213
  if (cur.changeAnnotations) {
2031
- if (changeAnnotations) {
2032
- Object.assign(changeAnnotations, cur.changeAnnotations);
2033
- } else {
2034
- changeAnnotations = cur.changeAnnotations;
2035
- }
2214
+ changeAnnotations = changeAnnotations ? {
2215
+ ...changeAnnotations,
2216
+ ...cur.changeAnnotations
2217
+ } : cur.changeAnnotations;
2036
2218
  }
2037
2219
  if (cur.documentChanges) {
2038
- if (documentChanges) {
2039
- documentChanges.push(...cur.documentChanges);
2040
- } else {
2041
- documentChanges = cur.documentChanges;
2042
- }
2220
+ documentChanges = documentChanges ? documentChanges.concat(cur.documentChanges) : cur.documentChanges;
2043
2221
  }
2044
2222
  }
2045
2223
  }
@@ -2055,19 +2233,17 @@ var service = {
2055
2233
  }
2056
2234
  },
2057
2235
  async doCodeActions(doc, params, cancel) {
2058
- const result = [];
2236
+ let result;
2059
2237
  try {
2060
- const requests = plugins.map((plugin) => {
2238
+ for (const pending of plugins.map((plugin) => {
2061
2239
  var _a;
2062
2240
  return (_a = plugin.doCodeActions) == null ? void 0 : _a.call(plugin, doc, params, cancel);
2063
- });
2064
- for (const pending of requests) {
2241
+ })) {
2065
2242
  const cur = await pending;
2066
2243
  if (cancel.isCancellationRequested)
2067
2244
  return;
2068
- if (cur) {
2069
- result.push(...cur);
2070
- }
2245
+ if (cur)
2246
+ result = result ? result.concat(cur) : cur;
2071
2247
  }
2072
2248
  } catch (err) {
2073
2249
  displayError(err);
@@ -2075,16 +2251,15 @@ var service = {
2075
2251
  return result;
2076
2252
  },
2077
2253
  async doValidate(doc) {
2078
- const result = [];
2254
+ let result;
2079
2255
  try {
2080
- const requests = plugins.map((plugin) => {
2256
+ for (const pending of plugins.map((plugin) => {
2081
2257
  var _a;
2082
2258
  return (_a = plugin.doValidate) == null ? void 0 : _a.call(plugin, doc);
2083
- });
2084
- for (const pending of requests) {
2259
+ })) {
2085
2260
  const cur = await pending;
2086
2261
  if (cur)
2087
- result.push(...cur);
2262
+ result = result ? result.concat(cur) : cur;
2088
2263
  }
2089
2264
  } catch (err) {
2090
2265
  displayError(err);
@@ -2126,6 +2301,7 @@ connection2.onInitialize(async (params) => {
2126
2301
  documentLinkProvider: { resolveProvider: false },
2127
2302
  colorProvider: true,
2128
2303
  documentHighlightProvider: true,
2304
+ documentSymbolProvider: true,
2129
2305
  completionProvider: {
2130
2306
  triggerCharacters: [
2131
2307
  ".",
@@ -2171,6 +2347,9 @@ connection2.onReferences(async (params, cancel) => {
2171
2347
  connection2.onDocumentLinks(async (params, cancel) => {
2172
2348
  return await service.findDocumentLinks(documents.get(params.textDocument.uri), params, cancel) || null;
2173
2349
  });
2350
+ connection2.onDocumentSymbol(async (params, cancel) => {
2351
+ return await service.findDocumentSymbols(documents.get(params.textDocument.uri), params, cancel) || null;
2352
+ });
2174
2353
  connection2.onDocumentHighlight(async (params, cancel) => {
2175
2354
  return await service.findDocumentHighlights(documents.get(params.textDocument.uri), params, cancel) || null;
2176
2355
  });