@likec4/language-server 1.46.3 → 1.46.4

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.
@@ -54,8 +54,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
54
54
  }
55
55
  }
56
56
  // we need lazy resolving here
57
- *genUniqueDescedants(of) {
58
- const element = of();
57
+ *genUniqueDescedants(element) {
59
58
  if (!element) {
60
59
  return;
61
60
  }
@@ -83,7 +82,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
83
82
  };
84
83
  }
85
84
  // we make extended element resolvable inside ExtendElementBody
86
- yield* this.genUniqueDescedants(() => elementRef(element));
85
+ yield* this.genUniqueDescedants(elementRef(element));
87
86
  }
88
87
  *genScopeElementView({ viewOf, extends: ext }) {
89
88
  if (viewOf) {
@@ -92,7 +91,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
92
91
  if (viewOf.modelElement.value.$nodeDescription) {
93
92
  yield viewOf.modelElement.value.$nodeDescription;
94
93
  }
95
- yield* this.genUniqueDescedants(() => elementRef(viewOf));
94
+ yield* this.genUniqueDescedants(elementRef(viewOf));
96
95
  return;
97
96
  }
98
97
  if (ext) {
@@ -115,10 +114,10 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
115
114
  if (deploymentNode.value.$nodeDescription) {
116
115
  yield deploymentNode.value.$nodeDescription;
117
116
  }
118
- yield* this.genUniqueDescedants(() => {
119
- const target = deploymentNode.value.ref;
120
- return target && ast.isDeploymentNode(target) ? target : undefined;
121
- });
117
+ const target = deploymentNode.value.ref;
118
+ if (target && ast.isDeploymentNode(target)) {
119
+ yield* this.genUniqueDescedants(target);
120
+ }
122
121
  }
123
122
  streamForFqnRef(projectId, container, context) {
124
123
  const parent = container.parent;
@@ -130,25 +129,23 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
130
129
  return EMPTY_STREAM;
131
130
  }
132
131
  if (ast.isImported(parentRef)) {
133
- return stream(this.genUniqueDescedants(() => {
134
- return parentRef.imported.ref;
135
- }));
132
+ return stream(this.genUniqueDescedants(parentRef.imported.ref));
136
133
  }
137
134
  if (ast.isDeploymentNode(parentRef)) {
138
- return stream(this.genUniqueDescedants(() => parentRef));
135
+ return stream(this.genUniqueDescedants(parentRef));
139
136
  }
140
137
  if (ast.isDeployedInstance(parentRef)) {
141
- // if (ast.isElement(target)) {
142
- return stream(this.genUniqueDescedants(() => {
143
- const target = parentRef.target.modelElement.value.ref;
144
- if (ast.isImported(target)) {
145
- return target.imported.ref;
146
- }
147
- return ast.isElement(target) ? target : undefined;
148
- }));
138
+ const target = parentRef.target.modelElement.value.ref;
139
+ // dprint-ignore
140
+ const resolvedTarget = ast.isImported(target)
141
+ ? target.imported.ref
142
+ : ast.isElement(target)
143
+ ? target
144
+ : undefined;
145
+ return stream(this.genUniqueDescedants(resolvedTarget));
149
146
  }
150
147
  if (ast.isElement(parentRef)) {
151
- return stream(this.genUniqueDescedants(() => parentRef));
148
+ return stream(this.genUniqueDescedants(parentRef));
152
149
  }
153
150
  return nonexhaustive(parentRef);
154
151
  }
@@ -16,8 +16,10 @@ export class IndexManager extends DefaultIndexManager {
16
16
  let documentUris = stream(this.symbolIndex.keys());
17
17
  return documentUris
18
18
  .filter(uri => {
19
- return (!uris || uris.has(uri)) && (projects.belongsTo(uri) === projectId ||
20
- projects.isIncluded(projectId, uri));
19
+ if (uris && !uris.has(uri)) {
20
+ return false;
21
+ }
22
+ return projects.isIncluded(projectId, uri);
21
23
  })
22
24
  .flatMap(uri => this.getFileDescriptions(uri, nodeType));
23
25
  }
