@likec4/language-server 1.20.1 → 1.20.2

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/README.md +19 -0
  2. package/bin/likec4-language-server.mjs +5 -0
  3. package/dist/LikeC4FileSystem.js +9 -9
  4. package/dist/Rpc.d.ts +2 -4
  5. package/dist/Rpc.js +27 -36
  6. package/dist/ast.d.ts +1 -0
  7. package/dist/ast.js +5 -1
  8. package/dist/bundled.mjs +5924 -0
  9. package/dist/formatting/LikeC4Formatter.d.ts +9 -0
  10. package/dist/formatting/LikeC4Formatter.js +131 -14
  11. package/dist/generated/ast.d.ts +13 -2
  12. package/dist/generated/ast.js +18 -1
  13. package/dist/generated/grammar.js +1 -1
  14. package/dist/lsp/CompletionProvider.js +11 -3
  15. package/dist/model/deployments-index.d.ts +2 -1
  16. package/dist/model/deployments-index.js +3 -10
  17. package/dist/model/fqn-index.d.ts +2 -1
  18. package/dist/model/fqn-index.js +24 -17
  19. package/dist/model/model-builder.d.ts +2 -1
  20. package/dist/model/model-builder.js +32 -30
  21. package/dist/model/model-parser.d.ts +1 -1
  22. package/dist/model/model-parser.js +9 -6
  23. package/dist/model/parser/PredicatesParser.js +7 -1
  24. package/dist/utils/disposable.d.ts +8 -0
  25. package/dist/utils/disposable.js +25 -0
  26. package/dist/utils/index.d.ts +1 -0
  27. package/dist/utils/index.js +1 -0
  28. package/dist/validation/_shared.js +4 -1
  29. package/dist/validation/index.d.ts +2 -2
  30. package/dist/validation/index.js +4 -1
  31. package/dist/validation/specification.d.ts +1 -0
  32. package/dist/validation/specification.js +30 -0
  33. package/package.json +33 -27
  34. package/src/LikeC4FileSystem.ts +14 -13
  35. package/src/Rpc.ts +28 -38
  36. package/src/ast.ts +6 -1
  37. package/src/formatting/LikeC4Formatter.ts +198 -17
  38. package/src/generated/ast.ts +35 -2
  39. package/src/generated/grammar.ts +1 -1
  40. package/src/like-c4.langium +14 -3
  41. package/src/lsp/CompletionProvider.ts +27 -18
  42. package/src/model/deployments-index.ts +4 -17
  43. package/src/model/fqn-index.ts +26 -19
  44. package/src/model/model-builder.ts +32 -31
  45. package/src/model/model-parser.ts +14 -11
  46. package/src/model/parser/PredicatesParser.ts +30 -24
  47. package/src/utils/disposable.ts +30 -0
  48. package/src/utils/index.ts +1 -0
  49. package/src/validation/_shared.ts +5 -2
  50. package/src/validation/index.ts +6 -2
  51. package/src/validation/specification.ts +34 -0
  52. package/contrib/likec4.tmLanguage.json +0 -73
  53. package/dist/like-c4.langium +0 -852
