@likec4/language-server 1.24.1 → 1.25.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.
@@ -1,4 +1,4 @@
1
- import { DocumentState, interruptAndCheck } from "langium";
1
+ import { DocumentState } from "langium";
2
2
  import { isLikeC4LangiumDocument, ViewOps } from "../ast.js";
3
3
  import { logger } from "../logger.js";
4
4
  export class LikeC4CodeLensProvider {
@@ -11,13 +11,9 @@ export class LikeC4CodeLensProvider {
11
11
  }
12
12
  if (doc.state <= DocumentState.Linked) {
13
13
  logger.debug(`Waiting for document ${doc.uri.path} to be Linked`);
14
- await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Validated, doc.uri, cancelToken);
15
- logger.debug(`Document ${doc.uri.path} is validated`);
14
+ await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Linked, doc.uri, cancelToken);
15
+ logger.debug(`Document is linked`);
16
16
  }
17
- if (cancelToken) {
18
- await interruptAndCheck(cancelToken);
19
- }
20
- this.services.likec4.ModelParser.parse(doc);
21
17
  const views = doc.parseResult.value.views.flatMap((v) => v.views);
22
18
  return views.flatMap((ast) => {
23
19
  const viewId = ViewOps.readId(ast);
@@ -1,7 +1,8 @@
1
- import { type AstNode, type MaybePromise } from 'langium';
1
+ import { type AstNode, type LangiumDocument } from 'langium';
2
2
  import type { DocumentSymbolProvider, NodeKindProvider } from 'langium/lsp';
3
+ import type { CancellationToken, DocumentSymbolParams } from 'vscode-languageserver';
3
4
  import { type DocumentSymbol, SymbolKind } from 'vscode-languageserver-types';
4
- import { type LikeC4LangiumDocument, ast } from '../ast';
5
+ import { ast } from '../ast';
5
6
  import type { LikeC4ModelLocator, LikeC4ModelParser } from '../model';
6
7
  import type { LikeC4Services } from '../module';
7
8
  import type { LikeC4NameProvider } from '../references';
@@ -12,7 +13,7 @@ export declare class LikeC4DocumentSymbolProvider implements DocumentSymbolProvi
12
13
  protected readonly parser: LikeC4ModelParser;
13
14
  protected readonly locator: LikeC4ModelLocator;
14
15
  constructor(services: LikeC4Services);
15
- getSymbols({ parseResult: { value: { specifications, models, deployments, views, likec4lib }, }, }: LikeC4LangiumDocument): MaybePromise<DocumentSymbol[]>;
16
+ getSymbols(doc: LangiumDocument, _params: DocumentSymbolParams, cancelToken?: CancellationToken): Promise<DocumentSymbol[]>;
16
17
  protected getLikec4LibSymbol(astLib: ast.LikeC4Lib): DocumentSymbol[];
17
18
  protected getSpecSymbol(astSpec: ast.SpecificationRule): DocumentSymbol[];
18
19
  protected getModelSymbol(astModel: ast.Model): DocumentSymbol[];
@@ -1,10 +1,11 @@
1
1
  import { nonexhaustive } from "@likec4/core";
2
- import { AstUtils, GrammarUtils } from "langium";
2
+ import { AstUtils, DocumentState, GrammarUtils } from "langium";
3
3
  import { filter, isEmpty, isTruthy, map, pipe } from "remeda";
4
4
  import { SymbolKind } from "vscode-languageserver-types";
5
- import { ast } from "../ast.js";
6
- import { logWarnError } from "../logger.js";
5
+ import { ast, isLikeC4LangiumDocument } from "../ast.js";
6
+ import { logger as rootLogger, logWarnError } from "../logger.js";
7
7
  import { readStrictFqn } from "../utils/elementRef.js";
8
+ const logger = rootLogger.getChild("DocumentSymbolProvider");
8
9
  export class LikeC4DocumentSymbolProvider {
9
10
  constructor(services) {
10
11
  this.services = services;
@@ -17,11 +18,20 @@ export class LikeC4DocumentSymbolProvider {
17
18
  nameProvider;
18
19
  parser;
19
20
  locator;
20
- getSymbols({
21
- parseResult: {
22
- value: { specifications, models, deployments, views, likec4lib }
21
+ async getSymbols(doc, _params, cancelToken) {
22
+ if (!isLikeC4LangiumDocument(doc)) {
23
+ return [];
23
24
  }
24
- }) {
25
+ if (doc.state <= DocumentState.Linked) {
26
+ logger.debug(`Waiting for document ${doc.uri.path} to be Linked`);
27
+ await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Linked, doc.uri, cancelToken);
28
+ logger.debug(`document is Linked`);
29
+ }
30
+ const {
31
+ parseResult: {
32
+ value: { specifications, models, deployments, views, likec4lib }
33
+ }
34
+ } = doc;
25
35
  return [
26
36
  ...likec4lib.map((l) => () => this.getLikec4LibSymbol(l)),
27
37
  ...specifications.map((s) => () => this.getSpecSymbol(s)),
@@ -8,13 +8,17 @@ import {
8
8
  Disposable,
9
9
  DocumentState
10
10
  } from "langium";
11
+ import prettyMs from "pretty-ms";
11
12
  import {
12
13
  filter,
13
14
  groupBy,
15
+ isNot,
14
16
  mapToObj,
15
17
  pipe,
18
+ prop,
16
19
  values
17
20
  } from "remeda";
21
+ import { isLikeC4Builtin } from "../likec4lib.js";
18
22
  import { logger as mainLogger, logWarnError } from "../logger.js";
19
23
  import { ADisposable } from "../utils/index.js";
20
24
  import { assignNavigateTo } from "../view-utils/index.js";
@@ -45,7 +49,10 @@ export class LikeC4ModelBuilder extends ADisposable {
45
49
  this.DocumentBuilder.onBuildPhase(
46
50
  DocumentState.Validated,
47
51
  (docs, _cancelToken) => {
48
- this.notifyListeners(docs.map((d) => d.uri));
52
+ const validated = docs.map(prop("uri")).filter(isNot(isLikeC4Builtin));
53
+ if (validated.length > 0) {
54
+ this.notifyListeners(validated);
55
+ }
49
56
  }
50
57
  )
51
58
  );
@@ -64,7 +71,6 @@ export class LikeC4ModelBuilder extends ADisposable {
64
71
  }
65
72
  const cache = this.cache;
66
73
  return cache.get(CACHE_KEY_PARSED_MODEL, () => {
67
- logger.debug("unsafeSyncParseModel ({docslength} docs)", { docslength: docs.length });
68
74
  return buildModel(docs);
69
75
  });
70
76
  }
@@ -72,13 +78,18 @@ export class LikeC4ModelBuilder extends ADisposable {
72
78
  const cache = this.cache;
73
79
  const cached = cache.get(CACHE_KEY_PARSED_MODEL);
74
80
  if (cached) {
75
- return await Promise.resolve(cached);
81
+ logger.debug("parseModel from cache");
82
+ return cached;
76
83
  }
77
84
  if (this.LangiumDocuments.all.some((doc) => doc.state < DocumentState.Validated)) {
78
85
  logger.debug("parseModel: waiting for documents to be validated");
79
86
  await this.DocumentBuilder.waitUntil(DocumentState.Validated, cancelToken);
87
+ logger.debug("parseModel: documents are validated");
80
88
  }
81
- return this.unsafeSyncParseModel();
89
+ const t0 = performance.now();
90
+ const result = this.unsafeSyncParseModel();
91
+ logger.debug(`parseModel in ${prettyMs(performance.now() - t0)}`);
92
+ return result;
82
93
  }
83
94
  previousViews = {};
84
95
  /**
@@ -126,13 +137,18 @@ export class LikeC4ModelBuilder extends ADisposable {
126
137
  const cache = this.cache;
127
138
  const cached = cache.get(CACHE_KEY_COMPUTED_MODEL);
128
139
  if (cached) {
129
- return await Promise.resolve(cached);
140
+ logger.debug("buildLikeC4Model from cache");
141
+ return cached;
130
142
  }
143
+ const t0 = performance.now();
131
144
  const model = await this.parseModel(cancelToken);
132
145
  if (!model) {
146
+ logger.debug("buildLikeC4Model: no model");
133
147
  return LikeC4Model.EMPTY;
134
148
  }
135
- return this.unsafeSyncBuildModel();
149
+ const result = this.unsafeSyncBuildModel();
150
+ logger.debug(`buildLikeC4Model in ${prettyMs(performance.now() - t0)}`);
151
+ return result;
136
152
  }
137
153
  async computeView(viewId, cancelToken) {
138
154
  const cache = this.cache;
@@ -147,9 +163,10 @@ export class LikeC4ModelBuilder extends ADisposable {
147
163
  return cache.get(cacheKey, () => {
148
164
  const view = parsed.views[viewId];
149
165
  if (!view) {
150
- logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
166
+ logger.warn`computeView: cant find view ${viewId}`;
151
167
  return null;
152
168
  }
169
+ logger.debug`computeView: ${viewId}`;
153
170
  const computeView = LikeC4Model.makeCompute(parsed);
154
171
  const result = computeView(view);
155
172
  if (!result.isSuccess) {
@@ -1,13 +1,13 @@
1
1
  import { Location, Range, TextEdit } from 'vscode-languageserver-types';
2
2
  import { type ParsedLikeC4LangiumDocument } from '../ast';
3
3
  import type { LikeC4Services } from '../module';
4
- import type { ChangeViewRequestParams } from '../protocol';
4
+ import type { ChangeView } from '../protocol';
5
5
  export declare class LikeC4ModelChanges {
6
6
  private services;
7
7
  private locator;
8
8
  constructor(services: LikeC4Services);
9
- applyChange(changeView: ChangeViewRequestParams): Promise<Location | null>;
10
- protected convertToTextEdit({ viewId, change }: ChangeViewRequestParams): {
9
+ applyChange(changeView: ChangeView.Params): Promise<Location | null>;
10
+ protected convertToTextEdit({ viewId, change }: ChangeView.Params): {
11
11
  doc: ParsedLikeC4LangiumDocument;
12
12
  modifiedRange: Range;
13
13
  edits: TextEdit[];
@@ -1,79 +1,106 @@
1
- import type { ComputedLikeC4Model, ComputedView, DiagramView, Fqn, ParsedLikeC4Model, RelationId, ViewChange, ViewId } from '@likec4/core';
1
+ import type { ComputedLikeC4Model, ComputedView, DiagramView, Fqn, LayoutedLikeC4Model, RelationId, ViewChange, ViewId } from '@likec4/core';
2
2
  import { NotificationType, RequestType, RequestType0 } from 'vscode-jsonrpc';
3
3
  import type { DiagnosticSeverity, DocumentUri, Location, Position } from 'vscode-languageserver-types';
4
4
  export declare const onDidChangeModel: NotificationType<string>;
5
5
  export type OnDidChangeModelNotification = typeof onDidChangeModel;
6
- export declare const fetchModel: RequestType0<{
7
- model: ParsedLikeC4Model | null;
8
- }, void>;
9
- export type FetchModelRequest = typeof fetchModel;
10
- export declare const fetchComputedModel: RequestType<{
11
- cleanCaches?: boolean | undefined;
12
- }, {
13
- model: ComputedLikeC4Model | null;
14
- }, void>;
15
- export type FetchComputedModelRequest = typeof fetchComputedModel;
16
- export declare const computeView: RequestType<{
17
- viewId: ViewId;
18
- }, {
19
- view: ComputedView | null;
20
- }, void>;
21
- export type ComputeViewRequest = typeof computeView;
6
+ export declare namespace FetchComputedModel {
7
+ type Params = {
8
+ cleanCaches?: boolean | undefined;
9
+ };
10
+ type Res = {
11
+ model: ComputedLikeC4Model | null;
12
+ };
13
+ const Req: RequestType<Params, Res, void>;
14
+ type Req = typeof Req;
15
+ }
16
+ export declare namespace ComputeView {
17
+ type Params = {
18
+ viewId: ViewId;
19
+ };
20
+ type Result = {
21
+ view: ComputedView | null;
22
+ };
23
+ const Req: RequestType<Params, Result, void>;
24
+ type Req = typeof Req;
25
+ }
26
+ export declare namespace FetchLayoutedModel {
27
+ type Res = {
28
+ model: LayoutedLikeC4Model | null;
29
+ };
30
+ const Req: RequestType0<Res, void>;
31
+ type Req = typeof Req;
32
+ }
22
33
  /**
23
34
  * Request to layout a view.
24
35
  */
25
- export declare const layoutView: RequestType<{
26
- viewId: ViewId;
27
- }, {
28
- result: {
29
- dot: string;
30
- diagram: DiagramView;
31
- } | null;
32
- }, void>;
33
- export type LayoutViewRequest = typeof layoutView;
36
+ export declare namespace LayoutView {
37
+ type Params = {
38
+ viewId: ViewId;
39
+ };
40
+ type Res = {
41
+ result: {
42
+ dot: string;
43
+ diagram: DiagramView;
44
+ } | null;
45
+ };
46
+ const Req: RequestType<Params, Res, void>;
47
+ type Req = typeof Req;
48
+ }
34
49
  /**
35
50
  * Request to layout all existing views.
36
51
  */
37
- export declare const validateLayout: RequestType<{}, {
38
- result: {
39
- uri: string;
40
- viewId: ViewId;
41
- message: string;
42
- severity: DiagnosticSeverity;
43
- range: {
44
- start: Position;
45
- end: Position;
46
- };
47
- }[] | null;
48
- }, void>;
49
- export type ValidateLayoutRequest = typeof validateLayout;
52
+ export declare namespace ValidateLayout {
53
+ type Params = never;
54
+ type Res = {
55
+ result: {
56
+ uri: string;
57
+ viewId: ViewId;
58
+ message: string;
59
+ severity: DiagnosticSeverity;
60
+ range: {
61
+ start: Position;
62
+ end: Position;
63
+ };
64
+ }[] | null;
65
+ };
66
+ const Req: RequestType0<Res, void>;
67
+ type Req = typeof Req;
68
+ }
50
69
  /**
51
70
  * Request to build documents.
52
71
  */
53
- export interface BuildDocumentsParams {
54
- docs: DocumentUri[];
72
+ export declare namespace BuildDocuments {
73
+ type Params = {
74
+ docs: DocumentUri[];
75
+ };
76
+ const Req: RequestType<Params, void, void>;
77
+ type Req = typeof Req;
55
78
  }
56
- export declare const buildDocuments: RequestType<BuildDocumentsParams, void, void>;
57
- export type BuildDocumentsRequest = typeof buildDocuments;
58
79
  /**
59
80
  * Request to locate an element, relation, deployment or view.
60
81
  */
61
- export type LocateParams = {
62
- element: Fqn;
63
- property?: string;
64
- } | {
65
- relation: RelationId;
66
- } | {
67
- deployment: Fqn;
68
- property?: string;
69
- } | {
70
- view: ViewId;
71
- };
72
- export declare const locate: RequestType<LocateParams, Location | null, void>;
73
- export type LocateRequest = typeof locate;
74
- export interface ChangeViewRequestParams {
75
- viewId: ViewId;
76
- change: ViewChange;
82
+ export declare namespace Locate {
83
+ type Params = {
84
+ element: Fqn;
85
+ property?: string;
86
+ } | {
87
+ relation: RelationId;
88
+ } | {
89
+ deployment: Fqn;
90
+ property?: string;
91
+ } | {
92
+ view: ViewId;
93
+ };
94
+ type Res = Location | null;
95
+ const Req: RequestType<Params, Res, void>;
96
+ type Req = typeof Req;
97
+ }
98
+ export declare namespace ChangeView {
99
+ type Params = {
100
+ viewId: ViewId;
101
+ change: ViewChange;
102
+ };
103
+ type Res = Location | null;
104
+ const Req: RequestType<Params, Res, void>;
105
+ type Req = typeof Req;
77
106
  }
78
- export declare const changeView: RequestType<ChangeViewRequestParams, Location | null, void>;
79
- export type ChangeViewRequest = typeof changeView;
package/dist/protocol.js CHANGED
@@ -1,16 +1,34 @@
1
1
  import { NotificationType, RequestType, RequestType0 } from "vscode-jsonrpc";
2
2
  export const onDidChangeModel = new NotificationType("likec4/onDidChangeModel");
3
- export const fetchModel = new RequestType0(
4
- "likec4/fetchModel"
5
- );
6
- export const fetchComputedModel = new RequestType(
7
- "likec4/fetchComputedModel"
8
- );
9
- export const computeView = new RequestType(
10
- "likec4/computeView"
11
- );
12
- export const layoutView = new RequestType("likec4/layout-view");
13
- export const validateLayout = new RequestType("likec4/validate-layout");
14
- export const buildDocuments = new RequestType("likec4/build");
15
- export const locate = new RequestType("likec4/locate");
16
- export const changeView = new RequestType("likec4/change-view");
3
+ export var FetchComputedModel;
4
+ ((FetchComputedModel2) => {
5
+ FetchComputedModel2.Req = new RequestType("likec4/fetchComputedModel");
6
+ })(FetchComputedModel || (FetchComputedModel = {}));
7
+ export var ComputeView;
8
+ ((ComputeView2) => {
9
+ ComputeView2.Req = new RequestType("likec4/computeView");
10
+ })(ComputeView || (ComputeView = {}));
11
+ export var FetchLayoutedModel;
12
+ ((FetchLayoutedModel2) => {
13
+ FetchLayoutedModel2.Req = new RequestType0("likec4/fetchLayoutedModel");
14
+ })(FetchLayoutedModel || (FetchLayoutedModel = {}));
15
+ export var LayoutView;
16
+ ((LayoutView2) => {
17
+ LayoutView2.Req = new RequestType("likec4/layout-view");
18
+ })(LayoutView || (LayoutView = {}));
19
+ export var ValidateLayout;
20
+ ((ValidateLayout2) => {
21
+ ValidateLayout2.Req = new RequestType0("likec4/validate-layout");
22
+ })(ValidateLayout || (ValidateLayout = {}));
23
+ export var BuildDocuments;
24
+ ((BuildDocuments2) => {
25
+ BuildDocuments2.Req = new RequestType("likec4/build");
26
+ })(BuildDocuments || (BuildDocuments = {}));
27
+ export var Locate;
28
+ ((Locate2) => {
29
+ Locate2.Req = new RequestType("likec4/locate");
30
+ })(Locate || (Locate = {}));
31
+ export var ChangeView;
32
+ ((ChangeView2) => {
33
+ ChangeView2.Req = new RequestType("likec4/change-view");
34
+ })(ChangeView || (ChangeView = {}));
@@ -1,5 +1,8 @@
1
+ import { loggable } from "@likec4/log";
2
+ import prettyMs from "pretty-ms";
1
3
  import { values } from "remeda";
2
- import { logError, logWarnError } from "../logger.js";
4
+ import { logError, logger as rootLogger, logWarnError } from "../logger.js";
5
+ const logger = rootLogger.getChild("Views");
3
6
  export class LikeC4Views {
4
7
  constructor(services) {
5
8
  this.services = services;
@@ -20,6 +23,7 @@ export class LikeC4Views {
20
23
  if (views.length === 0) {
21
24
  return [];
22
25
  }
26
+ logger.debug`layoutAll: ${views.length} views`;
23
27
  const results = [];
24
28
  const tasks = [];
25
29
  for (const view of views) {
@@ -41,26 +45,35 @@ export class LikeC4Views {
41
45
  results.push(task.value);
42
46
  }
43
47
  }
48
+ if (results.length !== views.length) {
49
+ logger.warn`layouted ${results.length} of ${views.length} views`;
50
+ } else if (results.length > 0) {
51
+ logger.debug`layouted all ${results.length} views`;
52
+ }
44
53
  return results;
45
54
  }
46
55
  async layoutView(viewId, cancelToken) {
47
56
  const model = await this.ModelBuilder.buildLikeC4Model(cancelToken);
48
57
  const view = model.findView(viewId)?.$view;
49
58
  if (!view) {
59
+ logger.warn`layoutView ${viewId} not found`;
50
60
  return null;
51
61
  }
52
62
  let cached = this.cache.get(view);
53
63
  if (cached) {
64
+ logger.debug`layout ${viewId} from cache`;
54
65
  return await Promise.resolve(cached);
55
66
  }
56
67
  try {
68
+ const start = performance.now();
57
69
  const result = await this.layouter.layout(view);
58
70
  this.viewsWithReportedErrors.delete(viewId);
59
71
  this.cache.set(view, result);
72
+ logger.debug(`layout {viewId} ready in ${prettyMs(performance.now() - start)}`, { viewId });
60
73
  return result;
61
74
  } catch (e) {
62
75
  if (!this.viewsWithReportedErrors.has(viewId)) {
63
- const errMessage = e instanceof Error ? e.message : "" + e;
76
+ const errMessage = loggable(e);
64
77
  this.services.shared.lsp.Connection?.window.showErrorMessage(`LikeC4: ${errMessage}`);
65
78
  this.viewsWithReportedErrors.add(viewId);
66
79
  logError(e);
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.24.1",
4
+ "version": "1.25.1",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -80,15 +80,13 @@
80
80
  "registry": "https://registry.npmjs.org",
81
81
  "access": "public"
82
82
  },
83
- "dependencies": {
84
- "@hpcc-js/wasm-graphviz": "1.7.0"
85
- },
86
83
  "devDependencies": {
84
+ "@hpcc-js/wasm-graphviz": "1.7.0",
87
85
  "@msgpack/msgpack": "^3.1.0",
88
86
  "@smithy/util-base64": "^4.0.0",
89
- "@types/node": "^20.17.17",
87
+ "@types/node": "^20.17.23",
90
88
  "@types/which": "^3.0.4",
91
- "@vitest/coverage-v8": "^3.0.6",
89
+ "@vitest/coverage-v8": "^3.0.7",
92
90
  "esm-env": "^1.2.2",
93
91
  "fast-equals": "^5.2.2",
94
92
  "fdir": "^6.4.3",
@@ -98,25 +96,26 @@
98
96
  "langium-cli": "3.3.0",
99
97
  "natural-compare-lite": "^1.4.0",
100
98
  "p-debounce": "^4.0.0",
101
- "remeda": "^2.20.2",
99
+ "pretty-ms": "^9.2.0",
100
+ "remeda": "^2.21.0",
102
101
  "strip-indent": "^4.0.0",
103
102
  "tsx": "~4.19.3",
104
- "turbo": "^2.4.2",
103
+ "turbo": "^2.4.4",
105
104
  "type-fest": "4.34.1",
106
- "typescript": "5.7.3",
105
+ "typescript": "5.8.2",
107
106
  "ufo": "^1.5.4",
108
107
  "unbuild": "^3.3.1",
109
- "vitest": "^3.0.6",
108
+ "vitest": "^3.0.7",
110
109
  "vscode-jsonrpc": "8.2.0",
111
110
  "vscode-languageserver": "9.0.1",
112
111
  "vscode-languageserver-types": "3.17.5",
113
112
  "vscode-uri": "3.1.0",
114
113
  "which": "^5.0.0",
115
- "@likec4/core": "1.24.1",
116
- "@likec4/icons": "1.24.1",
117
- "@likec4/layouts": "1.24.1",
118
- "@likec4/log": "1.24.1",
119
- "@likec4/tsconfig": "1.24.1"
114
+ "@likec4/core": "1.25.1",
115
+ "@likec4/icons": "1.25.1",
116
+ "@likec4/layouts": "1.25.1",
117
+ "@likec4/log": "1.25.1",
118
+ "@likec4/tsconfig": "1.25.1"
120
119
  },
121
120
  "scripts": {
122
121
  "typecheck": "tsc --noEmit",