@likec4/language-server 1.27.2 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/LikeC4LanguageServices.js +6 -7
  2. package/dist/ast.d.ts +16 -9
  3. package/dist/ast.js +58 -79
  4. package/dist/bundled.mjs +2130 -2127
  5. package/dist/config/schema.d.ts +3 -3
  6. package/dist/config/schema.js +12 -5
  7. package/dist/documentation/documentation-provider.js +3 -1
  8. package/dist/formatting/LikeC4Formatter.d.ts +0 -2
  9. package/dist/formatting/LikeC4Formatter.js +24 -53
  10. package/dist/generated/ast.d.ts +128 -233
  11. package/dist/generated/ast.js +134 -306
  12. package/dist/generated/grammar.js +1 -1
  13. package/dist/lsp/CompletionProvider.d.ts +3 -0
  14. package/dist/lsp/CompletionProvider.js +128 -113
  15. package/dist/lsp/DocumentLinkProvider.js +6 -3
  16. package/dist/lsp/HoverProvider.js +3 -1
  17. package/dist/lsp/SemanticTokenProvider.js +33 -43
  18. package/dist/model/builder/MergedSpecification.d.ts +5 -3
  19. package/dist/model/builder/MergedSpecification.js +21 -7
  20. package/dist/model/builder/buildModel.d.ts +6 -1
  21. package/dist/model/builder/buildModel.js +20 -15
  22. package/dist/model/deployments-index.js +4 -2
  23. package/dist/model/fqn-index.d.ts +4 -2
  24. package/dist/model/fqn-index.js +28 -5
  25. package/dist/model/model-builder.d.ts +2 -2
  26. package/dist/model/model-builder.js +54 -16
  27. package/dist/model/model-locator.js +7 -4
  28. package/dist/model/model-parser.d.ts +215 -52
  29. package/dist/model/model-parser.js +6 -2
  30. package/dist/model/parser/Base.d.ts +11 -2
  31. package/dist/model/parser/Base.js +138 -3
  32. package/dist/model/parser/DeploymentModelParser.d.ts +19 -2
  33. package/dist/model/parser/DeploymentModelParser.js +19 -29
  34. package/dist/model/parser/DeploymentViewParser.d.ts +18 -2
  35. package/dist/model/parser/DeploymentViewParser.js +6 -24
  36. package/dist/model/parser/FqnRefParser.d.ts +18 -3
  37. package/dist/model/parser/FqnRefParser.js +264 -40
  38. package/dist/model/parser/GlobalsParser.d.ts +35 -18
  39. package/dist/model/parser/ImportsParser.d.ts +32 -0
  40. package/dist/model/parser/ImportsParser.js +26 -0
  41. package/dist/model/parser/ModelParser.d.ts +26 -2
  42. package/dist/model/parser/ModelParser.js +21 -41
  43. package/dist/model/parser/PredicatesParser.d.ts +35 -12
  44. package/dist/model/parser/PredicatesParser.js +20 -271
  45. package/dist/model/parser/SpecificationParser.d.ts +8 -0
  46. package/dist/model/parser/SpecificationParser.js +5 -9
  47. package/dist/model/parser/ViewsParser.d.ts +36 -19
  48. package/dist/model/parser/ViewsParser.js +15 -11
  49. package/dist/model-change/changeElementStyle.d.ts +2 -2
  50. package/dist/model-change/changeElementStyle.js +2 -1
  51. package/dist/references/name-provider.js +8 -2
  52. package/dist/references/scope-computation.d.ts +1 -1
  53. package/dist/references/scope-computation.js +33 -3
  54. package/dist/references/scope-provider.d.ts +7 -8
  55. package/dist/references/scope-provider.js +59 -41
  56. package/dist/shared/NodeKindProvider.js +4 -2
  57. package/dist/test/testServices.d.ts +2 -0
  58. package/dist/test/testServices.js +4 -1
  59. package/dist/utils/elementRef.d.ts +1 -1
  60. package/dist/utils/elementRef.js +6 -1
  61. package/dist/utils/fqnRef.d.ts +3 -0
  62. package/dist/utils/fqnRef.js +15 -4
  63. package/dist/utils/index.d.ts +1 -0
  64. package/dist/utils/index.js +9 -0
  65. package/dist/utils/projectId.d.ts +2 -1
  66. package/dist/utils/projectId.js +11 -1
  67. package/dist/validation/_shared.js +2 -2
  68. package/dist/validation/deployment-checks.js +24 -10
  69. package/dist/validation/element-ref.d.ts +4 -0
  70. package/dist/validation/element-ref.js +12 -0
  71. package/dist/validation/element.d.ts +1 -1
  72. package/dist/validation/element.js +1 -1
  73. package/dist/validation/imports.d.ts +5 -0
  74. package/dist/validation/imports.js +30 -0
  75. package/dist/validation/index.d.ts +1 -1
  76. package/dist/validation/index.js +47 -45
  77. package/dist/validation/relation.d.ts +2 -2
  78. package/dist/validation/relation.js +24 -27
  79. package/dist/validation/specification.d.ts +9 -9
  80. package/dist/validation/specification.js +9 -9
  81. package/dist/validation/view-predicates/{element-with.d.ts → fqn-expr-with.d.ts} +1 -1
  82. package/dist/validation/view-predicates/fqn-expr-with.js +42 -0
  83. package/dist/validation/view-predicates/fqn-ref-expr.d.ts +4 -0
  84. package/dist/validation/view-predicates/fqn-ref-expr.js +53 -0
  85. package/dist/validation/view-predicates/incoming.d.ts +1 -1
  86. package/dist/validation/view-predicates/incoming.js +2 -2
  87. package/dist/validation/view-predicates/index.d.ts +6 -6
  88. package/dist/validation/view-predicates/index.js +6 -6
  89. package/dist/validation/view-predicates/outgoing.d.ts +1 -1
  90. package/dist/validation/view-predicates/outgoing.js +8 -4
  91. package/dist/validation/view-predicates/{expanded-element.d.ts → relation-expr.d.ts} +1 -1
  92. package/dist/validation/view-predicates/relation-expr.js +39 -0
  93. package/dist/validation/view-predicates/relation-with.d.ts +1 -1
  94. package/dist/validation/view-predicates/relation-with.js +8 -5
  95. package/dist/workspace/AstNodeDescriptionProvider.d.ts +1 -1
  96. package/dist/workspace/AstNodeDescriptionProvider.js +2 -3
  97. package/dist/workspace/IndexManager.d.ts +1 -1
  98. package/dist/workspace/IndexManager.js +5 -4
  99. package/dist/workspace/LangiumDocuments.d.ts +1 -1
  100. package/dist/workspace/LangiumDocuments.js +3 -5
  101. package/dist/workspace/ProjectsManager.d.ts +25 -7
  102. package/dist/workspace/ProjectsManager.js +76 -32
  103. package/dist/workspace/WorkspaceManager.d.ts +4 -5
  104. package/dist/workspace/WorkspaceManager.js +53 -20
  105. package/package.json +12 -10
  106. package/dist/validation/dynamic-view-rule.d.ts +0 -4
  107. package/dist/validation/dynamic-view-rule.js +0 -17
  108. package/dist/validation/view-predicates/element-with.js +0 -31
  109. package/dist/validation/view-predicates/expanded-element.js +0 -12
  110. package/dist/validation/view-predicates/expression-v2.d.ts +0 -5
  111. package/dist/validation/view-predicates/expression-v2.js +0 -83