package/README.md CHANGED
@@ -3,3 +3,22 @@
3
3
  [docs](https://likec4.dev/) | [playground](https://playground.likec4.dev/) | [demo](https://template.likec4.dev/view/cloud)
4
4
 
5
5
  Language Server Protocol (LSP) based on [languim](https://github.com/languim/languim) library.
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ npm install -g @likec4/language-server
11
+ likec4-language-server --stdio
12
+ ```
13
+
14
+ Valid arguments:
15
+ - `--node-ipc`
16
+ - `--stdio`
17
+ - `--socket={number}`
18
+
19
+ ### Usage in code
20
+
21
+ ```js
22
+ import { startLanguageServer } from '@likec4/language-server/bundled';
23
+ startLanguageServer();
24
+ ```
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { startLanguageServer } from '../dist/bundled.mjs'
4
+
5
+ startLanguageServer()
@@ -1,26 +1,26 @@
1
1
  import { fdir } from "fdir";
2
2
  import { URI } from "langium";
3
3
  import { NodeFileSystemProvider } from "langium/node";
4
- import { stat } from "node:fs/promises";
4
+ import { LikeC4LanguageMetaData } from "./generated/module.js";
5
5
  import { logger } from "./logger.js";
6
6
  export const LikeC4FileSystem = {
7
7
  fileSystemProvider: () => new SymLinkTraversingFileSystemProvider()
8
8
  };
9
+ const hasExtension = (path) => LikeC4LanguageMetaData.fileExtensions.some((ext) => path.endsWith(ext));
9
10
  class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
10
11
  async readDirectory(folderPath) {
11
- const crawled = await new fdir().withSymlinks().withFullPaths().crawl(folderPath.fsPath).withPromise();
12
12
  const entries = [];
13
- for (const path of crawled) {
14
- try {
15
- const stats = await stat(path);
13
+ try {
14
+ const crawled = await new fdir().withSymlinks().withFullPaths().filter(hasExtension).crawl(folderPath.fsPath).withPromise();
15
+ for (const path of crawled) {
16
16
  entries.push({
17
- isFile: stats.isFile(),
18
- isDirectory: stats.isDirectory(),
17
+ isFile: true,
18
+ isDirectory: false,
19
19
  uri: URI.file(path)
20
20
  });
21
- } catch (error) {
22
- logger.error(error);
23
21
  }
22
+ } catch (error) {
23
+ logger.error(error);
24
24
  }
25
25
  return entries;
26
26
  }
package/dist/Rpc.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  import type { LikeC4Services } from './module';
2
- import { Disposable } from 'langium';
3
- export declare class Rpc implements Disposable {
2
+ import { ADisposable } from './utils';
3
+ export declare class Rpc extends ADisposable {
4
4
  private services;
5
- private disposables;
6
5
  constructor(services: LikeC4Services);
7
6
  init(): void;
8
- dispose(): void;
9
7
  }
package/dist/Rpc.js CHANGED
@@ -1,5 +1,5 @@
1
- import { debounce } from "remeda";
2
- import { logError, logger } from "./logger.js";
1
+ import { funnel } from "remeda";
2
+ import { logger } from "./logger.js";
3
3
  import { nonexhaustive } from "@likec4/core";
4
4
  import { Disposable, interruptAndCheck, URI, UriUtils } from "langium";
5
5
  import { isLikeC4LangiumDocument } from "./ast.js";
@@ -14,11 +14,12 @@ import {
14
14
  locate,
15
15
  onDidChangeModel
16
16
  } from "./protocol.js";
17
- export class Rpc {
17
+ import { ADisposable } from "./utils/index.js";
18
+ export class Rpc extends ADisposable {
18
19
  constructor(services) {
20
+ super();
19
21
  this.services = services;
20
22
  }
21
- disposables = [];
22
23
  init() {
23
24
  const modelBuilder = this.services.likec4.ModelBuilder;
24
25
  const modelLocator = this.services.likec4.ModelLocator;
@@ -32,28 +33,26 @@ export class Rpc {
32
33
  logger.info(`[ServerRpc] init`);
33
34
  const LangiumDocuments = this.services.shared.workspace.LangiumDocuments;
34
35
  const DocumentBuilder = this.services.shared.workspace.DocumentBuilder;
35
- const notifyModelParsed = debounce(
36
+ const notifyModelParsed = funnel(
36
37
  () => {
37
38
  connection.sendNotification(onDidChangeModel, "").catch((e) => {
38
- logger.error(`[ServerRpc] error sending onDidChangeModel: ${e}`);
39
+ logger.warn(`[ServerRpc] error sending onDidChangeModel: ${e}`);
39
40
  return Promise.resolve();
40
41
  });
41
42
  },
42
43
  {
43
- timing: "trailing",
44
- waitMs: 300,
45
- maxWaitMs: 1e3
44
+ minQuietPeriodMs: 250,
45
+ maxBurstDurationMs: 1e3,
46
+ minGapMs: 200
46
47
  }
47
48
  );
48
49
  let isFirstBuild = true;
49
- this.disposables.push(
50
- Disposable.create(() => {
51
- notifyModelParsed.cancel();
52
- }),
50
+ this.onDispose(
53
51
  modelBuilder.onModelParsed(() => notifyModelParsed.call()),
54
52
  connection.onRequest(fetchComputedModel, async ({ cleanCaches }, cancelToken) => {
55
53
  if (cleanCaches) {
56
54
  this.services.WorkspaceCache.clear();
55
+ this.services.DocumentCache.clear();
57
56
  }
58
57
  const model = await modelBuilder.buildComputedModel(cancelToken);
59
58
  return { model };
@@ -94,39 +93,31 @@ export class Rpc {
94
93
  }
95
94
  })
96
95
  );
97
- await interruptAndCheck(cancelToken);
98
96
  }
99
97
  isFirstBuild = false;
98
+ await interruptAndCheck(cancelToken);
100
99
  await DocumentBuilder.update(changed, deleted, cancelToken);
101
100
  }),
102
101
  connection.onRequest(locate, (params) => {
103
- if ("element" in params) {
104
- return modelLocator.locateElement(params.element, params.property ?? "name");
105
- }
106
- if ("relation" in params) {
107
- return modelLocator.locateRelation(params.relation);
108
- }
109
- if ("view" in params) {
110
- return modelLocator.locateView(params.view);
111
- }
112
- if ("deployment" in params) {
113
- return modelLocator.locateDeploymentElement(params.deployment, params.property ?? "name");
102
+ switch (true) {
103
+ case "element" in params:
104
+ return modelLocator.locateElement(params.element, params.property ?? "name");
105
+ case "relation" in params:
106
+ return modelLocator.locateRelation(params.relation);
107
+ case "view" in params:
108
+ return modelLocator.locateView(params.view);
109
+ case "deployment" in params:
110
+ return modelLocator.locateDeploymentElement(params.deployment, params.property ?? "name");
111
+ default:
112
+ nonexhaustive(params);
114
113
  }
115
- nonexhaustive(params);
116
114
  }),
117
115
  connection.onRequest(changeView, async (request, _cancelToken) => {
118
116
  return await modelEditor.applyChange(request);
117
+ }),
118
+ Disposable.create(() => {
119
+ notifyModelParsed.cancel();
119
120
  })
120
121
  );
121
122
  }
122
- dispose() {
123
- let item;
124
- while (item = this.disposables.pop()) {
125
- try {
126
- item.dispose();
127
- } catch (e) {
128
- logError(e);
129
- }
130
- }
131
- }
132
123
  }
package/dist/ast.d.ts CHANGED
@@ -33,6 +33,7 @@ type ParsedElementStyle = {
33
33
  color?: c4.Color;
34
34
  border?: c4.BorderStyle;
35
35
  opacity?: number;
36
+ multiple?: boolean;
36
37
  };
37
38
  export interface ParsedAstSpecification {
38
39
  tags: Set<c4.Tag>;
package/dist/ast.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DefaultArrowType, DefaultLineStyle, DefaultRelationshipColor, LinkedList, nonexhaustive } from "@likec4/core";
2
2
  import { DocumentState } from "langium";
3
- import { clamp, isDefined, isNullish, isTruthy } from "remeda";
3
+ import { clamp, isBoolean, isDefined, isNullish, isTruthy } from "remeda";
4
4
  import * as ast from "./generated/ast.js";
5
5
  import { LikeC4LanguageMetaData } from "./generated/module.js";
6
6
  import { elementRef } from "./utils/elementRef.js";
@@ -152,6 +152,10 @@ export function toElementStyle(props, isValid) {
152
152
  result.opacity = parseAstOpacityProperty(prop);
153
153
  break;
154
154
  }
155
+ case ast.isMultipleProperty(prop): {
156
+ result.multiple = isBoolean(prop.value) ? prop.value : false;
157
+ break;
158
+ }
155
159
  default:
156
160
  nonexhaustive(prop);
157
161
  }