@likec4/language-server 1.46.3 → 1.47.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 (53) hide show
  1. package/dist/LikeC4LanguageServices.d.ts +27 -21
  2. package/dist/LikeC4LanguageServices.js +24 -14
  3. package/dist/Rpc.js +9 -3
  4. package/dist/ast.d.ts +1 -1
  5. package/dist/browser.d.ts +0 -1
  6. package/dist/browser.js +0 -1
  7. package/dist/bundled.mjs +3773 -3536
  8. package/dist/filesystem/ChokidarWatcher.d.ts +3 -0
  9. package/dist/filesystem/ChokidarWatcher.js +67 -42
  10. package/dist/filesystem/LikeC4FileSystem.d.ts +1 -1
  11. package/dist/filesystem/LikeC4FileSystem.js +16 -6
  12. package/dist/generated/ast.d.ts +2 -2
  13. package/dist/generated/ast.js +3 -3
  14. package/dist/generated/grammar.js +1 -1
  15. package/dist/generated-lib/icons.js +7 -1
  16. package/dist/index.d.ts +0 -1
  17. package/dist/index.js +0 -1
  18. package/dist/lsp/CodeLensProvider.js +1 -1
  19. package/dist/lsp/CompletionProvider.d.ts +4 -2
  20. package/dist/lsp/CompletionProvider.js +41 -3
  21. package/dist/lsp/DocumentSymbolProvider.js +1 -1
  22. package/dist/lsp/SemanticTokenProvider.d.ts +8 -1
  23. package/dist/lsp/SemanticTokenProvider.js +52 -11
  24. package/dist/mcp/interfaces.d.ts +1 -1
  25. package/dist/mcp/interfaces.js +0 -1
  26. package/dist/mcp/server/StreamableLikeC4MCPServer.js +27 -51
  27. package/dist/mcp/tools/_common.d.ts +2 -2
  28. package/dist/mcp/tools/find-relationships.d.ts +195 -5
  29. package/dist/mcp/tools/list-projects.d.ts +191 -3
  30. package/dist/mcp/tools/open-view.d.ts +194 -4
  31. package/dist/mcp/tools/read-deployment.d.ts +194 -4
  32. package/dist/mcp/tools/read-element.d.ts +194 -4
  33. package/dist/mcp/tools/read-project-summary.d.ts +193 -3
  34. package/dist/mcp/tools/read-view.d.ts +194 -4
  35. package/dist/mcp/tools/search-element.d.ts +193 -3
  36. package/dist/model/model-builder.d.ts +4 -2
  37. package/dist/model/model-builder.js +58 -57
  38. package/dist/model/model-parser.d.ts +6 -6
  39. package/dist/model/parser/Base.js +58 -48
  40. package/dist/model/parser/GlobalsParser.d.ts +3 -3
  41. package/dist/model/parser/ViewsParser.js +2 -2
  42. package/dist/protocol.d.ts +5 -0
  43. package/dist/references/scope-provider.d.ts +1 -1
  44. package/dist/references/scope-provider.js +18 -21
  45. package/dist/utils/elementRef.js +10 -4
  46. package/dist/validation/index.d.ts +1 -1
  47. package/dist/workspace/IndexManager.js +4 -2
  48. package/dist/workspace/LangiumDocuments.d.ts +4 -0
  49. package/dist/workspace/LangiumDocuments.js +26 -9
  50. package/dist/workspace/ProjectsManager.d.ts +9 -3
  51. package/dist/workspace/ProjectsManager.js +141 -102
  52. package/package.json +16 -15
  53. package/lib/package.json +0 -159
@@ -13,4 +13,7 @@ export declare class ChokidarFileSystemWatcher implements FileSystemWatcher {
13
13
  dispose(): Promise<void>;
14
14
  private createWatcher;
15
15
  private enqueueFileOp;
16
+ private onAddOrChange;
17
+ private onRemove;
18
+ private onRemoveDir;
16
19
  }
@@ -1,5 +1,4 @@
1
1
  import { isLikeC4Config } from '@likec4/config/node';
2
- import { loggable } from '@likec4/log';
3
2
  import chokidar from 'chokidar';
4
3
  import { URI } from 'langium';
5
4
  import PQueue from 'p-queue';
@@ -23,12 +22,11 @@ export class ChokidarFileSystemWatcher {
23
22
  }