@@ -1,17 +1,18 @@
1
1
  import { DefaultIndexManager, stream } from "langium";
2
2
  import { CancellationToken } from "vscode-jsonrpc";
3
3
  export class IndexManager extends DefaultIndexManager {
4
- projects;
5
4
  constructor(services) {
6
5
  super(services);
7
- this.projects = services.workspace.ProjectsManager;
6
+ this.services = services;
8
7
  }
9
8
  async updateContent(document, cancelToken = CancellationToken.None) {
10
- document.likec4ProjectId = this.projects.belongsTo(document.uri);
9
+ const projects = this.services.workspace.ProjectsManager;
10
+ document.likec4ProjectId = projects.belongsTo(document.uri);
11
11
  await super.updateContent(document, cancelToken);
12
12
  }
13
13
  projectElements(projectId, nodeType, uris) {
14
+ const projects = this.services.workspace.ProjectsManager;
14
15
  let documentUris = stream(this.symbolIndex.keys());
15
- return documentUris.filter((uri) => this.projects.belongsTo(uri) === projectId && (!uris || uris.has(uri))).flatMap((uri) => this.getFileDescriptions(uri, nodeType));
16
+ return documentUris.filter((uri) => projects.belongsTo(uri) === projectId && (!uris || uris.has(uri))).flatMap((uri) => this.getFileDescriptions(uri, nodeType));
16
17
  }
17
18
  }
@@ -3,7 +3,7 @@ import { type Stream, DefaultLangiumDocuments } from 'langium';
3
3
  import { type LikeC4LangiumDocument } from '../ast';
