@nudojs/lsp 0.4.0 → 0.4.1

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/server.js CHANGED
@@ -73,7 +73,7 @@ var require_ms = __commonJS({
73
73
  options = options || {};
74
74
  var type = typeof val;
75
75
  if (type === "string" && val.length > 0) {
76
- return parse3(val);
76
+ return parse4(val);
77
77
  } else if (type === "number" && isFinite(val)) {
78
78
  return options.long ? fmtLong(val) : fmtShort(val);
79
79
  }
@@ -81,7 +81,7 @@ var require_ms = __commonJS({
81
81
  "val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
82
82
  );
83
83
  };
84
- function parse3(str) {
84
+ function parse4(str) {
85
85
  str = String(str);
86
86
  if (str.length > 100) {
87
87
  return;
@@ -17328,7 +17328,7 @@ var require_resolve_uri_umd = __commonJS({
17328
17328
  }
17329
17329
  url.path = path;
17330
17330
  }
17331
- function resolve2(input, base) {
17331
+ function resolve3(input, base) {
17332
17332
  if (!input && !base)
17333
17333
  return "";
17334
17334
  const url = parseUrl(input);
@@ -17381,7 +17381,7 @@ var require_resolve_uri_umd = __commonJS({
17381
17381
  return url.scheme + "//" + url.user + url.host + url.port + url.path + queryHash;
17382
17382
  }
17383
17383
  }
17384
- return resolve2;
17384
+ return resolve3;
17385
17385
  }));
17386
17386
  }
17387
17387
  });
@@ -17600,11 +17600,11 @@ var require_trace_mapping_umd = __commonJS({
17600
17600
  state.lastNeedle = needle;
17601
17601
  return state.lastIndex = binarySearch(haystack, needle, low, high);
17602
17602
  }
17603
- function parse3(map) {
17603
+ function parse4(map) {
17604
17604
  return typeof map === "string" ? JSON.parse(map) : map;
17605
17605
  }
17606
17606
  var FlattenMap = function(map, mapUrl) {
17607
- const parsed = parse3(map);
17607
+ const parsed = parse4(map);
17608
17608
  if (!("sections" in parsed)) {
17609
17609
  return new TraceMap(parsed, mapUrl);
17610
17610
  }
@@ -17668,7 +17668,7 @@ var require_trace_mapping_umd = __commonJS({
17668
17668
  }
17669
17669
  }
17670
17670
  function addSection(input, mapUrl, mappings, sources, sourcesContent, names, ignoreList, lineOffset, columnOffset, stopLine, stopColumn) {
17671
- const parsed = parse3(input);
17671
+ const parsed = parse4(input);
17672
17672
  if ("sections" in parsed) return recurse(...arguments);
17673
17673
  const map = new TraceMap(parsed, mapUrl);
17674
17674
  const sourcesOffset = sources.length;
@@ -17718,7 +17718,7 @@ var require_trace_mapping_umd = __commonJS({
17718
17718
  constructor(map, mapUrl) {
17719
17719
  const isString = typeof map === "string";
17720
17720
  if (!isString && map._decodedMemo) return map;
17721
- const parsed = parse3(map);
17721
+ const parsed = parse4(map);
17722
17722
  const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
17723
17723
  this.version = version;
17724
17724
  this.file = file;
@@ -17727,8 +17727,8 @@ var require_trace_mapping_umd = __commonJS({
17727
17727
  this.sources = sources;
17728
17728
  this.sourcesContent = sourcesContent;
17729
17729
  this.ignoreList = parsed.ignoreList || parsed.x_google_ignoreList || void 0;
17730
- const resolve2 = resolver(mapUrl, sourceRoot);
17731
- this.resolvedSources = sources.map(resolve2);
17730
+ const resolve3 = resolver(mapUrl, sourceRoot);
17731
+ this.resolvedSources = sources.map(resolve3);
17732
17732
  const { mappings } = parsed;
17733
17733
  if (typeof mappings === "string") {
17734
17734
  this._encoded = mappings;
@@ -28350,14 +28350,14 @@ var require_lib6 = __commonJS({
28350
28350
  super.checkParams(node, false, true);
28351
28351
  this.scope.exit();
28352
28352
  }
28353
- forwardNoArrowParamsConversionAt(node, parse4) {
28353
+ forwardNoArrowParamsConversionAt(node, parse5) {
28354
28354
  let result;
28355
28355
  if (this.state.noArrowParamsConversionAt.includes(this.offsetToSourcePos(node.start))) {
28356
28356
  this.state.noArrowParamsConversionAt.push(this.state.start);
28357
- result = parse4();
28357
+ result = parse5();
28358
28358
  this.state.noArrowParamsConversionAt.pop();
28359
28359
  } else {
28360
- result = parse4();
28360
+ result = parse5();
28361
28361
  }
28362
28362
  return result;
28363
28363
  }
@@ -39752,7 +39752,7 @@ var require_lib6 = __commonJS({
39752
39752
  return result;
39753
39753
  }
39754
39754
  };
39755
- function parse3(input, options) {
39755
+ function parse4(input, options) {
39756
39756
  var _options;
39757
39757
  if (((_options = options) == null ? void 0 : _options.sourceType) === "unambiguous") {
39758
39758
  options = Object.assign({}, options);
@@ -39839,7 +39839,7 @@ var require_lib6 = __commonJS({
39839
39839
  }
39840
39840
  return cls;
39841
39841
  }
39842
- exports.parse = parse3;
39842
+ exports.parse = parse4;
39843
39843
  exports.parseExpression = parseExpression;
39844
39844
  exports.tokTypes = tokTypes;
39845
39845
  }
@@ -41773,7 +41773,7 @@ var require_introspection = __commonJS({
41773
41773
  exports.isStatic = isStatic;
41774
41774
  exports.matchesPattern = matchesPattern;
41775
41775
  exports.referencesImport = referencesImport;
41776
- exports.resolve = resolve2;
41776
+ exports.resolve = resolve3;
41777
41777
  exports.willIMaybeExecuteBefore = willIMaybeExecuteBefore;
41778
41778
  var _t = require_lib3();
41779
41779
  var {
@@ -42031,7 +42031,7 @@ var require_introspection = __commonJS({
42031
42031
  nodeMap.set(target.node, result);
42032
42032
  return result;
42033
42033
  }
42034
- function resolve2(dangerous, resolved) {
42034
+ function resolve3(dangerous, resolved) {
42035
42035
  return _resolve.call(this, dangerous, resolved) || this;
42036
42036
  }
42037
42037
  function _resolve(dangerous, resolved) {
@@ -43396,6 +43396,7 @@ import {
43396
43396
  InlayHintKind
43397
43397
  } from "vscode-languageserver/node";
43398
43398
  import { TextDocument } from "vscode-languageserver-textdocument";
43399
+ import { readFileSync as readFileSync3 } from "fs";
43399
43400
  import {
43400
43401
  getTypeAtPosition,
43401
43402
  getHoverAtPosition as getHoverAtPosition2,
@@ -43405,23 +43406,27 @@ import {
43405
43406
  isNudoTargetPath,
43406
43407
  collectAbsInlays as collectAbsInlays2
43407
43408
  } from "@nudojs/service";
43408
- import { parse as parse2 } from "@nudojs/parser";
43409
+ import { parse as parse3 } from "@nudojs/parser";
43409
43410
 
43410
43411
  // src/symbols.ts
43411
43412
  var import_traverse = __toESM(require_lib8(), 1);
43413
+ import { parse } from "@nudojs/parser";
43414
+ import { existsSync, readFileSync, readdirSync, statSync } from "fs";
43415
+ import { dirname, join, resolve } from "path";
43412
43416
  function locFromNode(node) {
43413
43417
  return {
43414
43418
  start: { line: node.loc?.start.line ?? 1, column: node.loc?.start.column ?? 0 },
43415
43419
  end: { line: node.loc?.end.line ?? 1, column: node.loc?.end.column ?? 0 }
43416
43420
  };
43417
43421
  }
43422
+ function traverseFn() {
43423
+ return typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
43424
+ }
43418
43425
  function buildSymbolTable(ast, uri) {
43419
43426
  const definitions = /* @__PURE__ */ new Map();
43420
43427
  const references = [];
43421
- const traverseFn = typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
43422
43428
  try {
43423
- traverseFn(ast, {
43424
- // Collect definitions
43429
+ traverseFn()(ast, {
43425
43430
  FunctionDeclaration(path) {
43426
43431
  if (path.node.id) {
43427
43432
  definitions.set(path.node.id.name, {
@@ -43452,7 +43457,6 @@ function buildSymbolTable(ast, uri) {
43452
43457
  });
43453
43458
  }
43454
43459
  },
43455
- // Collect references
43456
43460
  Identifier(path) {
43457
43461
  if (path.parentPath?.node.type === "FunctionDeclaration" && path.parentPath.node.id === path.node) return;
43458
43462
  if (path.parentPath?.node.type === "VariableDeclarator" && path.parentPath.node.id === path.node) return;
@@ -43476,9 +43480,8 @@ function findReferences(symbolTable, name) {
43476
43480
  }
43477
43481
  function findIdentifierAtPosition(ast, line, column) {
43478
43482
  let found = null;
43479
- const traverseFn = typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
43480
43483
  try {
43481
- traverseFn(ast, {
43484
+ traverseFn()(ast, {
43482
43485
  Identifier(path) {
43483
43486
  const loc = path.node.loc;
43484
43487
  if (!loc) return;
@@ -43492,6 +43495,349 @@ function findIdentifierAtPosition(ast, line, column) {
43492
43495
  }
43493
43496
  return found;
43494
43497
  }
43498
+ var SymbolKind = {
43499
+ Function: 12,
43500
+ Variable: 13,
43501
+ Class: 5,
43502
+ Method: 6,
43503
+ Property: 7,
43504
+ Constant: 14
43505
+ };
43506
+ function rangeOfLoc(loc) {
43507
+ return {
43508
+ start: { line: loc.start.line - 1, character: loc.start.column },
43509
+ end: { line: loc.end.line - 1, character: loc.end.column }
43510
+ };
43511
+ }
43512
+ function rangeOfNode(node) {
43513
+ return rangeOfLoc(locFromNode(node));
43514
+ }
43515
+ function documentSymbols(ast) {
43516
+ const out = [];
43517
+ const program = ast.program ?? ast;
43518
+ const body = program.body ?? [];
43519
+ const pushFn = (fnNode, idNode, exported) => {
43520
+ if (!idNode?.name) return;
43521
+ const range = rangeOfNode(fnNode);
43522
+ const selection = rangeOfNode(idNode);
43523
+ const children = [];
43524
+ const params = (fnNode.params ?? []).map((p) => p?.name ?? (p?.type === "RestElement" ? `...${p.argument?.name}` : "_")).filter(Boolean);
43525
+ out.push({
43526
+ name: idNode.name,
43527
+ detail: `(${params.join(", ")})${exported ? " export" : ""}`,
43528
+ kind: SymbolKind.Function,
43529
+ range,
43530
+ selectionRange: selection,
43531
+ children: children.length ? children : void 0
43532
+ });
43533
+ };
43534
+ for (const stmt of body) {
43535
+ let decl = stmt;
43536
+ let exported = false;
43537
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
43538
+ decl = stmt.declaration;
43539
+ exported = true;
43540
+ } else if (stmt.type === "ExportDefaultDeclaration") {
43541
+ decl = stmt.declaration;
43542
+ exported = true;
43543
+ }
43544
+ if (decl?.type === "FunctionDeclaration") {
43545
+ pushFn(decl, decl.id, exported);
43546
+ continue;
43547
+ }
43548
+ if (decl?.type === "ClassDeclaration" && decl.id) {
43549
+ out.push({
43550
+ name: decl.id.name,
43551
+ detail: exported ? "export class" : "class",
43552
+ kind: SymbolKind.Class,
43553
+ range: rangeOfNode(decl),
43554
+ selectionRange: rangeOfNode(decl.id),
43555
+ children: (decl.body?.body ?? []).flatMap((m) => {
43556
+ const key = m.key?.name ?? m.key?.value;
43557
+ if (!key || m.type !== "ClassMethod" && m.type !== "ClassProperty") return [];
43558
+ return [{
43559
+ name: String(key),
43560
+ kind: m.type === "ClassMethod" ? SymbolKind.Method : SymbolKind.Property,
43561
+ range: rangeOfNode(m),
43562
+ selectionRange: rangeOfNode(m.key)
43563
+ }];
43564
+ })
43565
+ });
43566
+ continue;
43567
+ }
43568
+ if (decl?.type === "VariableDeclaration") {
43569
+ for (const d of decl.declarations ?? []) {
43570
+ if (d.id?.type !== "Identifier") continue;
43571
+ const init = d.init;
43572
+ const isFn = init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression");
43573
+ out.push({
43574
+ name: d.id.name,
43575
+ detail: exported ? "export const" : "const",
43576
+ kind: isFn ? SymbolKind.Function : SymbolKind.Variable,
43577
+ range: rangeOfNode(d),
43578
+ selectionRange: rangeOfNode(d.id)
43579
+ });
43580
+ }
43581
+ }
43582
+ }
43583
+ return out;
43584
+ }
43585
+ function collectImportBindings(ast, fromFile) {
43586
+ const map = /* @__PURE__ */ new Map();
43587
+ const program = ast.program ?? ast;
43588
+ for (const stmt of program.body ?? []) {
43589
+ if (stmt.type !== "ImportDeclaration") continue;
43590
+ const spec = stmt.source.value;
43591
+ const resolvedPath = resolveRelativeModule(spec, fromFile);
43592
+ for (const specNode of stmt.specifiers ?? []) {
43593
+ if (specNode.type === "ImportSpecifier") {
43594
+ const imported = specNode.imported.type === "Identifier" ? specNode.imported.name : String(specNode.imported.value);
43595
+ map.set(specNode.local.name, { spec, importedName: imported, resolvedPath });
43596
+ } else if (specNode.type === "ImportDefaultSpecifier") {
43597
+ map.set(specNode.local.name, { spec, importedName: "default", resolvedPath });
43598
+ } else if (specNode.type === "ImportNamespaceSpecifier") {
43599
+ map.set(specNode.local.name, { spec, importedName: "*", resolvedPath });
43600
+ }
43601
+ }
43602
+ }
43603
+ return map;
43604
+ }
43605
+ function resolveRelativeModule(spec, fromFile) {
43606
+ if (!spec.startsWith(".") && !spec.startsWith("/")) return void 0;
43607
+ const base = resolve(dirname(fromFile), spec);
43608
+ for (const cand of [
43609
+ base,
43610
+ `${base}.js`,
43611
+ `${base}.cjs`,
43612
+ `${base}.mjs`,
43613
+ `${base}.ts`,
43614
+ `${base}.mts`,
43615
+ `${base}.cts`,
43616
+ join(base, "index.js"),
43617
+ join(base, "index.mjs"),
43618
+ join(base, "index.ts")
43619
+ ]) {
43620
+ try {
43621
+ if (existsSync(cand) && statSync(cand).isFile()) return cand;
43622
+ } catch {
43623
+ }
43624
+ }
43625
+ return void 0;
43626
+ }
43627
+ function readSourceIfExists(filePath) {
43628
+ try {
43629
+ return readFileSync(filePath, "utf-8");
43630
+ } catch {
43631
+ return void 0;
43632
+ }
43633
+ }
43634
+ function findExportDefinition(targetSource, exportName) {
43635
+ let ast;
43636
+ try {
43637
+ ast = parse(targetSource);
43638
+ } catch {
43639
+ return null;
43640
+ }
43641
+ const program = ast.program ?? ast;
43642
+ for (const stmt of program.body ?? []) {
43643
+ let decl = stmt;
43644
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
43645
+ decl = stmt.declaration;
43646
+ } else if (stmt.type === "ExportDefaultDeclaration") {
43647
+ decl = stmt.declaration;
43648
+ if (exportName === "default") {
43649
+ if (decl?.type === "FunctionDeclaration" && decl.id) {
43650
+ return { name: decl.id.name, kind: "function", loc: locFromNode(decl.id) };
43651
+ }
43652
+ if (decl?.type === "ClassDeclaration" && decl.id) {
43653
+ return { name: decl.id.name, kind: "class", loc: locFromNode(decl.id) };
43654
+ }
43655
+ if (decl?.type === "Identifier") {
43656
+ return { name: decl.name, kind: "variable", loc: locFromNode(decl) };
43657
+ }
43658
+ return null;
43659
+ }
43660
+ continue;
43661
+ }
43662
+ if (decl?.type === "FunctionDeclaration" && decl.id?.name === exportName) {
43663
+ return { name: decl.id.name, kind: "function", loc: locFromNode(decl.id) };
43664
+ }
43665
+ if (decl?.type === "ClassDeclaration" && decl.id?.name === exportName) {
43666
+ return { name: decl.id.name, kind: "class", loc: locFromNode(decl.id) };
43667
+ }
43668
+ if (decl?.type === "VariableDeclaration") {
43669
+ for (const d of decl.declarations ?? []) {
43670
+ if (d.id?.type === "Identifier" && d.id.name === exportName) {
43671
+ return { name: d.id.name, kind: "variable", loc: locFromNode(d.id) };
43672
+ }
43673
+ }
43674
+ }
43675
+ if (stmt.type === "ExportNamedDeclaration" && !stmt.declaration && stmt.specifiers) {
43676
+ for (const s of stmt.specifiers) {
43677
+ if (s.type !== "ExportSpecifier") continue;
43678
+ const exported = s.exported.type === "Identifier" ? s.exported.name : String(s.exported.value);
43679
+ if (exported !== exportName) continue;
43680
+ const local = s.local.type === "Identifier" ? s.local.name : String(s.local.value);
43681
+ const table = buildSymbolTable(ast, "");
43682
+ const def = table.definitions.get(local);
43683
+ if (def) return { name: local, kind: def.kind, loc: def.loc };
43684
+ }
43685
+ }
43686
+ }
43687
+ return null;
43688
+ }
43689
+ function findCrossFileDefinition(fromFile, source, ident) {
43690
+ let ast;
43691
+ try {
43692
+ ast = parse(source);
43693
+ } catch {
43694
+ return null;
43695
+ }
43696
+ const bindings = collectImportBindings(ast, fromFile);
43697
+ const binding = bindings.get(ident);
43698
+ if (!binding?.resolvedPath) return null;
43699
+ if (binding.importedName === "*") return null;
43700
+ const targetSource = readSourceIfExists(binding.resolvedPath);
43701
+ if (targetSource === void 0) return null;
43702
+ const def = findExportDefinition(targetSource, binding.importedName);
43703
+ if (!def) return null;
43704
+ return { filePath: binding.resolvedPath, loc: def.loc, name: def.name };
43705
+ }
43706
+ function findCrossFileReferences(definingFile, exportName, options = {}) {
43707
+ const out = [];
43708
+ const root = options.rootDir ?? dirname(definingFile);
43709
+ const maxFiles = options.maxFiles ?? 200;
43710
+ const candidates = new Set(options.extraFiles ?? []);
43711
+ try {
43712
+ collectJsFiles(root, candidates, maxFiles, 0);
43713
+ } catch {
43714
+ }
43715
+ const definingNorm = resolve(definingFile);
43716
+ for (const cand of candidates) {
43717
+ if (resolve(cand) === definingNorm) continue;
43718
+ const src = readSourceIfExists(cand);
43719
+ if (src === void 0) continue;
43720
+ let ast;
43721
+ try {
43722
+ ast = parse(src);
43723
+ } catch {
43724
+ continue;
43725
+ }
43726
+ const bindings = collectImportBindings(ast, cand);
43727
+ for (const [local, b] of bindings) {
43728
+ if (!b.resolvedPath || resolve(b.resolvedPath) !== definingNorm) continue;
43729
+ if (b.importedName !== exportName && b.importedName !== "*") continue;
43730
+ const table = buildSymbolTable(ast, cand);
43731
+ for (const ref of table.references) {
43732
+ if (ref.name !== local) continue;
43733
+ out.push({ name: local, loc: ref.loc, uri: cand });
43734
+ }
43735
+ }
43736
+ }
43737
+ return out;
43738
+ }
43739
+ function collectJsFiles(dir, out, max, depth) {
43740
+ if (out.size >= max || depth > 6) return;
43741
+ let entries;
43742
+ try {
43743
+ entries = readdirSync(dir);
43744
+ } catch {
43745
+ return;
43746
+ }
43747
+ for (const name of entries) {
43748
+ if (name === "node_modules" || name === ".git" || name === "dist") continue;
43749
+ const full = join(dir, name);
43750
+ try {
43751
+ const st = statSync(full);
43752
+ if (st.isDirectory()) {
43753
+ collectJsFiles(full, out, max, depth + 1);
43754
+ } else if (/\.(js|mjs|cjs|ts|mts)$/.test(name) && !/\.d\.ts$/.test(name)) {
43755
+ out.add(full);
43756
+ }
43757
+ } catch {
43758
+ }
43759
+ }
43760
+ }
43761
+ function resolveDefinition(fromFile, source, ident) {
43762
+ const ast = parse(source);
43763
+ const table = buildSymbolTable(ast, fromFile);
43764
+ const local = findDefinition(table, ident);
43765
+ if (local) return { filePath: fromFile, loc: local.loc, name: local.name };
43766
+ return findCrossFileDefinition(fromFile, source, ident);
43767
+ }
43768
+ function resolveReferences(fromFile, source, ident, options = {}) {
43769
+ const includeDecl = options.includeDeclaration !== false;
43770
+ const ast = parse(source);
43771
+ const table = buildSymbolTable(ast, fromFile);
43772
+ const localRefs = findReferences(table, ident);
43773
+ const localDef = findDefinition(table, ident);
43774
+ if (localDef) {
43775
+ const exportName = localDef.name;
43776
+ const isExported = isExportedName(ast, exportName);
43777
+ const cross2 = isExported ? findCrossFileReferences(fromFile, exportName, { extraFiles: options.extraFiles }) : [];
43778
+ const decl = includeDecl ? [{ name: localDef.name, loc: localDef.loc, uri: fromFile }] : [];
43779
+ return [...decl, ...localRefs, ...cross2];
43780
+ }
43781
+ const crossDef = findCrossFileDefinition(fromFile, source, ident);
43782
+ if (!crossDef) return localRefs;
43783
+ const defSource = readSourceIfExists(crossDef.filePath) ?? "";
43784
+ let defAst = null;
43785
+ try {
43786
+ defAst = parse(defSource);
43787
+ } catch {
43788
+ defAst = null;
43789
+ }
43790
+ const defLocalRefs = [];
43791
+ let defDecl = [];
43792
+ if (defAst) {
43793
+ const defTable = buildSymbolTable(defAst, crossDef.filePath);
43794
+ defLocalRefs.push(...findReferences(defTable, crossDef.name));
43795
+ const d = findDefinition(defTable, crossDef.name);
43796
+ if (d && includeDecl) {
43797
+ defDecl = [{ name: d.name, loc: d.loc, uri: crossDef.filePath }];
43798
+ }
43799
+ }
43800
+ const cross = findCrossFileReferences(crossDef.filePath, crossDef.name, {
43801
+ extraFiles: [...options.extraFiles ?? [], fromFile]
43802
+ });
43803
+ const seen = /* @__PURE__ */ new Set();
43804
+ const all = [];
43805
+ for (const r of [...defDecl, ...defLocalRefs, ...localRefs, ...cross]) {
43806
+ const key = `${r.uri ?? ""}:${r.loc.start.line}:${r.loc.start.column}`;
43807
+ if (seen.has(key)) continue;
43808
+ seen.add(key);
43809
+ all.push(r);
43810
+ }
43811
+ return all;
43812
+ }
43813
+ function isExportedName(ast, name) {
43814
+ const program = ast.program ?? ast;
43815
+ for (const stmt of program.body ?? []) {
43816
+ if (stmt.type === "ExportNamedDeclaration") {
43817
+ if (stmt.declaration) {
43818
+ const d = stmt.declaration;
43819
+ if (d.type === "FunctionDeclaration" && d.id?.name === name) return true;
43820
+ if (d.type === "ClassDeclaration" && d.id?.name === name) return true;
43821
+ if (d.type === "VariableDeclaration") {
43822
+ for (const dec of d.declarations ?? []) {
43823
+ if (dec.id?.type === "Identifier" && dec.id.name === name) return true;
43824
+ }
43825
+ }
43826
+ }
43827
+ for (const s of stmt.specifiers ?? []) {
43828
+ if (s.type !== "ExportSpecifier") continue;
43829
+ const exported = s.exported.type === "Identifier" ? s.exported.name : String(s.exported.value);
43830
+ if (exported === name) return true;
43831
+ }
43832
+ }
43833
+ if (stmt.type === "ExportDefaultDeclaration") {
43834
+ const d = stmt.declaration;
43835
+ if (d?.id?.name === name) return true;
43836
+ if (d?.type === "Identifier" && d.name === name) return true;
43837
+ }
43838
+ }
43839
+ return false;
43840
+ }
43495
43841
 
43496
43842
  // src/semantic-tokens.ts
43497
43843
  import {
@@ -43501,7 +43847,7 @@ import {
43501
43847
  } from "@nudojs/service";
43502
43848
 
43503
43849
  // src/validation.ts
43504
- import { dirname, resolve as resolvePath } from "path";
43850
+ import { dirname as dirname2, resolve as resolvePath } from "path";
43505
43851
  import {
43506
43852
  analyzeFile,
43507
43853
  analyzeFileAsync,
@@ -43540,7 +43886,7 @@ function registerNudoImportDeps(filePath, source) {
43540
43886
  const imports = extractNudoImports(source);
43541
43887
  for (const imp of imports) {
43542
43888
  if (!imp.spec.startsWith(".") && !imp.spec.startsWith("/")) continue;
43543
- const dep = normPath(resolvePath(dirname(filePath), imp.spec));
43889
+ const dep = normPath(resolvePath(dirname2(filePath), imp.spec));
43544
43890
  let set = nudoDepParents.get(dep);
43545
43891
  if (!set) {
43546
43892
  set = /* @__PURE__ */ new Set();
@@ -43714,8 +44060,8 @@ async function validateText(filePath, uri, text, version, deps, propagate = fals
43714
44060
  }
43715
44061
 
43716
44062
  // src/agent-tools.ts
43717
- import { readFileSync } from "fs";
43718
- import { resolve } from "path";
44063
+ import { readFileSync as readFileSync2 } from "fs";
44064
+ import { resolve as resolve2 } from "path";
43719
44065
  import {
43720
44066
  analyzeFile as analyzeFile2,
43721
44067
  buildCaseDirective,
@@ -43723,14 +44069,14 @@ import {
43723
44069
  collectAbsInlays,
43724
44070
  serializeInferJson
43725
44071
  } from "@nudojs/service";
43726
- import { parse } from "@nudojs/parser";
44072
+ import { parse as parse2 } from "@nudojs/parser";
43727
44073
  import { T, typeValueToString, checkSource as checkSource2, serializeCheckJson, pTrue as pTrue2 } from "@nudojs/core";
43728
44074
  function textResult(text) {
43729
44075
  return { content: [{ type: "text", text }] };
43730
44076
  }
43731
44077
  function normalizeFilePath(file) {
43732
44078
  const path = file.startsWith("file://") ? decodeURIComponent(file.slice(7)) : file;
43733
- return resolve(path);
44079
+ return resolve2(path);
43734
44080
  }
43735
44081
  var BARE_PRIMITIVES = /* @__PURE__ */ new Set(["number", "string", "boolean", "bigint", "symbol"]);
43736
44082
  function splitTopLevelUnion(expr) {
@@ -43795,7 +44141,7 @@ function injectBindings(source, bindings) {
43795
44141
  const applied = [];
43796
44142
  const unapplied = [];
43797
44143
  if (bindings.length === 0) return { source, applied, unapplied };
43798
- const ast = parse(source);
44144
+ const ast = parse2(source);
43799
44145
  const declLines = /* @__PURE__ */ new Map();
43800
44146
  for (const stmt of ast.program.body) {
43801
44147
  const names = /* @__PURE__ */ new Set();
@@ -43834,7 +44180,7 @@ function injectBindings(source, bindings) {
43834
44180
  function readSource(filePath, deps = {}) {
43835
44181
  const open = deps.getOpenText?.(filePath);
43836
44182
  if (open) return open.text;
43837
- return (deps.readFile ?? ((p) => readFileSync(p, "utf-8")))(filePath);
44183
+ return (deps.readFile ?? ((p) => readFileSync2(p, "utf-8")))(filePath);
43838
44184
  }
43839
44185
  function analysisError(err) {
43840
44186
  return textResult(`Error: ${err.message}`);
@@ -44054,6 +44400,8 @@ connection.onInitialize((_params) => ({
44054
44400
  definitionProvider: true,
44055
44401
  referencesProvider: true,
44056
44402
  renameProvider: true,
44403
+ documentSymbolProvider: true,
44404
+ workspaceSymbolProvider: true,
44057
44405
  codeActionProvider: {
44058
44406
  codeActionKinds: ["quickfix"]
44059
44407
  },
@@ -44306,84 +44654,174 @@ connection.languages.inlayHint.on((params) => {
44306
44654
  return [];
44307
44655
  }
44308
44656
  });
44657
+ function filePathToUri(filePath) {
44658
+ if (filePath.startsWith("file://")) return filePath;
44659
+ const normalized = filePath.replace(/\\/g, "/");
44660
+ return normalized.startsWith("/") ? `file://${normalized}` : `file:///${normalized}`;
44661
+ }
44662
+ function navigationExtraFiles(currentPath) {
44663
+ const open = documents.all().map((d) => uriToFilePath(d.uri));
44664
+ const all = /* @__PURE__ */ new Set([...open, ...knownFiles, currentPath]);
44665
+ return [...all];
44666
+ }
44667
+ connection.onDocumentSymbol((params) => {
44668
+ const document2 = documents.get(params.textDocument.uri);
44669
+ if (!document2) return [];
44670
+ if (!isNudoFile(params.textDocument.uri)) return [];
44671
+ try {
44672
+ const ast = parse3(document2.getText());
44673
+ const toLsp = (s) => ({
44674
+ name: s.name,
44675
+ detail: s.detail,
44676
+ kind: s.kind,
44677
+ range: s.range,
44678
+ selectionRange: s.selectionRange,
44679
+ children: s.children?.map(toLsp)
44680
+ });
44681
+ return documentSymbols(ast).map(toLsp);
44682
+ } catch {
44683
+ return [];
44684
+ }
44685
+ });
44686
+ connection.onWorkspaceSymbol((params) => {
44687
+ const query = params.query.toLowerCase();
44688
+ const out = [];
44689
+ const files = /* @__PURE__ */ new Set([...knownFiles]);
44690
+ for (const d of documents.all()) files.add(uriToFilePath(d.uri));
44691
+ for (const filePath of files) {
44692
+ const doc = documents.all().find((d) => uriToFilePath(d.uri) === filePath);
44693
+ let source;
44694
+ if (doc) {
44695
+ source = doc.getText();
44696
+ } else {
44697
+ try {
44698
+ source = readFileSync3(filePath, "utf-8");
44699
+ } catch {
44700
+ source = void 0;
44701
+ }
44702
+ }
44703
+ if (source === void 0) continue;
44704
+ try {
44705
+ const ast = parse3(source);
44706
+ const uri = doc?.uri ?? filePathToUri(filePath);
44707
+ for (const sym of documentSymbols(ast)) {
44708
+ if (query && !sym.name.toLowerCase().includes(query)) continue;
44709
+ out.push({
44710
+ name: sym.name,
44711
+ kind: sym.kind,
44712
+ location: {
44713
+ uri,
44714
+ range: {
44715
+ start: { line: sym.selectionRange.start.line, character: sym.selectionRange.start.character },
44716
+ end: { line: sym.selectionRange.end.line, character: sym.selectionRange.end.character }
44717
+ }
44718
+ }
44719
+ });
44720
+ }
44721
+ } catch {
44722
+ }
44723
+ }
44724
+ return out;
44725
+ });
44309
44726
  connection.onDefinition((params) => {
44310
44727
  const document2 = documents.get(params.textDocument.uri);
44311
44728
  if (!document2) return null;
44312
44729
  if (!isNudoFile(params.textDocument.uri)) return null;
44313
44730
  const source = document2.getText();
44314
- const ast = parse2(source);
44315
- const table = buildSymbolTable(ast, params.textDocument.uri);
44731
+ const filePath = uriToFilePath(params.textDocument.uri);
44316
44732
  const line = params.position.line + 1;
44317
44733
  const column = params.position.character;
44318
- const identAtPos = findIdentifierAtPosition(ast, line, column);
44319
- if (!identAtPos) return null;
44320
- const def = findDefinition(table, identAtPos);
44321
- if (!def) return null;
44322
- return {
44323
- uri: params.textDocument.uri,
44324
- range: {
44325
- start: { line: def.loc.start.line - 1, character: def.loc.start.column },
44326
- end: { line: def.loc.end.line - 1, character: def.loc.end.column }
44327
- }
44328
- };
44734
+ try {
44735
+ const ast = parse3(source);
44736
+ const identAtPos = findIdentifierAtPosition(ast, line, column);
44737
+ if (!identAtPos) return null;
44738
+ const def = resolveDefinition(filePath, source, identAtPos);
44739
+ if (!def) return null;
44740
+ return {
44741
+ uri: filePathToUri(def.filePath),
44742
+ range: {
44743
+ start: { line: def.loc.start.line - 1, character: def.loc.start.column },
44744
+ end: { line: def.loc.end.line - 1, character: def.loc.end.column }
44745
+ }
44746
+ };
44747
+ } catch {
44748
+ return null;
44749
+ }
44329
44750
  });
44330
44751
  connection.onReferences((params) => {
44331
44752
  const document2 = documents.get(params.textDocument.uri);
44332
44753
  if (!document2) return [];
44333
44754
  if (!isNudoFile(params.textDocument.uri)) return [];
44334
44755
  const source = document2.getText();
44335
- const ast = parse2(source);
44336
- const table = buildSymbolTable(ast, params.textDocument.uri);
44756
+ const filePath = uriToFilePath(params.textDocument.uri);
44337
44757
  const line = params.position.line + 1;
44338
44758
  const column = params.position.character;
44339
- const identAtPos = findIdentifierAtPosition(ast, line, column);
44340
- if (!identAtPos) return [];
44341
- const refs = findReferences(table, identAtPos);
44342
- return refs.map((ref) => ({
44343
- uri: params.textDocument.uri,
44344
- range: {
44345
- start: { line: ref.loc.start.line - 1, character: ref.loc.start.column },
44346
- end: { line: ref.loc.end.line - 1, character: ref.loc.end.column }
44347
- }
44348
- }));
44759
+ try {
44760
+ const ast = parse3(source);
44761
+ const identAtPos = findIdentifierAtPosition(ast, line, column);
44762
+ if (!identAtPos) return [];
44763
+ const refs = resolveReferences(filePath, source, identAtPos, {
44764
+ extraFiles: navigationExtraFiles(filePath),
44765
+ includeDeclaration: params.context?.includeDeclaration !== false
44766
+ });
44767
+ return refs.map((ref) => ({
44768
+ uri: ref.uri ? filePathToUri(ref.uri) : params.textDocument.uri,
44769
+ range: {
44770
+ start: { line: ref.loc.start.line - 1, character: ref.loc.start.column },
44771
+ end: { line: ref.loc.end.line - 1, character: ref.loc.end.column }
44772
+ }
44773
+ }));
44774
+ } catch {
44775
+ return [];
44776
+ }
44349
44777
  });
44350
44778
  connection.onRenameRequest((params) => {
44351
44779
  const document2 = documents.get(params.textDocument.uri);
44352
44780
  if (!document2) return null;
44353
44781
  if (!isNudoFile(params.textDocument.uri)) return null;
44354
44782
  const source = document2.getText();
44355
- const ast = parse2(source);
44356
- const table = buildSymbolTable(ast, params.textDocument.uri);
44783
+ const filePath = uriToFilePath(params.textDocument.uri);
44357
44784
  const line = params.position.line + 1;
44358
44785
  const column = params.position.character;
44359
- const identAtPos = findIdentifierAtPosition(ast, line, column);
44360
- if (!identAtPos) return null;
44361
- const def = findDefinition(table, identAtPos);
44362
- const refs = findReferences(table, identAtPos);
44363
- const edits = [];
44364
- if (def) {
44365
- edits.push({
44366
- range: {
44367
- start: { line: def.loc.start.line - 1, character: def.loc.start.column },
44368
- end: { line: def.loc.end.line - 1, character: def.loc.end.column }
44369
- },
44370
- newText: params.newName
44371
- });
44372
- }
44373
- for (const ref of refs) {
44374
- edits.push({
44375
- range: {
44376
- start: { line: ref.loc.start.line - 1, character: ref.loc.start.column },
44377
- end: { line: ref.loc.end.line - 1, character: ref.loc.end.column }
44378
- },
44379
- newText: params.newName
44380
- });
44381
- }
44382
- return {
44383
- changes: {
44384
- [params.textDocument.uri]: edits
44786
+ try {
44787
+ const ast = parse3(source);
44788
+ const identAtPos = findIdentifierAtPosition(ast, line, column);
44789
+ if (!identAtPos) return null;
44790
+ const def = resolveDefinition(filePath, source, identAtPos);
44791
+ const refs = resolveReferences(filePath, source, identAtPos, {
44792
+ extraFiles: navigationExtraFiles(filePath)
44793
+ });
44794
+ const changes = {};
44795
+ const push = (uri, loc) => {
44796
+ const list = changes[uri] ??= [];
44797
+ list.push({
44798
+ range: {
44799
+ start: { line: loc.start.line - 1, character: loc.start.column },
44800
+ end: { line: loc.end.line - 1, character: loc.end.column }
44801
+ },
44802
+ newText: params.newName
44803
+ });
44804
+ };
44805
+ if (def) {
44806
+ push(filePathToUri(def.filePath), def.loc);
44807
+ }
44808
+ for (const ref of refs) {
44809
+ push(ref.uri ? filePathToUri(ref.uri) : params.textDocument.uri, ref.loc);
44810
+ }
44811
+ for (const uri of Object.keys(changes)) {
44812
+ const seen = /* @__PURE__ */ new Set();
44813
+ changes[uri] = changes[uri].filter((e) => {
44814
+ const key = `${e.range.start.line}:${e.range.start.character}:${e.range.end.line}:${e.range.end.character}`;
44815
+ if (seen.has(key)) return false;
44816
+ seen.add(key);
44817
+ return true;
44818
+ });
44385
44819
  }
44386
- };
44820
+ if (Object.keys(changes).length === 0) return null;
44821
+ return { changes };
44822
+ } catch {
44823
+ return null;
44824
+ }
44387
44825
  });
44388
44826
  connection.onCodeAction((params) => {
44389
44827
  const document2 = documents.get(params.textDocument.uri);
@@ -44419,7 +44857,7 @@ connection.onSignatureHelp((params) => {
44419
44857
  const column = params.position.character;
44420
44858
  const cases = getActiveCasesForUri(params.textDocument.uri);
44421
44859
  try {
44422
- const ast = parse2(source);
44860
+ const ast = parse3(source);
44423
44861
  const callInfo = findEnclosingCall(ast, line, column);
44424
44862
  if (!callInfo) return null;
44425
44863
  const fnType = getTypeAtPosition(filePath, source, callInfo.calleeLine, callInfo.calleeCol, cases);