24
23
  watch(folder) {
25
24
  if (this.watcher) {
25
+ logger.debug `add watching folder: ${folder}`;
26
26
  this.watcher.add(folder);
27
+ return;
27
28
  }
28
- else {
29
- this.watcher = this.createWatcher(folder);
30
- }
31
- logger.debug `watching folder: ${folder}`;
29
+ this.watcher = this.createWatcher(folder);
32
30
  }
33
31
  async dispose() {
34
32
  if (this.watcher) {
@@ -39,6 +37,7 @@ export class ChokidarFileSystemWatcher {
39
37
  return;
40
38
  }
41
39
  createWatcher(folder) {
40
+ logger.debug `create watcher for folder: ${folder}`;
42
41
  let watcher = chokidar.watch(folder, {
43
42
  ignored: [
44
43
  path => path.includes('node_modules') || path.includes('.git'),
@@ -47,50 +46,30 @@ export class ChokidarFileSystemWatcher {
47
46
  followSymlinks: true,
48
47
  ignoreInitial: true,
49
48
  });
50
- const onAddOrChange = (path) => {
49
+ const onAddOrChange = (path, stats) => {
50
+ if (stats?.isDirectory()) {
51
+ return;
52
+ }
51
53
  this.enqueueFileOp('addOrChange: ' + path, async () => {
52
- if (isLikeC4Config(path)) {
53
- logger.debug `project file changed: ${path}`;
54
- await this.services.workspace.ProjectsManager.reloadProjects();
55
- }
56
- else if (isLikeC4File(path)) {
57
- logger.debug `file changed: ${path}`;
58
- await this.services.workspace.DocumentBuilder.update([URI.file(path)], []);
59
- }
60
- else if (isManualLayoutFile(path)) {
61
- logger.debug `manual layout file changed: ${path}`;
62
- const projectId = this.services.workspace.ProjectsManager.belongsTo(URI.file(path));
63
- await this.services.workspace.ProjectsManager.rebuidProject(projectId);
64
- }
65
- else {
66
- logger.warn `Unknown file change: ${path}`;
67
- }
54
+ await this.onAddOrChange(path);
68
55
  });
69
56
  };
70
- const onRemove = (path) => {
57
+ const onRemove = (path, stats) => {
58
+ if (stats?.isDirectory()) {
59
+ return;
60
+ }
71
61
  this.enqueueFileOp('remove: ' + path, async () => {
72
- const pm = this.services.workspace.ProjectsManager;
73
- if (isLikeC4Config(path)) {
74
- logger.debug `project file removed: ${path}`;
75
- await pm.reloadProjects();
76
- }
77
- else if (isLikeC4File(path)) {
78
- logger.debug `file removed: ${path}`;
79
- await this.services.workspace.DocumentBuilder.update([], [URI.file(path)]);
80
- }
81
- else if (isManualLayoutFile(path)) {
82
- logger.debug `manual layout file removed: ${path}`;
83
- const project = pm.belongsTo(path);
84
- await pm.rebuidProject(project);
85
- }
86
- else {
87
- logger.warn `Unknown file removal: ${path}`;
88
- }
62
+ await this.onRemove(path);
89
63
  });
90
64
  };
91
65
  watcher.on('add', onAddOrChange)
92
66
  .on('change', onAddOrChange)
93
- .on('unlink', onRemove);
67
+ .on('unlink', onRemove)
68
+ .on('unlinkDir', (path) => {
69
+ this.enqueueFileOp('removeDir: ' + path, async () => {
70
+ await this.onRemoveDir(path);
71
+ });
72
+ });
94
73
  return watcher;
95
74
  }
96
75
  enqueueFileOp(fileop, fn) {
@@ -102,7 +81,53 @@ export class ChokidarFileSystemWatcher {
102
81
  logger.warn(`Failed on ${fileop}`, { error });
103
82
  }
104
83
  }).catch(error => {
105
- logger.error(loggable(error));
84
+ logger.error(`Error on ${fileop}`, { error });
106
85
  });
107
86
  }
87
+ async onAddOrChange(path) {
88
+ const pm = this.services.workspace.ProjectsManager;
89
+ if (isLikeC4Config(path)) {
90
+ logger.debug `project file changed: ${path}`;
91
+ await pm.registerConfigFile(URI.file(path));
92
+ }
93
+ else if (isLikeC4File(path)) {
94
+ logger.debug `file changed: ${path}`;
95
+ await this.services.workspace.DocumentBuilder.update([URI.file(path)], []);
96
+ }
97
+ else if (isManualLayoutFile(path)) {
98
+ logger.debug `manual layout file changed: ${path}`;
99
+ const projectId = pm.belongsTo(URI.file(path));
100
+ await pm.rebuidProject(projectId);
101
+ }
102
+ else {
103
+ logger.warn `Unknown file change: ${path}`;
104
+ }
105
+ }
106
+ async onRemove(path) {
107
+ const pm = this.services.workspace.ProjectsManager;
108
+ if (isLikeC4Config(path)) {
109
+ logger.debug `project file removed: ${path}`;
110
+ await pm.reloadProjects();
111
+ }
112
+ else if (isLikeC4File(path)) {
113
+ logger.debug `file removed: ${path}`;
114
+ await this.services.workspace.DocumentBuilder.update([], [URI.file(path)]);
115
+ }
116
+ else if (isManualLayoutFile(path)) {
117
+ logger.debug `manual layout file removed: ${path}`;
118
+ const project = pm.belongsTo(path);
119
+ await pm.rebuidProject(project);
120
+ }
121
+ else {
122
+ logger.warn `Unknown file removal: ${path}`;
123
+ }
124
+ }
125
+ async onRemoveDir(path) {
126
+ logger.debug `directory removed: ${path}`;
127
+ const pm = this.services.workspace.ProjectsManager;
128
+ const projects = pm.findAllProjectsByFolder(path);
129
+ if (projects.length > 0) {
130
+ await pm.reloadProjects();
131
+ }
132
+ }
108
133
  }
@@ -1,3 +1,3 @@
1
1
  import type { FileSystemModuleContext } from './index';
2
2
  export declare const LikeC4FileSystem: (ehableWatcher?: boolean) => FileSystemModuleContext;
3
- export declare const isLikeC4File: (path: string) => boolean;
3
+ export declare const isLikeC4File: (path: string, isDirectory?: boolean) => boolean;
@@ -1,4 +1,5 @@
1
1
  import { isLikeC4Config, loadConfig } from '@likec4/config/node';
2
+ import { compareNaturalHierarchically } from '@likec4/core/utils';
2
3
  import { fdir } from 'fdir';
3
4
  import { URI } from 'langium';
4
5
  import { NodeFileSystemProvider } from 'langium/node';
@@ -15,7 +16,14 @@ export const LikeC4FileSystem = (ehableWatcher = true) => ({
15
16
  fileSystemProvider: () => new SymLinkTraversingFileSystemProvider(),
16
17
  ...ehableWatcher ? chokidarFileSystemWatcher : noopFileSystemWatcher,
17
18
  });
18
- export const isLikeC4File = (path) => LikeC4LanguageMetaData.fileExtensions.some((ext) => path.endsWith(ext));
19
+ export const isLikeC4File = (path, isDirectory = false) => !isDirectory && LikeC4LanguageMetaData.fileExtensions.some((ext) => path.endsWith(ext));
20
+ const isLikeC4ConfigFile = (path, isDirectory) => !isDirectory && isLikeC4Config(path);
21
+ const excludeNodeModules = (dirName) => ['node_modules', '.git', '.svn', '.yarn', '.pnpm'].includes(dirName);
22
+ /**
23
+ * Compare function for document paths to ensure consistent order
24
+ */
25
+ const compare = compareNaturalHierarchically('/');
26
+ const ensureOrder = (a, b) => compare(a.uri.path, b.uri.path);
19
27
  /**
20
28
  * A file system provider that follows symbolic links.
21
29
  * @see https://github.com/likec4/likec4/pull/1213
@@ -40,8 +48,9 @@ class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
40
48
  try {
41
49
  let crawler = new fdir()
42
50
  .withSymlinks({ resolvePaths: false })
43
- .withFullPaths()
44
- .filter(isLikeC4File);
51
+ .exclude(excludeNodeModules)
52
+ .filter(isLikeC4File)
53
+ .withFullPaths();
45
54
  if (!recursive) {
46
55
  crawler = crawler.withMaxDepth(1);
47
56
  }
@@ -62,10 +71,10 @@ class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
62
71
  catch (error) {
63
72
  logger.warn(`Failed to read directory ${folderPath.fsPath}`, { error });
64
73
  }
65
- return entries;
74
+ return entries.sort(ensureOrder);
66
75
  }
67
76
  async scanProjectFiles(folderUri) {
68
- return await this.scanDirectory(folderUri, isLikeC4Config);
77
+ return await this.scanDirectory(folderUri, isLikeC4ConfigFile);
69
78
  }
70
79
  async scanDirectory(directory, filter) {
71
80
  const entries = [];
@@ -74,6 +83,7 @@ class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
74
83
  .withSymlinks({ resolvePaths: false })
75
84
  .withFullPaths()
76
85
  .filter(filter)
86
+ .exclude(excludeNodeModules)
77
87
  .crawl(directory.fsPath)
78
88
  .withPromise();
79
89
  for (const path of crawled) {
@@ -87,7 +97,7 @@ class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
87
97
  catch (error) {
88
98
  logger.warn(`Failed to scan directory ${directory.fsPath}`, { error });
89
99
  }
90
- return entries;
100
+ return entries.sort(ensureOrder);
91
101
  }
92
102
  async loadProjectConfig(filepath) {
93
103
  return await loadConfig(filepath);
@@ -29,7 +29,7 @@ export declare const LikeC4Terminals: {
29
29
  Hex: RegExp;
30
30
  };
31
31
  export type LikeC4TerminalNames = keyof typeof LikeC4Terminals;
32
- export type LikeC4KeywordNames = "(" | ")" | "*" | "," | "->" | "-[" | ":" | ";" | "<-" | "<->" | "BottomTop" | "LeftRight" | "RightLeft" | "TopBottom" | "[" | "]" | "]->" | "amber" | "and" | "autoLayout" | "blue" | "border" | "browser" | "color" | "crow" | "cylinder" | "dashed" | "deployment" | "deploymentNode" | "description" | "diagram" | "diamond" | "dot" | "dotted" | "dynamic" | "dynamicPredicateGroup" | "element" | "element.kind" | "element.tag" | "exclude" | "extend" | "extends" | "from" | "global" | "gray" | "green" | "group" | "head" | "icon" | "icons" | "import" | "include" | "indigo" | "instance" | "instanceOf" | "is" | "kind" | "large" | "lg" | "likec4lib" | "line" | "link" | "max" | "md" | "medium" | "metadata" | "min" | "mobile" | "model" | "multiple" | "muted" | "navigateTo" | "node" | "none" | "normal" | "not" | "notation" | "notes" | "odiamond" | "odot" | "of" | "onormal" | "opacity" | "open" | "or" | "padding" | "par" | "parallel" | "person" | "predicate" | "predicateGroup" | "primary" | "queue" | "rank" | "rectangle" | "red" | "relationship" | "rgb" | "rgba" | "same" | "secondary" | "sequence" | "shape" | "sink" | "size" | "sky" | "slate" | "sm" | "small" | "solid" | "source" | "specification" | "storage" | "style" | "styleGroup" | "summary" | "tag" | "tail" | "target" | "technology" | "textSize" | "title" | "variant" | "vee" | "view" | "views" | "where" | "with" | "xl" | "xlarge" | "xs" | "xsmall" | "{" | "}";
32
+ export type LikeC4KeywordNames = "(" | ")" | "*" | "," | "->" | "-[" | ":" | ";" | "<-" | "<->" | "BottomTop" | "LeftRight" | "RightLeft" | "TopBottom" | "[" | "]" | "]->" | "amber" | "and" | "autoLayout" | "blue" | "border" | "browser" | "bucket" | "color" | "crow" | "cylinder" | "dashed" | "deployment" | "deploymentNode" | "description" | "diagram" | "diamond" | "document" | "dot" | "dotted" | "dynamic" | "dynamicPredicateGroup" | "element" | "element.kind" | "element.tag" | "exclude" | "extend" | "extends" | "from" | "global" | "gray" | "green" | "group" | "head" | "icon" | "icons" | "import" | "include" | "indigo" | "instance" | "instanceOf" | "is" | "kind" | "large" | "lg" | "likec4lib" | "line" | "link" | "max" | "md" | "medium" | "metadata" | "min" | "mobile" | "model" | "multiple" | "muted" | "navigateTo" | "node" | "none" | "normal" | "not" | "notation" | "notes" | "odiamond" | "odot" | "of" | "onormal" | "opacity" | "open" | "or" | "padding" | "par" | "parallel" | "person" | "predicate" | "predicateGroup" | "primary" | "queue" | "rank" | "rectangle" | "red" | "relationship" | "rgb" | "rgba" | "same" | "secondary" | "sequence" | "shape" | "sink" | "size" | "sky" | "slate" | "sm" | "small" | "solid" | "source" | "specification" | "storage" | "style" | "styleGroup" | "summary" | "tag" | "tail" | "target" | "technology" | "textSize" | "title" | "variant" | "vee" | "view" | "views" | "where" | "with" | "xl" | "xlarge" | "xs" | "xsmall" | "{" | "}";
33
33
  export type LikeC4TokenNames = LikeC4TerminalNames | LikeC4KeywordNames;
34
34
  export type AnyProperty = DynamicViewProperty | ElementProperty | RelationProperty | StringProperty | ViewProperty;
35
35
  export declare const AnyProperty = "AnyProperty";
@@ -66,7 +66,7 @@ export declare function isDynamicViewStep(item: unknown): item is DynamicViewSte
66
66
  export type ElementProperty = ElementStringProperty | ElementStyleProperty | IconProperty | LinkProperty | MetadataProperty;
67
67
  export declare const ElementProperty = "ElementProperty";
68
68
  export declare function isElementProperty(item: unknown): item is ElementProperty;
69
- export type ElementShape = 'browser' | 'cylinder' | 'mobile' | 'person' | 'queue' | 'rectangle' | 'storage';
69
+ export type ElementShape = 'browser' | 'bucket' | 'cylinder' | 'document' | 'mobile' | 'person' | 'queue' | 'rectangle' | 'storage';
70
70
  export declare function isElementShape(item: unknown): item is ElementShape;
71
71
  export type ExpressionV2 = FqnExprOrWith | RelationExprOrWith;
72
72
  export declare const ExpressionV2 = "ExpressionV2";
@@ -10,7 +10,7 @@ export const LikeC4Terminals = {
10
10
  WS: /[\t ]+/,
11
11
  NL: /[\r\n]+/,
12
12
  BOOLEAN: /\b(true|false)\b/,
13
- LIB_ICON: /(aws|azure|gcp|tech):[-\w]*/,
13
+ LIB_ICON: /(aws|azure|bootstrap|gcp|tech):[-\w]*/,
14
14
  URI_WITH_SCHEMA: /\w+:\/{2}\S+/,
15
15
  URI_RELATIVE: /\.{0,2}\/[^\/]\S+/,
16
16
  URI_ALIAS: /@[a-zA-Z0-9_-]*\/[^\s]+/,
@@ -78,7 +78,7 @@ export function isElementProperty(item) {
78
78
  return reflection.isInstance(item, ElementProperty);
79
79
  }
80
80
  export function isElementShape(item) {
81
- return item === 'rectangle' || item === 'person' || item === 'browser' || item === 'mobile' || item === 'cylinder' || item === 'storage' || item === 'queue';
81
+ return item === 'rectangle' || item === 'person' || item === 'browser' || item === 'mobile' || item === 'cylinder' || item === 'storage' || item === 'queue' || item === 'bucket' || item === 'document';
82
82
  }
83
83
  export const ExpressionV2 = 'ExpressionV2';
84
84
  export function isExpressionV2(item) {
@@ -109,7 +109,7 @@ export function isFqnReferenceable(item) {
109
109
  return reflection.isInstance(item, FqnReferenceable);
110
110
  }
111
111
  export function isIconId(item) {
112
- return (typeof item === 'string' && (/(aws|azure|gcp|tech):[-\w]*/.test(item)));
112
+ return (typeof item === 'string' && (/(aws|azure|bootstrap|gcp|tech):[-\w]*/.test(item)));
113
113
  }
114
114
  export function isId(item) {
115
115
  return isElementShape(item) || isThemeColor(item) || isArrowType(item) || isLineOptions(item) || isParticipant(item) || isSizeValue(item) || isDynamicViewDisplayVariantValue(item) || item === 'element' || item === 'model' || item === 'group' || item === 'node' || item === 'deployment' || item === 'instance' || (typeof item === 'string' && (/[_]*[a-zA-Z][-\w]*/.test(item)));