4
4
  import type { LikeC4SharedServices } from '../module';
5
5
  export declare class LangiumDocuments extends DefaultLangiumDocuments {
6
- private projects;
6
+ protected services: LikeC4SharedServices;
7
7
  constructor(services: LikeC4SharedServices);
8
8
  /**
9
9
  * Returns all user documents, excluding built-in documents.
@@ -3,22 +3,20 @@ import { groupBy, prop } from "remeda";
3
3
  import { isLikeC4LangiumDocument } from "../ast.js";
4
4
  import { isLikeC4Builtin } from "../likec4lib.js";
5
5
  export class LangiumDocuments extends DefaultLangiumDocuments {
6
- projects;
7
6
  constructor(services) {
8
7
  super(services);
9
- this.projects = services.workspace.ProjectsManager;
8
+ this.services = services;
10
9
  }
11
10
  /**
12
11
  * Returns all user documents, excluding built-in documents.
13
12
  */
14
13
  get allExcludingBuiltin() {
14
+ const projects = this.services.workspace.ProjectsManager;
15
15
  return super.all.filter((doc) => {
16
16
  if (!isLikeC4LangiumDocument(doc) || isLikeC4Builtin(doc.uri)) {
17
17
  return false;
18
18
  }
19
- if (!doc.likec4ProjectId) {
20
- doc.likec4ProjectId = this.projects.belongsTo(doc.uri);
21
- }
19
+ doc.likec4ProjectId ??= projects.belongsTo(doc.uri);
22
20
  return true;
23
21
  });
24
22
  }
@@ -1,7 +1,14 @@
1
1
  import type { NonEmptyReadonlyArray, ProjectId } from '@likec4/core';
2
2
  import { type FileSystemNode, type LangiumDocument, URI, WorkspaceCache } from 'langium';
3
+ import picomatch from 'picomatch/posix';
3
4
  import { ProjectConfig } from '../config';
4
5
  import type { LikeC4SharedServices } from '../module';
6
+ interface Project {
7
+ id: ProjectId;
8
+ config: ProjectConfig;
9
+ folder: string;
10
+ exclude?: picomatch.Matcher;
11
+ }
5
12
  export declare class ProjectsManager {
6
13
  protected services: LikeC4SharedServices;
7
14
  /**
@@ -14,35 +21,46 @@ export declare class ProjectsManager {
14
21
  * The mapping between project config files and project IDs.
15
22
  */
16
23
  private projectIdToFolder;
17
- private _mappingsToProject;
18
24
  /**
19
25
  * Registered projects.
20
26
  * Sorted descending by the number of segments in the folder path.
21
27
  * This ensures that the most specific project is used for a document.
22
28
  */
23
29
  private _projects;
30
+ private defaultGlobalProject;
24
31
  constructor(services: LikeC4SharedServices);
32
+ /**
33
+ * Returns:
34
+ * - the default project ID if there are no projects.
35
+ * - the ID of the only project
36
+ * - undefined if there are multiple projects.
37
+ */
25
38
  get defaultProjectId(): ProjectId | undefined;
26
39
  get all(): NonEmptyReadonlyArray<ProjectId>;
27
- getProject(projectId: ProjectId): {
40
+ getProject(arg: ProjectId | LangiumDocument): {
41
+ id: ProjectId;
28
42
  folder: URI;
29
43
  config: Readonly<ProjectConfig>;
30
44
  };
31
45
  ensureProjectId(projectId?: ProjectId | undefined): ProjectId;
32
46
  hasMultipleProjects(): boolean;
47
+ checkIfExcluded(documentUri: URI): boolean;
48
+ isConfigFile(entry: FileSystemNode): boolean;
33
49
  /**
34
50
  * Checks if the provided file system entry is a valid project config file.
35
51
  *
36
52
  * @param entry The file system entry to check
37
53
  * @returns {boolean} Returns true if the entry is a valid config file, false otherwise.
38
54
  */
39
- loadConfigFile(entry: FileSystemNode): Promise<boolean>;
40
- registerProject(configFile: URI): Promise<void>;
55
+ loadConfigFile(entry: FileSystemNode): Promise<Project | undefined>;
56
+ registerProject(configFile: URI): Promise<Project>;
41
57
  registerProject(opts: {
42
58
  config: ProjectConfig;
43
59
  folderUri: URI | string;
44
- }): Promise<void>;
60
+ }): Promise<Project>;
45
61
  belongsTo(document: LangiumDocument | URI | string): ProjectId;
46
- private getProjectId;
47
- protected get mappingsToProject(): WorkspaceCache<string, ProjectId>;
62
+ protected findProjectForDocument(documentUri: string): Omit<Project, 'folder'>;
63
+ private _mappingsToProject;
64
+ protected get mappingsToProject(): WorkspaceCache<string, Omit<Project, 'folder'>>;
48
65
  }
