@abco20/btxml-checker 0.1.0 → 0.1.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.cjs CHANGED
@@ -15878,6 +15878,7 @@ var require_node3 = __commonJS({
15878
15878
  });
15879
15879
 
15880
15880
  // src/server.ts
15881
+ var import_promises2 = require("fs/promises");
15881
15882
  var import_node_path3 = __toESM(require("path"), 1);
15882
15883
  var import_node_url3 = require("url");
15883
15884
 
@@ -40915,14 +40916,14 @@ function analyzeAssignment(context) {
40915
40916
  }
40916
40917
  const left = expression.left;
40917
40918
  const accessKind = expression.operator === ":=" ? "declare" : expression.operator === "=" ? "write" : "readwrite";
40918
- const access = {
40919
+ const access2 = {
40919
40920
  name: left.name,
40920
40921
  kind: accessKind,
40921
40922
  range: left.range,
40922
40923
  identifier: left,
40923
40924
  statementIndex: context.statementIndex
40924
40925
  };
40925
- identifiers.push(access);
40926
+ identifiers.push(access2);
40926
40927
  const existingSymbol = environment.symbols.get(left.name);
40927
40928
  if (expression.operator === ":=" && !existingSymbol) {
40928
40929
  const symbol2 = {
@@ -40939,14 +40940,14 @@ function analyzeAssignment(context) {
40939
40940
  };
40940
40941
  environment.symbols.set(left.name, symbol2);
40941
40942
  context.resolvedIdentifiers.push({
40942
- access,
40943
+ access: access2,
40943
40944
  resolution: { kind: "symbol", symbol: symbol2 }
40944
40945
  });
40945
40946
  introducedSymbols.push(symbol2);
40946
40947
  return rightType;
40947
40948
  }
40948
40949
  if (!existingSymbol) {
40949
- context.resolvedIdentifiers.push({ access, resolution: { kind: "unknown" } });
40950
+ context.resolvedIdentifiers.push({ access: access2, resolution: { kind: "unknown" } });
40950
40951
  reportDiagnostic(
40951
40952
  context,
40952
40953
  "assignment-to-unknown-variable",
@@ -40958,7 +40959,7 @@ function analyzeAssignment(context) {
40958
40959
  return ERROR_TYPE;
40959
40960
  }
40960
40961
  context.resolvedIdentifiers.push({
40961
- access,
40962
+ access: access2,
40962
40963
  resolution: { kind: "symbol", symbol: existingSymbol }
40963
40964
  });
40964
40965
  if (expression.operator === "=") {
@@ -40993,18 +40994,18 @@ function analyzeAssignment(context) {
40993
40994
  return resultType;
40994
40995
  }
40995
40996
  function analyzeReadIdentifier(context, name, range) {
40996
- const access = {
40997
+ const access2 = {
40997
40998
  name,
40998
40999
  kind: "read",
40999
41000
  range,
41000
41001
  identifier: context.expression,
41001
41002
  statementIndex: context.statementIndex
41002
41003
  };
41003
- context.identifiers.push(access);
41004
+ context.identifiers.push(access2);
41004
41005
  const enumValue = context.environment.enums.get(name);
41005
41006
  if (enumValue !== void 0) {
41006
41007
  context.resolvedIdentifiers.push({
41007
- access,
41008
+ access: access2,
41008
41009
  resolution: { kind: "enum", name, value: enumValue }
41009
41010
  });
41010
41011
  return NUMBER_TYPE;
@@ -41012,13 +41013,13 @@ function analyzeReadIdentifier(context, name, range) {
41012
41013
  const symbol2 = context.environment.symbols.get(name);
41013
41014
  if (symbol2) {
41014
41015
  context.resolvedIdentifiers.push({
41015
- access,
41016
+ access: access2,
41016
41017
  resolution: { kind: "symbol", symbol: symbol2 }
41017
41018
  });
41018
41019
  return symbol2.type;
41019
41020
  }
41020
- context.resolvedIdentifiers.push({ access, resolution: { kind: "unknown" } });
41021
- context.unknownIdentifiers.push(access);
41021
+ context.resolvedIdentifiers.push({ access: access2, resolution: { kind: "unknown" } });
41022
+ context.unknownIdentifiers.push(access2);
41022
41023
  return UNKNOWN_TYPE;
41023
41024
  }
41024
41025
  function compoundAssignmentResult(left, right, operator) {
@@ -44796,40 +44797,40 @@ function createWorkspaceService(options = {}) {
44796
44797
  return documents2.get(uri);
44797
44798
  },
44798
44799
  getDiagnostics(uri) {
44799
- const access = getDocumentAccess(uri);
44800
- if (!access || !access.emitDiagnostics) return { diagnostics: [] };
44800
+ const access2 = getDocumentAccess(uri);
44801
+ if (!access2 || !access2.emitDiagnostics) return { diagnostics: [] };
44801
44802
  const result = {
44802
- diagnostics: access.snapshot.diagnostics.diagnostics
44803
+ diagnostics: access2.snapshot.diagnostics.diagnostics
44803
44804
  };
44804
- if (access.snapshot.diagnostics.partial) result.partial = true;
44805
+ if (access2.snapshot.diagnostics.partial) result.partial = true;
44805
44806
  return result;
44806
44807
  },
44807
44808
  getWorkspaceDiagnostics() {
44808
44809
  return { diagnostics: [...getRuntimeState()?.diagnostics ?? runtimeDiagnostics] };
44809
44810
  },
44810
44811
  getSemanticDocumentView(uri) {
44811
- const access = getDocumentAccess(uri);
44812
- if (!access) return { diagnostics: [] };
44813
- return getSemanticDocumentResult(uri, access.effectiveConfig);
44812
+ const access2 = getDocumentAccess(uri);
44813
+ if (!access2) return { diagnostics: [] };
44814
+ return getSemanticDocumentResult(uri, access2.effectiveConfig);
44814
44815
  },
44815
44816
  getNodeCatalog(uri) {
44816
- const access = getDocumentAccess(uri);
44817
- if (!access) return { models: [] };
44818
- return { models: getNodeCatalogModels(access.snapshot) };
44817
+ const access2 = getDocumentAccess(uri);
44818
+ if (!access2) return { models: [] };
44819
+ return { models: getNodeCatalogModels(access2.snapshot) };
44819
44820
  },
44820
44821
  getSemanticNode(uri, nodeId) {
44821
- const access = getDocumentAccess(uri);
44822
- if (!access) return {};
44823
- const result = getSemanticDocumentResult(uri, access.effectiveConfig);
44822
+ const access2 = getDocumentAccess(uri);
44823
+ if (!access2) return {};
44824
+ const result = getSemanticDocumentResult(uri, access2.effectiveConfig);
44824
44825
  return {
44825
44826
  node: result.view?.nodes.find((candidate) => candidate.nodeId === nodeId)
44826
44827
  };
44827
44828
  },
44828
44829
  getNodeUsageAt(uri, position) {
44829
- const access = getDocumentAccess(uri);
44830
- if (!access) return {};
44831
- const view = getSemanticView(access.snapshot);
44832
- if (!access.snapshot.parsed || !view) return {};
44830
+ const access2 = getDocumentAccess(uri);
44831
+ if (!access2) return {};
44832
+ const view = getSemanticView(access2.snapshot);
44833
+ if (!access2.snapshot.parsed || !view) return {};
44833
44834
  const node = findSemanticNodeAtPosition(view, position);
44834
44835
  if (!node) return {};
44835
44836
  return {
@@ -44859,16 +44860,16 @@ function createWorkspaceService(options = {}) {
44859
44860
  if (genericKind === "Action" || genericKind === "Condition") {
44860
44861
  return { capable: false, reason: "generic-leaf" };
44861
44862
  }
44862
- const access = getDocumentAccess(uri);
44863
- if (!access) {
44863
+ const access2 = getDocumentAccess(uri);
44864
+ if (!access2) {
44864
44865
  return { capable: false, reason: "unknown-model" };
44865
44866
  }
44866
- const usage = resolveNodeUsage(access.snapshot.semantic, {
44867
+ const usage = resolveNodeUsage(access2.snapshot.semantic, {
44867
44868
  element: createSyntheticElement(tagName, attributes),
44868
- documentRoot: access.snapshot.parsed?.root,
44869
+ documentRoot: access2.snapshot.parsed?.root,
44869
44870
  uri,
44870
- config: access.snapshot.config,
44871
- policy: access.snapshot.nodeUsagePolicy
44871
+ config: access2.snapshot.config,
44872
+ policy: access2.snapshot.nodeUsagePolicy
44872
44873
  });
44873
44874
  if (usage.model.status !== "resolved") {
44874
44875
  return {
@@ -44883,20 +44884,20 @@ function createWorkspaceService(options = {}) {
44883
44884
  };
44884
44885
  },
44885
44886
  getPortInfoAt(uri, position) {
44886
- const access = getDocumentAccess(uri);
44887
- if (!access) return {};
44888
- const view = getSemanticView(access.snapshot);
44889
- if (!access.snapshot.parsed || !view) return {};
44887
+ const access2 = getDocumentAccess(uri);
44888
+ if (!access2) return {};
44889
+ const view = getSemanticView(access2.snapshot);
44890
+ if (!access2.snapshot.parsed || !view) return {};
44890
44891
  const binding = findSemanticPortBindingAtPosition(view, position);
44891
44892
  const nodeUsageAt = this.getNodeUsageAt(uri, position);
44892
44893
  const node = nodeUsageAt.node;
44893
- const usage = binding && node ? resolvePortUsage(access.snapshot.semantic, {
44894
+ const usage = binding && node ? resolvePortUsage(access2.snapshot.semantic, {
44894
44895
  element: node.usage.element,
44895
- documentRoot: access.snapshot.parsed.root,
44896
+ documentRoot: access2.snapshot.parsed.root,
44896
44897
  attributeName: binding.portName,
44897
44898
  uri,
44898
- config: access.snapshot.config,
44899
- policy: access.snapshot.nodeUsagePolicy
44899
+ config: access2.snapshot.config,
44900
+ policy: access2.snapshot.nodeUsagePolicy
44900
44901
  }) : void 0;
44901
44902
  return {
44902
44903
  node,
@@ -44907,72 +44908,72 @@ function createWorkspaceService(options = {}) {
44907
44908
  };
44908
44909
  },
44909
44910
  getFormattingEdits(uri) {
44910
- const access = getDocumentAccess(uri);
44911
- if (!access) return { edits: [], diagnostics: [] };
44911
+ const access2 = getDocumentAccess(uri);
44912
+ if (!access2) return { edits: [], diagnostics: [] };
44912
44913
  return languageService.getFormattingEdits({
44913
- document: access.document,
44914
- config: access.effectiveConfig
44914
+ document: access2.document,
44915
+ config: access2.effectiveConfig
44915
44916
  });
44916
44917
  },
44917
44918
  getCompletions(uri, position, triggerCharacter) {
44918
- const access = getDocumentAccess(uri);
44919
- if (!access) return { items: [] };
44919
+ const access2 = getDocumentAccess(uri);
44920
+ if (!access2) return { items: [] };
44920
44921
  return languageService.getCompletions({
44921
- document: access.document,
44922
+ document: access2.document,
44922
44923
  position,
44923
44924
  workspace: getRuntimeWorkspace(),
44924
44925
  triggerCharacter,
44925
- config: access.effectiveConfig
44926
+ config: access2.effectiveConfig
44926
44927
  });
44927
44928
  },
44928
44929
  getHover(uri, position) {
44929
- const access = getDocumentAccess(uri);
44930
- if (!access) return {};
44930
+ const access2 = getDocumentAccess(uri);
44931
+ if (!access2) return {};
44931
44932
  return languageService.getHover({
44932
- document: access.document,
44933
+ document: access2.document,
44933
44934
  position,
44934
44935
  workspace: getRuntimeWorkspace(),
44935
- config: access.effectiveConfig
44936
+ config: access2.effectiveConfig
44936
44937
  });
44937
44938
  },
44938
44939
  getDefinition(uri, position) {
44939
- const access = getDocumentAccess(uri);
44940
- if (!access) return { locations: [] };
44940
+ const access2 = getDocumentAccess(uri);
44941
+ if (!access2) return { locations: [] };
44941
44942
  return languageService.getDefinition({
44942
- document: access.document,
44943
+ document: access2.document,
44943
44944
  position,
44944
44945
  workspace: getRuntimeWorkspace(),
44945
- config: access.effectiveConfig
44946
+ config: access2.effectiveConfig
44946
44947
  });
44947
44948
  },
44948
44949
  getReferences(uri, position) {
44949
- const access = getDocumentAccess(uri);
44950
- if (!access) return { locations: [] };
44950
+ const access2 = getDocumentAccess(uri);
44951
+ if (!access2) return { locations: [] };
44951
44952
  return languageService.getReferences({
44952
- document: access.document,
44953
+ document: access2.document,
44953
44954
  position,
44954
44955
  workspace: getRuntimeWorkspace(),
44955
- config: access.effectiveConfig
44956
+ config: access2.effectiveConfig
44956
44957
  });
44957
44958
  },
44958
44959
  getDocumentSymbols(uri) {
44959
- const access = getDocumentAccess(uri);
44960
- if (!access) return { symbols: [] };
44960
+ const access2 = getDocumentAccess(uri);
44961
+ if (!access2) return { symbols: [] };
44961
44962
  return languageService.getDocumentSymbols({
44962
- document: access.document,
44963
+ document: access2.document,
44963
44964
  workspace: getRuntimeWorkspace(),
44964
- config: access.effectiveConfig
44965
+ config: access2.effectiveConfig
44965
44966
  });
44966
44967
  },
44967
44968
  getCodeActions(uri, range, diagnostics) {
44968
- const access = getDocumentAccess(uri);
44969
- if (!access) return { actions: [] };
44969
+ const access2 = getDocumentAccess(uri);
44970
+ if (!access2) return { actions: [] };
44970
44971
  return languageService.getCodeActions({
44971
- document: access.document,
44972
+ document: access2.document,
44972
44973
  range,
44973
44974
  diagnostics,
44974
44975
  workspace: getRuntimeWorkspace(),
44975
- config: access.effectiveConfig
44976
+ config: access2.effectiveConfig
44976
44977
  });
44977
44978
  },
44978
44979
  getLanguageService() {
@@ -45727,10 +45728,11 @@ var connection = (0, import_node4.createConnection)(import_node4.ProposedFeature
45727
45728
  var documents = new import_node4.TextDocuments(TextDocument);
45728
45729
  var openUris = /* @__PURE__ */ new Set();
45729
45730
  var debounceTimers = /* @__PURE__ */ new Map();
45730
- var documentWorkspaceRoots = /* @__PURE__ */ new Map();
45731
- var workspaceServices = /* @__PURE__ */ new Map();
45732
- var loadedWorkspaceRoots = /* @__PURE__ */ new Set();
45733
- var workspaceRoots = [process.cwd()];
45731
+ var documentProjectKeys = /* @__PURE__ */ new Map();
45732
+ var projectBindings = /* @__PURE__ */ new Map();
45733
+ var projectServices = /* @__PURE__ */ new Map();
45734
+ var loadedProjects = /* @__PURE__ */ new Set();
45735
+ var workspaceRoots = [import_node_path3.default.resolve(process.cwd())];
45734
45736
  var settings = {
45735
45737
  configPath: void 0,
45736
45738
  diagnosticsEnabled: true,
@@ -45751,41 +45753,45 @@ function normalizeDocumentUri(uri) {
45751
45753
  return uri;
45752
45754
  }
45753
45755
  }
45754
- function normalizeWorkspaceRoot2(rootPath) {
45755
- const normalized = import_node_path3.default.resolve(rootPath).replace(/\\/g, "/").replace(/\/$/, "");
45756
+ function normalizeFsPath(fsPath) {
45757
+ const normalized = import_node_path3.default.resolve(fsPath).replace(/\\/g, "/").replace(/\/$/, "");
45756
45758
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
45757
45759
  }
45760
+ function normalizeLanguageId(languageId) {
45761
+ return languageId === "btcpp-xml" ? "btcpp-xml" : "xml";
45762
+ }
45763
+ function toDocumentSnapshot(document) {
45764
+ return {
45765
+ text: document.getText(),
45766
+ version: document.version,
45767
+ languageId: normalizeLanguageId(document.languageId)
45768
+ };
45769
+ }
45770
+ function isWithinPath(parentPath, targetPath) {
45771
+ const normalizedParent = normalizeFsPath(parentPath);
45772
+ const normalizedTarget = normalizeFsPath(targetPath);
45773
+ return normalizedTarget === normalizedParent || normalizedTarget.startsWith(`${normalizedParent}/`);
45774
+ }
45758
45775
  function setWorkspaceRoots(nextRoots) {
45759
- const deduped = /* @__PURE__ */ new Set();
45776
+ const deduped = /* @__PURE__ */ new Map();
45760
45777
  for (const root of nextRoots) {
45761
45778
  const resolvedRoot = import_node_path3.default.resolve(root);
45762
- const normalizedRoot = normalizeWorkspaceRoot2(resolvedRoot);
45779
+ const normalizedRoot = normalizeFsPath(resolvedRoot);
45763
45780
  if (deduped.has(normalizedRoot)) continue;
45764
- deduped.add(normalizedRoot);
45781
+ deduped.set(normalizedRoot, resolvedRoot);
45765
45782
  }
45766
- workspaceRoots = [...deduped].map((normalizedRoot) => {
45767
- return nextRoots.find(
45768
- (root) => normalizeWorkspaceRoot2(import_node_path3.default.resolve(root)) === normalizedRoot
45769
- );
45770
- }).filter((root) => Boolean(root)) || [process.cwd()];
45783
+ workspaceRoots = deduped.size > 0 ? [...deduped.values()] : [import_node_path3.default.resolve(process.cwd())];
45771
45784
  }
45772
45785
  function collectWorkspaceRoots(params) {
45773
45786
  const roots = params.workspaceFolders?.map((folder) => fileUriToPath4(folder.uri)) || (params.rootUri ? [fileUriToPath4(params.rootUri)] : params.rootPath ? [params.rootPath] : []);
45774
45787
  return roots.length > 0 ? roots : [process.cwd()];
45775
45788
  }
45776
- function getWorkspaceService(rootPath) {
45777
- let workspace = workspaceServices.get(rootPath);
45778
- if (workspace) return workspace;
45779
- workspace = createNodeWorkspaceService({ cwd: rootPath });
45780
- workspaceServices.set(rootPath, workspace);
45781
- return workspace;
45782
- }
45783
45789
  function resolveWorkspaceRootForPath(fsPath) {
45784
- const normalizedPath = normalizeWorkspaceRoot2(fsPath);
45785
- let matchedRoot = workspaceRoots[0] || process.cwd();
45790
+ const normalizedPath = normalizeFsPath(fsPath);
45791
+ let matchedRoot = workspaceRoots[0] || import_node_path3.default.resolve(process.cwd());
45786
45792
  let matchedLength = -1;
45787
45793
  for (const root of workspaceRoots) {
45788
- const normalizedRoot = normalizeWorkspaceRoot2(root);
45794
+ const normalizedRoot = normalizeFsPath(root);
45789
45795
  if (normalizedPath !== normalizedRoot && !normalizedPath.startsWith(`${normalizedRoot}/`)) {
45790
45796
  continue;
45791
45797
  }
@@ -45796,18 +45802,21 @@ function resolveWorkspaceRootForPath(fsPath) {
45796
45802
  return matchedRoot;
45797
45803
  }
45798
45804
  function resolveWorkspaceRootForUri(uri) {
45799
- if (!uri.startsWith("file://")) return workspaceRoots[0] || process.cwd();
45805
+ if (!uri.startsWith("file://")) return workspaceRoots[0] || import_node_path3.default.resolve(process.cwd());
45800
45806
  try {
45801
45807
  return resolveWorkspaceRootForPath(fileUriToPath4(uri));
45802
45808
  } catch {
45803
- return workspaceRoots[0] || process.cwd();
45809
+ return workspaceRoots[0] || import_node_path3.default.resolve(process.cwd());
45804
45810
  }
45805
45811
  }
45806
- function getDocumentWorkspaceRoot(uri) {
45807
- return documentWorkspaceRoots.get(uri) || resolveWorkspaceRootForUri(uri);
45808
- }
45809
- function getWorkspaceForDocumentUri(uri) {
45810
- return getWorkspaceService(getDocumentWorkspaceRoot(uri));
45812
+ function withNormalizedTextDocumentUri(params) {
45813
+ return {
45814
+ ...params,
45815
+ textDocument: {
45816
+ ...params.textDocument,
45817
+ uri: normalizeDocumentUri(params.textDocument.uri)
45818
+ }
45819
+ };
45811
45820
  }
45812
45821
  function applySettings(next) {
45813
45822
  const scoped = next && typeof next === "object" && "btxml" in next ? next.btxml : next;
@@ -45821,103 +45830,187 @@ function applySettings(next) {
45821
45830
  diagnosticsDebounceMs: Number(value.diagnosticsDebounceMs ?? value.lsp?.diagnosticsDebounceMs ?? 200) || 200
45822
45831
  };
45823
45832
  }
45824
- function getDocument(uri) {
45825
- const normalizedUri = normalizeDocumentUri(uri);
45826
- const workspace = getWorkspaceForDocumentUri(normalizedUri);
45827
- return workspace.getDocument(normalizedUri) || workspace.getDocument(fileUriToPath4(normalizedUri));
45833
+ async function pathExists(fsPath) {
45834
+ try {
45835
+ await (0, import_promises2.access)(fsPath);
45836
+ return true;
45837
+ } catch {
45838
+ return false;
45839
+ }
45828
45840
  }
45829
- function withNormalizedTextDocumentUri(params) {
45841
+ function getConfiguredConfigPath(workspaceRoot) {
45842
+ if (!settings.configPath) return void 0;
45843
+ return import_node_path3.default.isAbsolute(settings.configPath) ? import_node_path3.default.resolve(settings.configPath) : import_node_path3.default.resolve(workspaceRoot, settings.configPath);
45844
+ }
45845
+ async function findNearestConfigPath(documentPath, workspaceRoot) {
45846
+ const boundedRoot = isWithinPath(workspaceRoot, documentPath) ? import_node_path3.default.resolve(workspaceRoot) : void 0;
45847
+ let currentDir = import_node_path3.default.dirname(documentPath);
45848
+ while (true) {
45849
+ const candidate = import_node_path3.default.join(currentDir, "btxml.config.json");
45850
+ if (await pathExists(candidate)) return candidate;
45851
+ if (boundedRoot && normalizeFsPath(currentDir) === normalizeFsPath(boundedRoot))
45852
+ return void 0;
45853
+ const parentDir = import_node_path3.default.dirname(currentDir);
45854
+ if (parentDir === currentDir) return void 0;
45855
+ if (boundedRoot && !isWithinPath(boundedRoot, parentDir)) return void 0;
45856
+ currentDir = parentDir;
45857
+ }
45858
+ }
45859
+ function toWorkspaceProjectBinding(workspaceRoot) {
45860
+ const resolvedWorkspaceRoot = import_node_path3.default.resolve(workspaceRoot);
45830
45861
  return {
45831
- ...params,
45832
- textDocument: {
45833
- ...params.textDocument,
45834
- uri: normalizeDocumentUri(params.textDocument.uri)
45835
- }
45862
+ key: `workspace:${normalizeFsPath(resolvedWorkspaceRoot)}`,
45863
+ cwd: resolvedWorkspaceRoot,
45864
+ workspaceRoot: resolvedWorkspaceRoot
45836
45865
  };
45837
45866
  }
45838
- async function reloadWorkspace(rootPath) {
45839
- const workspace = getWorkspaceService(rootPath);
45840
- const result = await workspace.loadProject({
45841
- cwd: rootPath,
45842
- configPath: settings.configPath
45843
- });
45844
- if (workspace.getResolvedConfig()) {
45845
- loadedWorkspaceRoots.add(rootPath);
45846
- } else {
45847
- loadedWorkspaceRoots.delete(rootPath);
45867
+ function toConfigProjectBinding(configPath2, workspaceRoot) {
45868
+ const resolvedConfigPath = import_node_path3.default.resolve(configPath2);
45869
+ return {
45870
+ key: `config:${normalizeFsPath(resolvedConfigPath)}`,
45871
+ cwd: import_node_path3.default.dirname(resolvedConfigPath),
45872
+ workspaceRoot: import_node_path3.default.resolve(workspaceRoot),
45873
+ configPath: resolvedConfigPath
45874
+ };
45875
+ }
45876
+ async function resolveProjectForDocumentUri(uri) {
45877
+ const workspaceRoot = import_node_path3.default.resolve(resolveWorkspaceRootForUri(uri));
45878
+ const configuredConfigPath = getConfiguredConfigPath(workspaceRoot);
45879
+ if (configuredConfigPath) {
45880
+ return toConfigProjectBinding(configuredConfigPath, workspaceRoot);
45881
+ }
45882
+ if (uri.startsWith("file://")) {
45883
+ try {
45884
+ const nearestConfigPath = await findNearestConfigPath(fileUriToPath4(uri), workspaceRoot);
45885
+ if (nearestConfigPath) return toConfigProjectBinding(nearestConfigPath, workspaceRoot);
45886
+ } catch {
45887
+ }
45888
+ }
45889
+ return toWorkspaceProjectBinding(workspaceRoot);
45890
+ }
45891
+ function getProjectService(binding) {
45892
+ projectBindings.set(binding.key, binding);
45893
+ let projectService = projectServices.get(binding.key);
45894
+ if (projectService) return projectService;
45895
+ projectService = createNodeWorkspaceService({ cwd: binding.cwd });
45896
+ projectServices.set(binding.key, projectService);
45897
+ return projectService;
45898
+ }
45899
+ function getProjectBindingForDocumentUri(uri) {
45900
+ const key = documentProjectKeys.get(uri);
45901
+ return key ? projectBindings.get(key) : void 0;
45902
+ }
45903
+ function getProjectServiceForBoundDocumentUri(uri) {
45904
+ const binding = getProjectBindingForDocumentUri(uri);
45905
+ return binding ? getProjectService(binding) : void 0;
45906
+ }
45907
+ function getWorkspaceDocument(uri) {
45908
+ const normalizedUri = normalizeDocumentUri(uri);
45909
+ const projectService = getProjectServiceForBoundDocumentUri(normalizedUri);
45910
+ return projectService?.getDocument(normalizedUri) || (normalizedUri.startsWith("file://") ? projectService?.getDocument(fileUriToPath4(normalizedUri)) : void 0);
45911
+ }
45912
+ function getOpenDocumentSnapshot(uri) {
45913
+ const document = documents.get(uri);
45914
+ if (document) return toDocumentSnapshot(document);
45915
+ const projectService = getProjectServiceForBoundDocumentUri(uri);
45916
+ const workspaceDocument = projectService?.getDocument(uri);
45917
+ return workspaceDocument ? toDocumentSnapshot(workspaceDocument) : void 0;
45918
+ }
45919
+ function isProjectInUse(key) {
45920
+ for (const projectKey of documentProjectKeys.values()) {
45921
+ if (projectKey === key) return true;
45922
+ }
45923
+ return false;
45924
+ }
45925
+ function disposeProject(key) {
45926
+ const projectService = projectServices.get(key);
45927
+ if (projectService) projectService.dispose();
45928
+ projectServices.delete(key);
45929
+ projectBindings.delete(key);
45930
+ loadedProjects.delete(key);
45931
+ }
45932
+ function disposeProjectIfUnused(key) {
45933
+ if (isProjectInUse(key)) return;
45934
+ disposeProject(key);
45935
+ }
45936
+ function disposeUnusedProjects() {
45937
+ for (const key of [...projectServices.keys()]) {
45938
+ disposeProjectIfUnused(key);
45848
45939
  }
45940
+ }
45941
+ async function reloadProject(binding, options) {
45942
+ const projectService = getProjectService(binding);
45943
+ const result = await projectService.loadProject(
45944
+ binding.configPath ? {
45945
+ cwd: binding.cwd,
45946
+ configPath: binding.configPath
45947
+ } : {
45948
+ cwd: binding.workspaceRoot
45949
+ }
45950
+ );
45951
+ loadedProjects.add(binding.key);
45849
45952
  if (!result.ok && result.diagnostics.length > 0) {
45850
45953
  const lines = result.diagnostics.map((diag) => `${diag.severity} ${diag.code} ${diag.message}`);
45851
45954
  process.stderr.write(`${lines.join("\n")}
45852
45955
  `);
45853
45956
  }
45854
- for (const uri of openUris) {
45855
- if (getDocumentWorkspaceRoot(uri) !== rootPath) continue;
45856
- publishDiagnostics(uri);
45957
+ if (options?.publishDiagnostics !== false) {
45958
+ for (const uri of openUris) {
45959
+ if (documentProjectKeys.get(uri) !== binding.key) continue;
45960
+ publishDiagnostics(uri);
45961
+ }
45857
45962
  }
45858
45963
  return result;
45859
45964
  }
45860
- async function reloadAllWorkspaces() {
45861
- return Promise.all(workspaceRoots.map((rootPath) => reloadWorkspace(rootPath)));
45965
+ async function ensureProjectLoaded(binding) {
45966
+ if (loadedProjects.has(binding.key)) return false;
45967
+ await reloadProject(binding);
45968
+ return true;
45862
45969
  }
45863
- async function handleWorkspaceFoldersChanged(params) {
45864
- const removedRoots = new Set(
45865
- params.event.removed.map((folder) => normalizeWorkspaceRoot2(fileUriToPath4(folder.uri)))
45866
- );
45867
- const addedRoots = params.event.added.map((folder) => fileUriToPath4(folder.uri));
45868
- const movedOpenDocuments = /* @__PURE__ */ new Map();
45869
- for (const uri of openUris) {
45870
- const previousRoot = documentWorkspaceRoots.get(uri);
45871
- if (!previousRoot || !removedRoots.has(normalizeWorkspaceRoot2(previousRoot))) continue;
45872
- const document = workspaceServices.get(previousRoot)?.getDocument(uri);
45873
- movedOpenDocuments.set(
45874
- uri,
45875
- document ? {
45876
- text: document.text,
45877
- version: document.version,
45878
- languageId: document.languageId
45879
- } : void 0
45880
- );
45881
- }
45882
- setWorkspaceRoots(
45883
- workspaceRoots.filter((rootPath) => !removedRoots.has(normalizeWorkspaceRoot2(rootPath))).concat(addedRoots)
45884
- );
45885
- for (const [rootPath, workspace] of [...workspaceServices.entries()]) {
45886
- if (workspaceRoots.some(
45887
- (candidate) => normalizeWorkspaceRoot2(candidate) === normalizeWorkspaceRoot2(rootPath)
45888
- )) {
45889
- continue;
45890
- }
45891
- workspace.dispose();
45892
- workspaceServices.delete(rootPath);
45893
- loadedWorkspaceRoots.delete(rootPath);
45970
+ async function bindOpenDocument(uri, snapshot) {
45971
+ const normalizedUri = normalizeDocumentUri(uri);
45972
+ const nextBinding = await resolveProjectForDocumentUri(normalizedUri);
45973
+ const previousKey = documentProjectKeys.get(normalizedUri);
45974
+ const previousService = previousKey ? projectServices.get(previousKey) : void 0;
45975
+ const nextService = getProjectService(nextBinding);
45976
+ const changedProject = previousKey !== nextBinding.key;
45977
+ const wasLoaded = loadedProjects.has(nextBinding.key);
45978
+ if (changedProject) previousService?.closeDocument(normalizedUri);
45979
+ documentProjectKeys.set(normalizedUri, nextBinding.key);
45980
+ projectBindings.set(nextBinding.key, nextBinding);
45981
+ if (!wasLoaded) {
45982
+ await reloadProject(nextBinding, { publishDiagnostics: false });
45983
+ }
45984
+ if (!wasLoaded || changedProject || !nextService.getDocument(normalizedUri)) {
45985
+ nextService.openDocument(normalizedUri, snapshot.text, snapshot.version, snapshot.languageId);
45986
+ } else {
45987
+ nextService.updateDocument(normalizedUri, snapshot.text, snapshot.version, snapshot.languageId);
45894
45988
  }
45895
- await reloadAllWorkspaces();
45989
+ if (changedProject && previousKey) disposeProjectIfUnused(previousKey);
45990
+ return { binding: nextBinding, reloaded: !wasLoaded };
45991
+ }
45992
+ async function rebindAllOpenDocuments() {
45896
45993
  for (const uri of openUris) {
45897
- const nextRoot = resolveWorkspaceRootForUri(uri);
45898
- const previousRoot = documentWorkspaceRoots.get(uri);
45899
- if (previousRoot === nextRoot) {
45900
- publishDiagnostics(uri);
45901
- continue;
45902
- }
45903
- const previousWorkspace = previousRoot ? workspaceServices.get(previousRoot) : void 0;
45904
- const document = previousWorkspace?.getDocument(uri) ?? movedOpenDocuments.get(uri);
45905
- previousWorkspace?.closeDocument(uri);
45906
- documentWorkspaceRoots.set(uri, nextRoot);
45907
- if (document) {
45908
- getWorkspaceService(nextRoot).openDocument(
45909
- uri,
45910
- document.text,
45911
- document.version,
45912
- document.languageId
45913
- );
45914
- }
45994
+ const snapshot = getOpenDocumentSnapshot(uri);
45995
+ if (!snapshot) continue;
45996
+ await bindOpenDocument(uri, snapshot);
45915
45997
  publishDiagnostics(uri);
45916
45998
  }
45999
+ disposeUnusedProjects();
46000
+ }
46001
+ async function getProjectServiceForDocumentUri(uri) {
46002
+ const normalizedUri = normalizeDocumentUri(uri);
46003
+ const boundBinding = getProjectBindingForDocumentUri(normalizedUri);
46004
+ if (boundBinding) {
46005
+ await ensureProjectLoaded(boundBinding);
46006
+ return getProjectService(boundBinding);
46007
+ }
46008
+ const binding = await resolveProjectForDocumentUri(normalizedUri);
46009
+ await ensureProjectLoaded(binding);
46010
+ return getProjectService(binding);
45917
46011
  }
45918
46012
  function publishDiagnostics(uri) {
45919
46013
  const normalizedUri = normalizeDocumentUri(uri);
45920
- const workspace = getWorkspaceForDocumentUri(normalizedUri);
45921
46014
  if (!settings.diagnosticsEnabled) {
45922
46015
  connection.sendNotification("textDocument/publishDiagnostics", {
45923
46016
  uri: normalizedUri,
@@ -45925,15 +46018,16 @@ function publishDiagnostics(uri) {
45925
46018
  });
45926
46019
  return;
45927
46020
  }
45928
- const document = getDocument(normalizedUri);
45929
- if (!document) {
46021
+ const document = getWorkspaceDocument(normalizedUri);
46022
+ const projectService = getProjectServiceForBoundDocumentUri(normalizedUri);
46023
+ if (!document || !projectService) {
45930
46024
  connection.sendNotification("textDocument/publishDiagnostics", {
45931
46025
  uri: normalizedUri,
45932
46026
  diagnostics: []
45933
46027
  });
45934
46028
  return;
45935
46029
  }
45936
- const result = workspace.getDiagnostics(normalizedUri);
46030
+ const result = projectService.getDiagnostics(normalizedUri);
45937
46031
  connection.sendNotification("textDocument/publishDiagnostics", {
45938
46032
  uri: normalizedUri,
45939
46033
  diagnostics: result.diagnostics.map(toDiagnostic)
@@ -45948,54 +46042,52 @@ function scheduleDiagnostics(uri) {
45948
46042
  }, settings.diagnosticsDebounceMs);
45949
46043
  debounceTimers.set(uri, timeout);
45950
46044
  }
46045
+ function bindingMatchesFilePath(binding, fsPath) {
46046
+ if (binding.configPath && normalizeFsPath(binding.configPath) === normalizeFsPath(fsPath))
46047
+ return true;
46048
+ return isWithinPath(binding.cwd, fsPath);
46049
+ }
45951
46050
  documents.onDidOpen(async (event) => {
45952
46051
  const normalizedUri = normalizeDocumentUri(event.document.uri);
45953
- const rootPath = resolveWorkspaceRootForUri(normalizedUri);
45954
- documentWorkspaceRoots.set(normalizedUri, rootPath);
45955
- if (!loadedWorkspaceRoots.has(rootPath)) {
45956
- await reloadWorkspace(rootPath);
45957
- }
45958
46052
  openUris.add(normalizedUri);
45959
- const workspace = getWorkspaceService(rootPath);
45960
- workspace.openDocument(
45961
- normalizedUri,
45962
- event.document.getText(),
45963
- event.document.version,
45964
- event.document.languageId === "btcpp-xml" ? "btcpp-xml" : "xml"
45965
- );
46053
+ await bindOpenDocument(normalizedUri, toDocumentSnapshot(event.document));
45966
46054
  publishDiagnostics(normalizedUri);
45967
46055
  });
45968
46056
  documents.onDidChangeContent((event) => {
45969
46057
  const normalizedUri = normalizeDocumentUri(event.document.uri);
45970
- const workspace = getWorkspaceForDocumentUri(normalizedUri);
45971
- workspace.updateDocument(
46058
+ const projectService = getProjectServiceForBoundDocumentUri(normalizedUri);
46059
+ if (!projectService) return;
46060
+ projectService.updateDocument(
45972
46061
  normalizedUri,
45973
46062
  event.document.getText(),
45974
46063
  event.document.version,
45975
- event.document.languageId === "btcpp-xml" ? "btcpp-xml" : "xml"
46064
+ normalizeLanguageId(event.document.languageId)
45976
46065
  );
45977
46066
  scheduleDiagnostics(normalizedUri);
45978
46067
  });
45979
46068
  documents.onDidClose((event) => {
45980
46069
  const normalizedUri = normalizeDocumentUri(event.document.uri);
45981
- const workspace = getWorkspaceForDocumentUri(normalizedUri);
46070
+ const projectKey = documentProjectKeys.get(normalizedUri);
46071
+ const projectService = projectKey ? projectServices.get(projectKey) : void 0;
45982
46072
  openUris.delete(normalizedUri);
45983
46073
  const existing = debounceTimers.get(normalizedUri);
45984
46074
  if (existing) clearTimeout(existing);
45985
46075
  debounceTimers.delete(normalizedUri);
45986
- workspace.closeDocument(normalizedUri);
45987
- documentWorkspaceRoots.delete(normalizedUri);
46076
+ projectService?.closeDocument(normalizedUri);
46077
+ if (projectKey) {
46078
+ documentProjectKeys.delete(normalizedUri);
46079
+ disposeProjectIfUnused(projectKey);
46080
+ }
45988
46081
  connection.sendNotification("textDocument/publishDiagnostics", {
45989
46082
  uri: normalizedUri,
45990
46083
  diagnostics: []
45991
46084
  });
45992
46085
  });
45993
- connection.onInitialize(async (params) => {
46086
+ connection.onInitialize((params) => {
45994
46087
  applySettings(params.initializationOptions);
45995
46088
  setWorkspaceRoots(
45996
46089
  collectWorkspaceRoots(params)
45997
46090
  );
45998
- await reloadAllWorkspaces();
45999
46091
  return {
46000
46092
  serverInfo: {
46001
46093
  name: "btxml",
@@ -46031,114 +46123,125 @@ connection.onNotification(
46031
46123
  "workspace/didChangeConfiguration",
46032
46124
  async (params) => {
46033
46125
  applySettings(params.settings);
46034
- await reloadAllWorkspaces();
46126
+ loadedProjects.clear();
46127
+ await rebindAllOpenDocuments();
46035
46128
  }
46036
46129
  );
46037
46130
  connection.onNotification(
46038
46131
  "workspace/didChangeWorkspaceFolders",
46039
46132
  async (params) => {
46040
- await handleWorkspaceFoldersChanged(params);
46133
+ const removedRoots = new Set(
46134
+ params.event.removed.map((folder) => normalizeFsPath(fileUriToPath4(folder.uri)))
46135
+ );
46136
+ const addedRoots = params.event.added.map((folder) => fileUriToPath4(folder.uri));
46137
+ setWorkspaceRoots(
46138
+ workspaceRoots.filter((rootPath) => !removedRoots.has(normalizeFsPath(rootPath))).concat(addedRoots)
46139
+ );
46140
+ loadedProjects.clear();
46141
+ await rebindAllOpenDocuments();
46041
46142
  }
46042
46143
  );
46043
46144
  connection.onNotification("workspace/didChangeWatchedFiles", async (params) => {
46044
46145
  const changes = params?.changes || [];
46045
- const reloadedRoots = /* @__PURE__ */ new Set();
46046
- await Promise.all(
46047
- changes.map(async (change) => {
46048
- const normalizedUri = normalizeDocumentUri(change.uri);
46049
- const affectedRoots = normalizedUri.startsWith("file://") ? (() => {
46050
- const rootPath = resolveWorkspaceRootForUri(normalizedUri);
46051
- const fsPath = fileUriToPath4(normalizedUri);
46052
- const normalizedRoot = normalizeWorkspaceRoot2(rootPath);
46053
- const normalizedPath = normalizeWorkspaceRoot2(fsPath);
46054
- return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}/`) ? [rootPath] : workspaceRoots;
46055
- })() : workspaceRoots;
46056
- const reloads = await Promise.all(
46057
- affectedRoots.map(
46058
- (rootPath) => getWorkspaceService(rootPath).notifyWatchedFileChanged(normalizedUri)
46059
- )
46060
- );
46061
- if (reloads.some((result) => result)) {
46062
- for (const rootPath of affectedRoots) reloadedRoots.add(rootPath);
46063
- }
46064
- })
46065
- );
46066
- if (reloadedRoots.size > 0) {
46067
- for (const uri of openUris) {
46068
- if (!reloadedRoots.has(getDocumentWorkspaceRoot(uri))) continue;
46069
- publishDiagnostics(uri);
46146
+ if (changes.length === 0) return;
46147
+ if (changes.some(
46148
+ (change) => normalizeDocumentUri(change.uri).toLowerCase().endsWith("btxml.config.json")
46149
+ )) {
46150
+ loadedProjects.clear();
46151
+ await rebindAllOpenDocuments();
46152
+ return;
46153
+ }
46154
+ const reloadedProjects = /* @__PURE__ */ new Set();
46155
+ for (const change of changes) {
46156
+ const normalizedUri = normalizeDocumentUri(change.uri);
46157
+ if (!normalizedUri.startsWith("file://")) continue;
46158
+ const fsPath = fileUriToPath4(normalizedUri);
46159
+ for (const binding of projectBindings.values()) {
46160
+ if (!bindingMatchesFilePath(binding, fsPath)) continue;
46161
+ const result = await getProjectService(binding).notifyWatchedFileChanged(normalizedUri);
46162
+ if (!result) continue;
46163
+ loadedProjects.add(binding.key);
46164
+ reloadedProjects.add(binding.key);
46070
46165
  }
46071
46166
  }
46167
+ if (reloadedProjects.size === 0) return;
46168
+ for (const uri of openUris) {
46169
+ const projectKey = documentProjectKeys.get(uri);
46170
+ if (!projectKey || !reloadedProjects.has(projectKey)) continue;
46171
+ publishDiagnostics(uri);
46172
+ }
46072
46173
  });
46073
- connection.onRequest("textDocument/completion", (params) => {
46174
+ connection.onRequest("textDocument/completion", async (params) => {
46074
46175
  if (!settings.completionEnabled) return [];
46075
46176
  const normalizedParams = withNormalizedTextDocumentUri(params);
46076
46177
  return handleCompletion(
46077
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46078
- getDocument(normalizedParams.textDocument.uri),
46178
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46179
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46079
46180
  normalizedParams
46080
46181
  );
46081
46182
  });
46082
- connection.onRequest("textDocument/hover", (params) => {
46183
+ connection.onRequest("textDocument/hover", async (params) => {
46083
46184
  const normalizedParams = withNormalizedTextDocumentUri(params);
46084
46185
  return handleHover(
46085
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46086
- getDocument(normalizedParams.textDocument.uri),
46186
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46187
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46087
46188
  normalizedParams
46088
46189
  );
46089
46190
  });
46090
- connection.onRequest("textDocument/definition", (params) => {
46191
+ connection.onRequest("textDocument/definition", async (params) => {
46091
46192
  const normalizedParams = withNormalizedTextDocumentUri(params);
46092
46193
  return handleDefinition(
46093
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46094
- getDocument(normalizedParams.textDocument.uri),
46194
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46195
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46095
46196
  normalizedParams
46096
46197
  );
46097
46198
  });
46098
- connection.onRequest("textDocument/references", (params) => {
46199
+ connection.onRequest("textDocument/references", async (params) => {
46099
46200
  const normalizedParams = withNormalizedTextDocumentUri(params);
46100
46201
  return handleReferences(
46101
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46102
- getDocument(normalizedParams.textDocument.uri),
46202
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46203
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46103
46204
  normalizedParams
46104
46205
  );
46105
46206
  });
46106
- connection.onRequest("textDocument/documentSymbol", (params) => {
46207
+ connection.onRequest("textDocument/documentSymbol", async (params) => {
46107
46208
  const normalizedParams = withNormalizedTextDocumentUri(params);
46108
46209
  return handleDocumentSymbols(
46109
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46110
- getDocument(normalizedParams.textDocument.uri),
46210
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46211
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46111
46212
  normalizedParams,
46112
46213
  import_node4.SymbolKind
46113
46214
  );
46114
46215
  });
46115
- connection.onRequest("textDocument/formatting", (params) => {
46216
+ connection.onRequest("textDocument/formatting", async (params) => {
46116
46217
  if (!settings.formatEnabled) return [];
46117
46218
  const normalizedParams = withNormalizedTextDocumentUri(params);
46118
46219
  return handleFormatting(
46119
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46120
- getDocument(normalizedParams.textDocument.uri),
46220
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46221
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46121
46222
  normalizedParams
46122
46223
  );
46123
46224
  });
46124
- connection.onRequest("textDocument/codeAction", (params) => {
46225
+ connection.onRequest("textDocument/codeAction", async (params) => {
46125
46226
  const normalizedParams = withNormalizedTextDocumentUri(params);
46126
46227
  return handleCodeActions(
46127
- getWorkspaceForDocumentUri(normalizedParams.textDocument.uri),
46128
- getDocument(normalizedParams.textDocument.uri),
46228
+ await getProjectServiceForDocumentUri(normalizedParams.textDocument.uri),
46229
+ getWorkspaceDocument(normalizedParams.textDocument.uri),
46129
46230
  normalizedParams
46130
46231
  );
46131
46232
  });
46132
- connection.onRequest("btxml/getNodeModelById", (params) => {
46133
- return handleGetNodeModelById(getWorkspaceForDocumentUri(normalizeDocumentUri(params.uri)), {
46233
+ connection.onRequest("btxml/getNodeModelById", async (params) => {
46234
+ const normalizedUri = normalizeDocumentUri(params.uri);
46235
+ return handleGetNodeModelById(await getProjectServiceForDocumentUri(normalizedUri), {
46134
46236
  ...params,
46135
- uri: normalizeDocumentUri(params.uri)
46237
+ uri: normalizedUri
46136
46238
  });
46137
46239
  });
46138
- connection.onRequest("btxml/getChildCapability", (params) => {
46139
- return handleGetChildCapability(getWorkspaceForDocumentUri(normalizeDocumentUri(params.uri)), {
46240
+ connection.onRequest("btxml/getChildCapability", async (params) => {
46241
+ const normalizedUri = normalizeDocumentUri(params.uri);
46242
+ return handleGetChildCapability(await getProjectServiceForDocumentUri(normalizedUri), {
46140
46243
  ...params,
46141
- uri: normalizeDocumentUri(params.uri)
46244
+ uri: normalizedUri
46142
46245
  });
46143
46246
  });
46144
46247
  async function startLanguageServer() {