@@ -1,6 +1,6 @@
1
1
  import { compareNaturalHierarchically } from '@likec4/core/utils';
2
2
  import { DefaultLangiumDocuments, stream } from 'langium';
3
- import { groupBy, prop } from 'remeda';
3
+ import { hasAtLeast } from 'remeda';
4
4
  import { isLikeC4LangiumDocument } from '../ast';
5
5
  import { LikeC4LanguageMetaData } from '../generated/module';
6
6
  import { isLikeC4Builtin } from '../likec4lib';
@@ -66,12 +66,23 @@ export class LangiumDocuments extends DefaultLangiumDocuments {
66
66
  */
67
67
  projectDocuments(projectId) {
68
68
  const projects = this.services.workspace.ProjectsManager;
69
- return this.allExcludingBuiltin.filter(doc => {
70
- return doc.likec4ProjectId === projectId || projects.isIncluded(projectId, doc.uri);
69
+ return this.all.filter(doc => {
70
+ if (isLikeC4Builtin(doc.uri)) {
71
+ return false;
72
+ }
73
+ return projects.isIncluded(projectId, doc.uri);
71
74
  });
72
75
  }
73
76
  groupedByProject() {
74
- return groupBy(this.allExcludingBuiltin.toArray(), prop('likec4ProjectId'));
77
+ return this.services.workspace.ProjectsManager
78
+ .all
79
+ .reduce((acc, projectId) => {
80
+ const docs = this.projectDocuments(projectId).toArray();
81
+ if (hasAtLeast(docs, 1)) {
82
+ acc[projectId] = docs;
83
+ }
84
+ return acc;
85
+ }, {});
75
86
  }
76
87
  /**
77
88
  * Reset the project IDs of all documents.
@@ -2,6 +2,7 @@ import { type IncludeConfig, type LikeC4ProjectConfig, type LikeC4ProjectConfigI
2
2
  import type { NonEmptyArray, NonEmptyReadonlyArray } from '@likec4/core';
3
3
  import type { ProjectId, scalar } from '@likec4/core/types';
4
4
  import { type Cancellation, type LangiumDocument, URI, WorkspaceCache } from 'langium';
5
+ import picomatch from 'picomatch';
5
6
  import type { Tagged } from 'type-fest';
6
7
  import type { LikeC4SharedServices } from '../module';
7
8
  /**
@@ -15,7 +16,7 @@ interface ProjectData {
15
16
  config: LikeC4ProjectConfig;
16
17
  folder: ProjectFolder;
17
18
  folderUri: URI;
18
- exclude?: (path: string) => boolean;
19
+ exclude?: picomatch.Matcher;
19
20
  /**
20
21
  * Resolved include paths with both URI and folder string representations.
21
22
  * These are additional directories that are part of this project.
@@ -75,10 +76,11 @@ export declare class ProjectsManager {
75
76
  */
76
77
  isExcluded(document: LangiumDocument | URI | string): boolean;
77
78
  /**
78
- * Checks if the specified document is included by the project.
79
+ * Checks if the specified document is included by the project:
80
+ * - if the document belongs to the project and is not excluded
81
+ * - if the document is included by the project
79
82
  */
80
83
  isIncluded(projectId: ProjectId, document: LangiumDocument | URI | string): boolean;
81
- includedInProjects(document: LangiumDocument | URI | string): ProjectId[];
82
84
  /**
83
85
  * Checks if it is a config file and it is not excluded by default exclude pattern
84
86
  *
@@ -1,11 +1,12 @@
1
+ var _a;
1
2
  import { isLikeC4Config, normalizeIncludeConfig, validateProjectConfig, } from '@likec4/config';
2
- import { BiMap, invariant, memoizeProp, nonNullable } from '@likec4/core/utils';
3
+ import { BiMap, DefaultMap, invariant, memoizeProp, nonNullable } from '@likec4/core/utils';
3
4
  import { wrapError } from '@likec4/log';
4
5
  import { deepEqual } from 'fast-equals';
5
6
  import { interruptAndCheck, URI, WorkspaceCache, } from 'langium';
6
7
  import picomatch from 'picomatch';
7
- import { filter, hasAtLeast, isNullish, map, pipe, prop, sortBy } from 'remeda';
8
- import { joinRelativeURL, parseFilename, withoutProtocol, withTrailingSlash, } from 'ufo';
8
+ import { hasAtLeast, isNullish, map, pipe, prop, sortBy } from 'remeda';
9
+ import { cleanDoubleSlashes, isRelative, joinRelativeURL, joinURL, parseFilename, withoutProtocol, withTrailingSlash, } from 'ufo';
9
10
  import { logger as mainLogger } from '../logger';
10
11
  const logger = mainLogger.getChild('ProjectsManager');
11
12
  function normalizeUri(uri) {
@@ -66,6 +67,23 @@ export class ProjectsManager {
66
67
  * This ensures that the most specific project is used for a document.
67
68
  */
68
69
  #projects = [];
70
+ /**
71
+ * This is a cached lookup for performance.
72
+ */
73
+ #lookupById = new DefaultMap((id) => {
74
+ if (id === _a.DefaultProjectId) {
75
+ const folderUri = this.getWorkspaceFolder();
76
+ return {
77
+ id,
78
+ config: DefaultProject.config,
79
+ folder: ProjectFolder(folderUri),
80
+ folderUri,
81
+ exclude: DefaultProject.exclude,
82
+ includeConfig: DefaultProject.includeConfig,
83
+ };
84
+ }
85
+ return nonNullable(this.#projects.find(p => p.id === id), `Project ${id} not found`);
86
+ });
69
87
  #excludedDocuments = new WeakMap();
70
88
  constructor(services) {
71
89
  this.services = services;
@@ -85,14 +103,14 @@ export class ProjectsManager {
85
103
  if (this.#projects.length > 1) {
86
104
  return undefined;
87
105
  }
88
- return this.#projects[0]?.id ?? ProjectsManager.DefaultProjectId;
106
+ return this.#projects[0]?.id ?? _a.DefaultProjectId;
89
107
  }
90
108
  set defaultProjectId(id) {
91
109
  if (id === this.#defaultProjectId) {
92
110
  return;
93
111
  }
94
112
  this.#defaultProject = undefined;
95
- if (!id || id === ProjectsManager.DefaultProjectId) {
113
+ if (!id || id === _a.DefaultProjectId) {
96
114
  logger.debug `reset default project ID`;
97
115
  this.#defaultProjectId = undefined;
98
116
  return;
@@ -103,7 +121,7 @@ export class ProjectsManager {
103
121
  }
104
122
  get default() {
105
123
  if (!this.#defaultProject) {
106
- const id = this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
124
+ const id = this.defaultProjectId ?? _a.DefaultProjectId;
107
125
  let project = this.#projects.find(p => p.id === id);
108
126
  if (!project) {
109
127
  const folderUri = this.getWorkspaceFolder();
@@ -151,12 +169,12 @@ export class ProjectsManager {
151
169
  // ignore - workspace not initialized
152
170
  }
153
171
  return {
154
- id: ProjectsManager.DefaultProjectId,
172
+ id: _a.DefaultProjectId,
155
173
  config: DefaultProject.config,
156
174
  folderUri,
157
175
  };
158
176
  }
159
- const project = nonNullable(this.#projects.find(p => p.id === id), `Project "${id}" not found`);
177
+ const project = nonNullable(this.#lookupById.get(id), `Project ${id} not found`);
160
178
  return {
161
179
  id,
162
180
  folderUri: project.folderUri,
@@ -170,8 +188,8 @@ export class ProjectsManager {
170
188
  * If there are multiple projects and default project is not set, throws an error
171
189
  */
172
190
  ensureProjectId(projectId) {
173
- if (projectId === ProjectsManager.DefaultProjectId) {
174
- return this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
191
+ if (projectId === _a.DefaultProjectId) {
192
+ return this.defaultProjectId ?? _a.DefaultProjectId;
175
193
  }
176
194
  if (projectId) {
177
195
  invariant(this.#projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
@@ -196,7 +214,11 @@ export class ProjectsManager {
196
214
  if (typeof document === 'string' || URI.isUri(document)) {
197
215
  let docUriAsString = normalizeUri(document);
198
216
  const project = this.findProjectForDocument(docUriAsString);
199
- return !!project.exclude && project.exclude(withoutProtocol(docUriAsString));
217
+ if (!project.exclude) {
218
+ return false;
219
+ }
220
+ const input = withoutProtocol(docUriAsString);
221
+ return project.exclude(input);
200
222
  }
201
223
  let isExcluded = this.#excludedDocuments.get(document);
202
224
  if (isExcluded === undefined) {
@@ -206,24 +228,23 @@ export class ProjectsManager {
206
228
  return isExcluded;
207
229
  }
208
230
  /**
209
- * Checks if the specified document is included by the project.
231
+ * Checks if the specified document is included by the project:
232
+ * - if the document belongs to the project and is not excluded
233
+ * - if the document is included by the project
210
234
  */
211
235
  isIncluded(projectId, document) {
212
- if (typeof document === 'string' || URI.isUri(document)) {
213
- let project = this.#projects.find(p => p.id === projectId);
214
- if (!project || !project.includePaths) {
215
- return false;
216
- }
217
- return project.includePaths.some(isParentFolderFor(document));
236
+ if (typeof document !== 'string' && !URI.isUri(document)) {
237
+ return this.isIncluded(projectId, document.uri);
218
238
  }
219
- return this.isIncluded(projectId, document.uri);
220
- }
221
- includedInProjects(document) {
222
- if (typeof document === 'string' || URI.isUri(document)) {
223
- let docUriAsString = normalizeUri(document);
224
- return pipe(this.#projects, filter(p => !!p.includePaths && p.includePaths.some(isParentFolderFor(docUriAsString))), map(p => p.id));
239
+ const belongsTo = this.belongsTo(document);
240
+ if (belongsTo === projectId) {
241
+ return !this.isExcluded(document);
242
+ }
243
+ let includePaths = this.#lookupById.get(projectId)?.includePaths;
244
+ if (!includePaths) {
245
+ return false;
225
246
  }
226
- return this.includedInProjects(document.uri);
247
+ return includePaths.some(isParentFolderFor(document));
227
248
  }
228
249
  /**
229
250
  * Checks if it is a config file and it is not excluded by default exclude pattern
@@ -320,7 +341,16 @@ export class ProjectsManager {
320
341
  project.exclude = DefaultProject.exclude;
321
342
  }
322
343
  else if (hasAtLeast(config.exclude, 1)) {
323
- project.exclude = picomatch(config.exclude, { dot: true });
344
+ const patterns = map(config.exclude, p => {
345
+ if (!isRelative(p) && !p.startsWith('**')) {
346
+ p = joinURL('**', p);
347
+ }
348
+ return cleanDoubleSlashes(joinRelativeURL(project.folderUri.path, p));
349
+ });
350
+ project.exclude = picomatch(patterns, {
351
+ contains: true,
352
+ dot: true,
353
+ });
324
354
  }
325
355
  // Resolve include paths relative to project folder
326
356
  if (project.includeConfig.paths && hasAtLeast(project.includeConfig.paths, 1)) {
@@ -367,6 +397,7 @@ export class ProjectsManager {
367
397
  delete project.includePaths;
368
398
  }
369
399
  this.#projectIdToFolder.set(project.id, folder);
400
+ this.#lookupById.clear();
370
401
  // Reset assigned project IDs if no projects reload is active
371
402
  if (mustReset && !this.#activeReload) {
372
403
  await this.rebuidProject(project.id, cancelToken).catch(error => {
@@ -435,6 +466,7 @@ export class ProjectsManager {
435
466
  }
436
467
  this.#projects = [];
437
468
  this.#projectIdToFolder.clear();
469
+ this.#lookupById.clear();
438
470
  for (const uri of configFiles) {
439
471
  if (cancelToken) {
440
472
  await interruptAndCheck(cancelToken);
@@ -466,6 +498,7 @@ export class ProjectsManager {
466
498
  this.services.workspace.LangiumDocuments.resetProjectIds();
467
499
  this.documentBelongsTo.clear();
468
500
  this.mappingsToProject.clear();
501
+ this.#lookupById.clear();
469
502
  this.#excludedDocuments = new WeakMap();
470
503
  }
471
504
  async rebuidProject(projectId, cancelToken) {
@@ -569,3 +602,4 @@ export class ProjectsManager {
569
602
  }
570
603
  }
571
604
  }
605
+ _a = ProjectsManager;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.46.3",
4
+ "version": "1.46.4",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -126,12 +126,12 @@
126
126
  "vscode-uri": "3.1.0",
127
127
  "which": "^5.0.0",
128
128
  "zod": "^3.25.76",
129
- "@likec4/core": "1.46.3",
129
+ "@likec4/config": "1.46.4",
130
+ "@likec4/core": "1.46.4",
130
131
  "@likec4/devops": "1.42.0",
131
- "@likec4/config": "1.46.3",
132
- "@likec4/icons": "1.46.1",
133
- "@likec4/layouts": "1.46.3",
132
+ "@likec4/icons": "1.46.4",
134
133
  "@likec4/log": "1.46.1",
134
+ "@likec4/layouts": "1.46.4",
135
135
  "@likec4/tsconfig": "1.46.1"
136
136
  },
137
137
  "scripts": {
package/lib/package.json DELETED
@@ -1,159 +0,0 @@
1
- {
2
- "name": "@likec4/language-server",
3
- "description": "LikeC4 Language Server",
4
- "version": "1.45.0",
5
- "license": "MIT",
6
- "bugs": "https://github.com/likec4/likec4/issues",
7
- "homepage": "https://likec4.dev",
8
- "author": "Denis Davydkov <denis@davydkov.com>",
9
- "files": [
10
- "bin",
11
- "dist",
12
- "**/package.json",
13
- "!**/__mocks__/",
14
- "!**/__tests__/",
15
- "!**/*.spec.*",
16
- "!**/*.map"
17
- ],
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/likec4/likec4.git",
21
- "directory": "packages/language-server"
22
- },
23
- "engines": {
24
- "node": "^20.19.0 || >=22.18.0"
25
- },
26
- "engineStrict": true,
27
- "type": "module",
28
- "sideEffects": false,
29
- "bin": {
30
- "likec4-language-server": "./bin/likec4-language-server.mjs"
31
- },
32
- "exports": {
33
- ".": {
34
- "sources": "./src/index.ts",
35
- "default": {
36
- "types": "./dist/index.d.ts",
37
- "default": "./dist/index.js"
38
- }
39
- },
40
- "./likec4lib": {
41
- "sources": "./src/likec4lib.ts",
42
- "default": {
43
- "types": "./dist/likec4lib.d.ts",
44
- "import": "./dist/likec4lib.js",
45
- "default": "./dist/likec4lib.js"
46
- }
47
- },
48
- "./browser-worker": {
49
- "sources": "./src/browser-worker.ts",
50
- "default": {
51
- "types": "./dist/browser-worker.d.ts",
52
- "import": "./dist/browser-worker.js",
53
- "default": "./dist/browser-worker.js"
54
- }
55
- },
56
- "./browser": {
57
- "sources": "./src/browser.ts",
58
- "default": {
59
- "types": "./dist/browser.d.ts",
60
- "import": "./dist/browser.js",
61
- "default": "./dist/browser.js"
62
- }
63
- },
64
- "./protocol": {
65
- "sources": "./src/protocol.ts",
66
- "default": {
67
- "types": "./dist/protocol.d.ts",
68
- "import": "./dist/protocol.js",
69
- "default": "./dist/protocol.js"
70
- }
71
- },
72
- "./bundled": "./dist/bundled.mjs"
73
- },
74
- "publishConfig": {
75
- "registry": "https://registry.npmjs.org",
76
- "access": "public",
77
- "types": "dist/index.d.ts",
78
- "module": "dist/index.js"
79
- },
80
- "scripts": {
81
- "typecheck": "tsc -b --verbose",
82
- "build": "unbuild",
83
- "prepack": "likec4ops prepack",
84
- "pack": "pnpm pack",
85
- "postpack": "cp likec4-language-server-$npm_package_version.tgz package.tgz || true",
86
- "pregenerate": "rm -f src/generated/* || true",
87
- "watch:langium": "langium generate --watch",
88
- "watch:ts": "tsc --watch",
89
- "generate": "langium generate && tsx scripts/generate-icons.ts",
90
- "dev": "run-p \"watch:*\"",
91
- "lint": "oxlint --type-aware",
92
- "lint:fix": "oxlint --type-aware --fix",
93
- "lint:package": "pnpx publint ./package.tgz",
94
- "clean": "likec4ops clean contrib src/generated src/generated-lib",
95
- "test": "vitest run --no-isolate",
96
- "test-dbg": "vitest run --no-isolate -t formating",
97
- "vitest:ui": "vitest --no-isolate --ui",
98
- "test:watch": "vitest"
99
- },
100
- "dependencies": {
101
- "@hpcc-js/wasm-graphviz": "catalog:utils",
102
- "bundle-require": "catalog:utils",
103
- "esbuild": "catalog:esbuild"
104
- },
105
- "devDependencies": {
106
- "@hono/node-server": "catalog:hono",
107
- "@likec4/config": "workspace:*",
108
- "@likec4/core": "workspace:*",
109
- "@likec4/devops": "workspace:*",
110
- "@likec4/icons": "workspace:*",
111
- "@likec4/layouts": "workspace:*",
112
- "@likec4/log": "workspace:*",
113
- "@likec4/tsconfig": "workspace:*",
114
- "@modelcontextprotocol/sdk": "catalog:mcp",
115
- "@msgpack/msgpack": "^3.1.2",
116
- "@smithy/util-base64": "^4.3.0",
117
- "@types/natural-compare-lite": "catalog:utils",
118
- "@types/node": "catalog:",
119
- "@types/picomatch": "catalog:utils",
120
- "@types/vscode": "catalog:vscode",
121
- "@types/which": "^3.0.4",
122
- "chokidar": "catalog:utils",
123
- "defu": "catalog:utils",
124
- "esm-env": "catalog:utils",
125
- "fast-equals": "catalog:utils",
126
- "fdir": "catalog:utils",
127
- "fetch-to-node": "^2.1.0",
128
- "hono": "catalog:hono",
129
- "immer": "catalog:utils",
130
- "indent-string": "^5.0.0",
131
- "json5": "catalog:utils",
132
- "langium": "catalog:langium",
133
- "langium-cli": "catalog:langium",
134
- "nano-spawn": "catalog:utils",
135
- "natural-compare-lite": "catalog:utils",
136
- "oxlint": "catalog:",
137
- "p-debounce": "catalog:utils",
138
- "p-queue": "catalog:utils",
139
- "p-timeout": "catalog:utils",
140
- "picomatch": "catalog:utils",
141
- "pretty-ms": "^9.2.0",
142
- "remeda": "catalog:utils",
143
- "strip-indent": "catalog:utils",
144
- "tsx": "catalog:",
145
- "turbo": "catalog:",
146
- "type-fest": "catalog:utils",
147
- "typescript": "catalog:",
148
- "ufo": "catalog:utils",
149
- "unbuild": "catalog:",
150
- "vitest": "catalog:vitest",
151
- "vscode-jsonrpc": "catalog:vscode",
152
- "vscode-languageserver": "catalog:vscode",
153
- "vscode-languageserver-protocol": "catalog:vscode",
154
- "vscode-languageserver-types": "catalog:vscode",
155
- "vscode-uri": "catalog:vscode",
156
- "which": "^5.0.0",
157
- "zod": "catalog:utils"
158
- }
159
- }