66
+ export {};
@@ -1,7 +1,14 @@
1
1
  import { BiMap, invariant, nonNullable } from "@likec4/core";
2
2
  import { URI, WorkspaceCache } from "langium";
3
- import { hasAtLeast, map, pipe, prop, sortBy } from "remeda";
4
- import { hasProtocol, joinRelativeURL, parseFilename, withoutProtocol, withProtocol } from "ufo";
3
+ import picomatch from "picomatch/posix";
4
+ import { hasAtLeast, isNullish, map, pipe, prop, sortBy } from "remeda";
5
+ import {
6
+ hasProtocol,
7
+ joinRelativeURL,
8
+ parseFilename,
9
+ withoutProtocol,
10
+ withProtocol
11
+ } from "ufo";
5
12
  import { parseConfigJson } from "../config/index.js";
6
13
  import { logger as mainLogger } from "../logger.js";
7
14
  const logger = mainLogger.getChild("ProjectsManager");
@@ -24,14 +31,26 @@ export class ProjectsManager {
24
31
  * The mapping between project config files and project IDs.
25
32
  */
26
33
  projectIdToFolder = new BiMap();
27
- // The mapping between document URIs and their corresponding project IDs.
28
- _mappingsToProject;
29
34
  /**
30
35
  * Registered projects.
31
36
  * Sorted descending by the number of segments in the folder path.
32
37
  * This ensures that the most specific project is used for a document.
33
38
  */
34
39
  _projects = [];
40
+ defaultGlobalProject = {
41
+ id: ProjectsManager.DefaultProjectId,
42
+ config: {
43
+ name: ProjectsManager.DefaultProjectId,
44
+ exclude: ["**/node_modules/**/*"]
45
+ },
46
+ exclude: picomatch("**/node_modules/**/*")
47
+ };
48
+ /**
49
+ * Returns:
50
+ * - the default project ID if there are no projects.
51
+ * - the ID of the only project
52
+ * - undefined if there are multiple projects.
53
+ */
35
54
  get defaultProjectId() {
36
55
  if (this._projects.length > 1) {
37
56
  return void 0;
@@ -47,20 +66,24 @@ export class ProjectsManager {
47
66
  }
48
67
  return [ProjectsManager.DefaultProjectId];
49
68
  }
50
- getProject(projectId) {
51
- if (projectId === ProjectsManager.DefaultProjectId) {
52
- const folder = this.services.workspace.WorkspaceManager.workspaceUri;
69
+ getProject(arg) {
70
+ const id = typeof arg === "string" ? arg : arg.likec4ProjectId || this.belongsTo(arg);
71
+ if (id === ProjectsManager.DefaultProjectId) {
72
+ const folder2 = this.services.workspace.WorkspaceManager.workspaceUri;
53
73
  return {
54
- folder,
55
- config: {
56
- name: ProjectsManager.DefaultProjectId
57
- }
74
+ id,
75
+ folder: folder2,
76
+ config: this.defaultGlobalProject.config
58
77
  };
59
78
  }
60
- const project = nonNullable(this._projects.find(({ id }) => id === projectId), `Project "${projectId}" not found`);
79
+ const {
80
+ config,
81
+ folder
82
+ } = nonNullable(this._projects.find((p) => p.id === id), `Project "${id}" not found`);
61
83
  return {
62
- folder: URI.parse(project.folder),
63
- config: project.config
84
+ id,
85
+ folder: URI.parse(folder),
86
+ config
64
87
  };
65
88
  }
66
89
  ensureProjectId(projectId) {
@@ -79,6 +102,15 @@ export class ProjectsManager {
79
102
  hasMultipleProjects() {
80
103
  return this._projects.length > 1;
81
104
  }
105
+ checkIfExcluded(documentUri) {
106
+ let docUriAsString = documentUri.toString();
107
+ const project = this.findProjectForDocument(docUriAsString);
108
+ return project.exclude ? project.exclude(withoutProtocol(docUriAsString)) : false;
109
+ }
110
+ isConfigFile(entry) {
111
+ const filename = parseFilename(entry.uri.toString(), { strict: false })?.toLowerCase();
112
+ return !!filename && ProjectsManager.ConfigFileNames.includes(filename);
113
+ }
82
114
  /**
83
115
  * Checks if the provided file system entry is a valid project config file.
84
116
  *
@@ -87,17 +119,12 @@ export class ProjectsManager {
87
119
  */
88
120
  async loadConfigFile(entry) {
89
121
  if (entry.isDirectory) {
90
- return false;
91
- }
92
- const filename = parseFilename(entry.uri.fsPath, { strict: false });
93
- if (!filename) {
94
- return false;
122
+ return void 0;
95
123
  }
96
- if (ProjectsManager.ConfigFileNames.includes(filename)) {
97
- await this.registerProject(entry.uri);
98
- return true;
124
+ if (this.isConfigFile(entry)) {
125
+ return await this.registerProject(entry.uri);
99
126
  }
100
- return false;
127
+ return void 0;
101
128
  }
102
129
  async registerProject(opts) {
103
130
  if (URI.isUri(opts)) {
@@ -109,9 +136,10 @@ export class ProjectsManager {
109
136
  return this.registerProject({ config: config2, folderUri: folderUri2 });
110
137
  }
111
138
  const { config, folderUri } = opts;
112
- const id = config.name;
113
- if (this._projects.some(({ id: existingId }) => existingId === id)) {
114
- throw new Error(`Project ID ${id} already registered`);
139
+ let id = config.name;
140
+ let i = 1;
141
+ while (this.projectIdToFolder.has(id)) {
142
+ id = `${config.name}-${i++}`;
115
143
  }
116
144
  let folder;
117
145
  if (URI.isUri(folderUri)) {
@@ -119,14 +147,25 @@ export class ProjectsManager {
119
147
  } else {
120
148
  folder = hasProtocol(folderUri) ? folderUri : withProtocol(folderUri, "file://");
121
149
  }
150
+ const project = {
151
+ id,
152
+ config,
153
+ folder
154
+ };
155
+ if (isNullish(config.exclude)) {
156
+ project.exclude = this.defaultGlobalProject.exclude;
157
+ } else if (hasAtLeast(config.exclude, 1)) {
158
+ project.exclude = picomatch(config.exclude);
159
+ }
122
160
  this._projects = pipe(
123
- [...this._projects, { folder, config, id }],
161
+ [...this._projects, project],
124
162
  sortBy(
125
163
  [({ folder: folder2 }) => withoutProtocol(folder2).split("/").length, "desc"]
126
164
  )
127
165
  );
128
166
  this.projectIdToFolder.set(id, folder);
129
- logger.debug`registered project ${id} folder: ${folder})`;
167
+ logger.info`register project ${id} folder: ${folder})`;
168
+ return project;
130
169
  }
131
170
  belongsTo(document) {
132
171
  let documentUri;
@@ -137,12 +176,17 @@ export class ProjectsManager {
137
176
  } else {
138
177
  documentUri = document.uri.toString();
139
178
  }
140
- return this.mappingsToProject.get(documentUri, () => this.getProjectId(documentUri));
179
+ return this.findProjectForDocument(documentUri).id;
141
180
  }
142
- getProjectId(documentUri) {
143
- const project = this._projects.find(({ folder }) => documentUri.toString().startsWith(folder));
144
- return project?.id ?? ProjectsManager.DefaultProjectId;
181
+ findProjectForDocument(documentUri) {
182
+ return this.mappingsToProject.get(documentUri, () => {
183
+ const project = this._projects.find(({ folder }) => documentUri.startsWith(folder));
184
+ return project ?? this.defaultGlobalProject;
185
+ });
145
186
  }
187
+ // The mapping between document URIs and their corresponding project ID
188
+ // Lazy-created due to initialization order of the LanguageServer
189
+ _mappingsToProject;
146
190
  get mappingsToProject() {
147
191
  this._mappingsToProject ??= new WorkspaceCache(this.services);
148
192
  return this._mappingsToProject;
@@ -1,11 +1,11 @@
1
- import type { LangiumDocument } from 'langium';
1
+ import type { FileSystemNode, LangiumDocument } from 'langium';
2
2
  import { DefaultWorkspaceManager } from 'langium';
3
3
  import type { WorkspaceFolder } from 'vscode-languageserver';
4
4
  import { URI } from 'vscode-uri';
5
5
  import type { LikeC4SharedServices } from '../module';
6
6
  export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
7
+ private services;
7
8
  private documentFactory;
8
- private projects;
9
9
  constructor(services: LikeC4SharedServices);
10
10
  /**
11
11
  * Load all additional documents that shall be visible in the context of the given workspace
@@ -14,10 +14,9 @@ export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
14
14
  */
15
15
  protected loadAdditionalDocuments(folders: WorkspaceFolder[], collector: (document: LangiumDocument) => void): Promise<void>;
16
16
  /**
17
- * We override the default implementation to process project config files during the traversal.
18
- * This is necessary to ensure that the project config files are loaded and processed correctly.
17
+ * Determine whether the given folder entry shall be included while indexing the workspace.
19
18
  */
20
- protected traverseFolder(workspaceFolder: WorkspaceFolder, folderPath: URI, fileExtensions: string[], collector: (document: LangiumDocument) => void): Promise<void>;
19
+ protected includeEntry(_workspaceFolder: WorkspaceFolder, entry: FileSystemNode, fileExtensions: string[]): boolean;
21
20
  workspace(): any;
22
21
  get workspaceUri(): URI;
23
22
  get workspaceURL(): URL;
@@ -2,42 +2,75 @@ import { hasAtLeast, invariant } from "@likec4/core";
2
2
  import { DefaultWorkspaceManager } from "langium";
3
3
  import { URI } from "vscode-uri";
4
4
  import * as BuiltIn from "../likec4lib.js";
5
+ import { logError } from "../logger.js";
5
6
  export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
6
- documentFactory;
7
- projects;
8
7
  constructor(services) {
9
8
  super(services);
9
+ this.services = services;
10
10
  this.documentFactory = services.workspace.LangiumDocumentFactory;
11
- this.projects = services.workspace.ProjectsManager;
12
11
  }
12
+ documentFactory;
13
13
  /**
14
14
  * Load all additional documents that shall be visible in the context of the given workspace
15
15
  * folders and add them to the collector. This can be used to include built-in libraries of
16
16
  * your language, which can be either loaded from provided files or constructed in memory.
17
17
  */
18
18
  async loadAdditionalDocuments(folders, collector) {
19
+ const projects = this.services.workspace.ProjectsManager;
20
+ for (const folder of folders) {
21
+ try {
22
+ const content = await this.fileSystemProvider.readDirectory(URI.parse(folder.uri));
23
+ for (const entry of content) {
24
+ try {
25
+ await projects.loadConfigFile(entry);
26
+ } catch (error) {
27
+ logError(error);
28
+ }
29
+ }
30
+ } catch (error) {
31
+ logError(error);
32
+ }
33
+ }
19
34
  collector(this.documentFactory.fromString(BuiltIn.Content, URI.parse(BuiltIn.Uri)));
20
35
  await super.loadAdditionalDocuments(folders, collector);
21
36
  }
37
+ // /**
38
+ // * We override the default implementation to process project config files during the traversal.
39
+ // * This is necessary to ensure that the project config files are loaded and processed correctly.
40
+ // */
41
+ // protected override async traverseFolder(
42
+ // workspaceFolder: WorkspaceFolder,
43
+ // folderPath: URI,
44
+ // fileExtensions: string[],
45
+ // collector: (document: LangiumDocument) => void,
46
+ // ): Promise<void> {
47
+ // // Then load other files
48
+ // for (const entry of nonConfigFiles) {
49
+ // try {
50
+ // if (this.includeEntry(workspaceFolder, entry, fileExtensions)) {
51
+ // if (entry.isDirectory) {
52
+ // await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector)
53
+ // } else if (entry.isFile) {
54
+ // const document = await this.langiumDocuments.getOrCreateDocument(entry.uri)
55
+ // collector(document)
56
+ // }
57
+ // }
58
+ // } catch (error) {
59
+ // logError(error)
60
+ // }
61
+ // }
62
+ // }
22
63
  /**
23
- * We override the default implementation to process project config files during the traversal.
24
- * This is necessary to ensure that the project config files are loaded and processed correctly.
64
+ * Determine whether the given folder entry shall be included while indexing the workspace.
25
65
  */
26
- async traverseFolder(workspaceFolder, folderPath, fileExtensions, collector) {
27
- const content = await this.fileSystemProvider.readDirectory(folderPath);
28
- await Promise.all(content.map(async (entry) => {
29
- if (await this.projects.loadConfigFile(entry)) {
30
- return;
31
- }
32
- if (this.includeEntry(workspaceFolder, entry, fileExtensions)) {
33
- if (entry.isDirectory) {
34
- await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector);
35
- } else if (entry.isFile) {
36
- const document = await this.langiumDocuments.getOrCreateDocument(entry.uri);
37
- collector(document);
38
- }
39
- }
40
- }));
66
+ includeEntry(_workspaceFolder, entry, fileExtensions) {
67
+ if (this.services.workspace.ProjectsManager.isConfigFile(entry)) {
68
+ return false;
69
+ }
70
+ if (entry.isFile) {
71
+ return !this.services.workspace.ProjectsManager.checkIfExcluded(entry.uri);
72
+ }
73
+ return super.includeEntry(_workspaceFolder, entry, fileExtensions);
41
74
  }
42
75
  workspace() {
43
76
  if (this.folders && hasAtLeast(this.folders, 1)) {
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.27.2",
4
+ "version": "1.28.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -109,24 +109,26 @@
109
109
  "p-debounce": "^4.0.0",
110
110
  "pretty-ms": "^9.2.0",
111
111
  "remeda": "^2.21.2",
112
+ "@types/picomatch": "^4.0.0",
113
+ "picomatch": "^4.0.2",
112
114
  "strip-indent": "^4.0.0",
113
115
  "tsx": "~4.19.3",
114
- "turbo": "^2.4.4",
115
- "type-fest": "^4.38.0",
116
- "typescript": "^5.8.2",
116
+ "turbo": "^2.5.0",
117
+ "type-fest": "^4.39.1",
118
+ "typescript": "^5.8.3",
117
119
  "ufo": "^1.5.4",
118
120
  "unbuild": "^3.5.0",
119
121
  "valibot": "^1.0.0",
120
- "vitest": "^3.0.9",
122
+ "vitest": "^3.1.1",
121
123
  "vscode-languageserver": "9.0.1",
122
124
  "vscode-languageserver-types": "3.17.5",
123
125
  "vscode-languageserver-protocol": "3.17.5",
124
126
  "which": "^5.0.0",
125
- "@likec4/core": "1.27.2",
126
- "@likec4/icons": "1.27.2",
127
- "@likec4/layouts": "1.27.2",
128
- "@likec4/log": "1.27.2",
129
- "@likec4/tsconfig": "1.27.2"
127
+ "@likec4/core": "1.28.0",
128
+ "@likec4/icons": "1.28.0",
129
+ "@likec4/log": "1.28.0",
130
+ "@likec4/layouts": "1.28.0",
131
+ "@likec4/tsconfig": "1.28.0"
130
132
  },
131
133
  "scripts": {
132
134
  "typecheck": "tsc --noEmit",
@@ -1,4 +0,0 @@
1
- import type { ValidationCheck } from 'langium';
2
- import { ast } from '../ast';
3
- import type { LikeC4Services } from '../module';
4
- export declare const dynamicViewRulePredicate: (_services: LikeC4Services) => ValidationCheck<ast.DynamicViewPredicateIterator>;
@@ -1,17 +0,0 @@
1
- import { ast, elementExpressionFromPredicate } from "../ast.js";
2
- import { tryOrLog } from "./_shared.js";
3
- export const dynamicViewRulePredicate = (_services) => {
4
- return tryOrLog((predicate, accept) => {
5
- const expr = elementExpressionFromPredicate(predicate.value);
6
- switch (true) {
7
- case ast.isElementKindExpression(expr):
8
- case ast.isElementTagExpression(expr):
9
- case ast.isWildcardExpression(expr): {
10
- accept("warning", `Predicate is ignored, as not supported in dynamic views`, {
11
- node: predicate
12
- });
13
- return;
14
- }
15
- }
16
- });
17
- };
@@ -1,31 +0,0 @@
1
- import { nonexhaustive } from "@likec4/core";
2
- import { AstUtils } from "langium";
3
- import { ast } from "../../ast.js";
4
- import { tryOrLog } from "../_shared.js";
5
- export const elementPredicateWithChecks = (_services) => {
6
- return tryOrLog((el, accept) => {
7
- const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate);
8
- if (ast.isExcludePredicate(container)) {
9
- accept("error", 'Invalid usage inside "exclude"', {
10
- node: el
11
- });
12
- }
13
- const subject = ast.isElementPredicateWhere(el.subject) ? el.subject.subject : el.subject;
14
- switch (true) {
15
- case ast.isElementRef(subject):
16
- case ast.isElementDescedantsExpression(subject):
17
- case ast.isExpandElementExpression(subject):
18
- case ast.isWildcardExpression(subject):
19
- return;
20
- case ast.isElementKindExpression(subject):
21
- case ast.isElementTagExpression(subject):
22
- accept("error", "Invalid target (expect reference to specific element)", {
23
- node: el,
24
- property: "subject"
25
- });
26
- return;
27
- default:
28
- nonexhaustive(subject);
29
- }
30
- });
31
- };
@@ -1,12 +0,0 @@
1
- import { AstUtils } from "langium";
2
- import { ast } from "../../ast.js";
3
- import { tryOrLog } from "../_shared.js";
4
- export const expandElementExprChecks = (_services) => {
5
- return tryOrLog((el, accept) => {
6
- if (AstUtils.hasContainerOfType(el, ast.isRelationExpression)) {
7
- accept("warning", `Redundant usage, expand predicate resolves parent element only when used in relations`, {
8
- node: el
9
- });
10
- }
11
- });
12
- };
@@ -1,5 +0,0 @@
1
- import { type ValidationCheck } from 'langium';
2
- import { ast } from '../../ast';
3
- import type { LikeC4Services } from '../../module';
4
- export declare const relationExprChecks: (services: LikeC4Services) => ValidationCheck<ast.RelationExpr>;
5
- export declare const fqnRefExprChecks: (services: LikeC4Services) => ValidationCheck<ast.FqnRefExpr>;
@@ -1,83 +0,0 @@
1
- import { FqnExpr, FqnRef } from "@likec4/core";
2
- import { AstUtils } from "langium";
3
- import { isNonNullish, isNullish } from "remeda";
4
- import { ast } from "../../ast.js";
5
- import { tryOrLog } from "../_shared.js";
6
- export const relationExprChecks = (services) => {
7
- const ModelParser = services.likec4.ModelParser;
8
- return tryOrLog((node, accept) => {
9
- if (node.$container.$type !== "DeploymentViewRulePredicateExpression") {
10
- return;
11
- }
12
- const predicate = AstUtils.getContainerOfType(node, ast.isDeploymentViewRulePredicate);
13
- if (!predicate || predicate.isInclude !== true) {
14
- return;
15
- }
16
- const doc = AstUtils.getDocument(node);
17
- const parser = ModelParser.forDocument(doc);
18
- const ModelRefOnlyExclude = "Model reference is allowed in exclude predicate only";
19
- if (ast.isDirectedRelationExpr(node)) {
20
- if (FqnExpr.isModelRef(parser.parseFqnExpr(node.source.from))) {
21
- accept("error", ModelRefOnlyExclude, {
22
- node: node.source,
23
- property: "from"
24
- });
25
- }
26
- if (FqnExpr.isModelRef(parser.parseFqnExpr(node.target))) {
27
- accept("error", ModelRefOnlyExclude, {
28
- node,
29
- property: "target"
30
- });
31
- }
32
- return;
33
- }
34
- let expr;
35
- if (ast.isIncomingRelationExpr(node)) {
36
- expr = node.to;
37
- } else if (ast.isOutgoingRelationExpr(node)) {
38
- expr = node.from;
39
- } else {
40
- expr = node.inout.to;
41
- }
42
- if (FqnExpr.isModelRef(parser.parseFqnExpr(expr))) {
43
- accept("error", ModelRefOnlyExclude, {
44
- node
45
- });
46
- return;
47
- }
48
- });
49
- };
50
- export const fqnRefExprChecks = (services) => {
51
- const ModelParser = services.likec4.ModelParser;
52
- return tryOrLog((node, accept) => {
53
- const referenceTo = node.ref.value.ref;
54
- if (isNullish(referenceTo)) {
55
- accept("error", "Invalid empty reference", {
56
- node
57
- });
58
- return;
59
- }
60
- const doc = AstUtils.getDocument(node);
61
- const expr = ModelParser.forDocument(doc).parseFqnRefExpr(node);
62
- if (node.$container.$type === "DeploymentViewRulePredicateExpression") {
63
- if (FqnExpr.isModelRef(expr)) {
64
- accept("error", "Deployment view predicate must reference deployment model", {
65
- node
66
- });
67
- return;
68
- }
69
- if (FqnExpr.isDeploymentRef(expr) && FqnRef.isInsideInstanceRef(expr.ref)) {
70
- accept("error", "Must reference deployment nodes or instances, but not internals", {
71
- node
72
- });
73
- return;
74
- }
75
- }
76
- if (!ast.isDeploymentNode(referenceTo) && isNonNullish(node.selector)) {
77
- accept("warning", `Selector '${node.selector}' applies to deployment nodes only, ignored here`, {
78
- node,
79
- property: "selector"
80
- });
81
- }
82
- });
83
- };