@likec4/language-server 1.30.0 → 1.31.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.
@@ -1,9 +1,25 @@
1
- import { type AstNode } from 'langium';
2
- import { AbstractFormatter } from 'langium/lsp';
1
+ import { type AstNode, type LangiumDocument } from 'langium';
2
+ import { AbstractFormatter, FormattingRegion } from 'langium/lsp';
3
+ import type { FormattingOptions as LSFormattingOptions, Range, TextEdit } from 'vscode-languageserver-types';
4
+ import type { LikeC4Services } from '../module';
5
+ type QuoteStyle = 'single' | 'double' | 'ignore' | 'auto';
6
+ interface LikeC4FormatterOptions {
7
+ quoteStyle: QuoteStyle;
8
+ }
9
+ type ExtendedFormattingCommandType = 'normalizeQuotes';
10
+ interface ExtendedFormattingCommand {
11
+ type: ExtendedFormattingCommandType;
12
+ region: FormattingRegion;
13
+ }
3
14
  export declare class LikeC4Formatter extends AbstractFormatter {
15
+ protected options: LikeC4FormatterOptions;
16
+ extendedFormattingCommands: ExtendedFormattingCommand[];
17
+ constructor(services: LikeC4Services);
18
+ protected doDocumentFormat(document: LangiumDocument, options: LSFormattingOptions, range?: Range): TextEdit[];
4
19
  protected format(node: AstNode): void;
5
20
  protected formatTags(node: AstNode): void;
6
21
  protected formatDeploymentRelation(node: AstNode): void;
22
+ protected formatExtendDeployment(node: AstNode): void;
7
23
  protected formatRelation(node: AstNode): void;
8
24
  protected removeIndentFromTopLevelStatements(node: AstNode): void;
9
25
  protected indentContentInBraces(node: AstNode): void;
@@ -15,7 +31,9 @@ export declare class LikeC4Formatter extends AbstractFormatter {
15
31
  protected formatAutolayoutProperty(node: AstNode): void;
16
32
  protected formatMetadataProperty(node: AstNode): void;
17
33
  protected formatElementDeclaration(node: AstNode): void;
34
+ protected formatExtendElement(node: AstNode): void;
18
35
  protected formatGlobals(node: AstNode): void;
36
+ protected formatImports(node: AstNode): void;
19
37
  protected formatSpecificationRule(node: AstNode): void;
20
38
  protected formatWithPredicate(node: AstNode): void;
21
39
  protected formatDeploymentNodeDeclaration(node: AstNode): void;
@@ -31,4 +49,10 @@ export declare class LikeC4Formatter extends AbstractFormatter {
31
49
  protected formatRelationExpression(node: AstNode): void;
32
50
  private findPredicateExpressionRoot;
33
51
  private on;
52
+ private doExtendedFormatting;
53
+ protected normalizeQuotes(node: AstNode): void;
54
+ private quotesNormalizerFactory;
55
+ private getAutoQuoteStyle;
56
+ private onConfigurationUpdate;
34
57
  }
58
+ export {};
@@ -1,3 +1,4 @@
1
+ import { nonexhaustive } from "@likec4/core";
1
2
  import { GrammarUtils } from "langium";
2
3
  import { AbstractFormatter, Formatting } from "langium/lsp";
3
4
  import { filter, isTruthy } from "remeda";
@@ -11,17 +12,37 @@ const FormattingOptions = {
11
12
  noIndent: Formatting.noIndent()
12
13
  };
13
14
  export class LikeC4Formatter extends AbstractFormatter {
15
+ options = {
16
+ quoteStyle: "auto"
17
+ };
18
+ extendedFormattingCommands = [];
19
+ constructor(services) {
20
+ super();
21
+ services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate(
22
+ (update) => this.onConfigurationUpdate(update.configuration.formatting)
23
+ );
24
+ }
25
+ doDocumentFormat(document, options, range) {
26
+ this.extendedFormattingCommands = [];
27
+ const edits = super.doDocumentFormat(document, options, range);
28
+ this.doExtendedFormatting(edits);
29
+ return edits;
30
+ }
14
31
  format(node) {
15
32
  this.removeIndentFromTopLevelStatements(node);
16
33
  this.indentContentInBraces(node);
34
+ this.normalizeQuotes(node);
35
+ this.formatImports(node);
17
36
  this.formatSpecificationRule(node);
18
37
  this.formatGlobals(node);
19
38
  this.formatElementDeclaration(node);
39
+ this.formatExtendElement(node);
20
40
  this.formatRelation(node);
21
41
  this.formatMetadataProperty(node);
22
42
  this.formatDeploymentNodeDeclaration(node);
23
43
  this.formatDeployedInstance(node);
24
44
  this.formatDeploymentRelation(node);
45
+ this.formatExtendDeployment(node);
25
46
  this.formatView(node);
26
47
  this.formatViewRuleGroup(node);
27
48
  this.formatViewRuleGlobalStyle(node);
@@ -58,6 +79,11 @@ export class LikeC4Formatter extends AbstractFormatter {
58
79
  f.properties("title", "technology").prepend(FormattingOptions.oneSpace);
59
80
  });
60
81
  }
82
+ formatExtendDeployment(node) {
83
+ this.on(node, ast.isExtendDeployment, (n, f) => {
84
+ f.keywords("extend").append(FormattingOptions.oneSpace);
85
+ });
86
+ }
61
87
  formatRelation(node) {
62
88
  this.on(
63
89
  node,
@@ -90,7 +116,7 @@ export class LikeC4Formatter extends AbstractFormatter {
90
116
  }
91
117
  }
92
118
  indentContentInBraces(node) {
93
- if (ast.isLikeC4Lib(node) || ast.isSpecificationRule(node) || ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node) || ast.isSpecificationDeploymentNodeKind(node) || ast.isGlobals(node) || ast.isGlobalStyle(node) || ast.isGlobalStyleGroup(node) || ast.isGlobalPredicateGroup(node) || ast.isGlobalDynamicPredicateGroup(node) || ast.isGlobalStyleGroup(node) || ast.isModel(node) || ast.isElementBody(node) || ast.isExtendElementBody(node) || ast.isRelationBody(node) || ast.isRelationStyleProperty(node) || ast.isMetadataBody(node) || ast.isModelViews(node) || ast.isElementViewBody(node) || ast.isDynamicViewBody(node) || ast.isDeploymentViewBody(node) || ast.isViewRuleStyle(node) || ast.isViewRuleGroup(node) || ast.isCustomElementProperties(node) || ast.isCustomRelationProperties(node) || ast.isElementStyleProperty(node) || ast.isDynamicViewParallelSteps(node) || ast.isModelDeployments(node) || ast.isDeploymentNodeBody(node) || ast.isDeploymentRelationBody(node) || ast.isDeployedInstanceBody(node)) {
119
+ if (ast.isLikeC4Lib(node) || ast.isSpecificationRule(node) || ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node) || ast.isSpecificationDeploymentNodeKind(node) || ast.isGlobals(node) || ast.isGlobalStyle(node) || ast.isGlobalStyleGroup(node) || ast.isGlobalPredicateGroup(node) || ast.isGlobalDynamicPredicateGroup(node) || ast.isGlobalStyleGroup(node) || ast.isModel(node) || ast.isElementBody(node) || ast.isExtendElementBody(node) || ast.isRelationBody(node) || ast.isRelationStyleProperty(node) || ast.isMetadataBody(node) || ast.isModelViews(node) || ast.isElementViewBody(node) || ast.isDynamicViewBody(node) || ast.isDeploymentViewBody(node) || ast.isViewRuleStyle(node) || ast.isViewRuleGroup(node) || ast.isCustomElementProperties(node) || ast.isCustomRelationProperties(node) || ast.isElementStyleProperty(node) || ast.isDynamicViewParallelSteps(node) || ast.isModelDeployments(node) || ast.isDeploymentNodeBody(node) || ast.isDeploymentRelationBody(node) || ast.isDeployedInstanceBody(node) || ast.isExtendDeploymentBody(node)) {
94
120
  const formatter = this.getNodeFormatter(node);
95
121
  const openBrace = formatter.keywords("{");
96
122
  const closeBrace = formatter.keywords("}");
@@ -120,7 +146,7 @@ export class LikeC4Formatter extends AbstractFormatter {
120
146
  this.on(node, ast.isDeploymentView)?.keywords("deployment", "view").append(FormattingOptions.oneSpace);
121
147
  }
122
148
  formatLeafProperty(node) {
123
- if (ast.isElementStringProperty(node) || ast.isRelationStringProperty(node) || ast.isViewStringProperty(node) || ast.isNotationProperty(node) || ast.isSpecificationElementStringProperty(node) || ast.isSpecificationRelationshipStringProperty(node) || ast.isColorProperty(node) || ast.isLineProperty(node) || ast.isArrowProperty(node) || ast.isIconProperty(node) || ast.isShapeProperty(node) || ast.isBorderProperty(node) || ast.isOpacityProperty(node) || ast.isMultipleProperty(node) || ast.isShapeSizeProperty(node) || ast.isPaddingSizeProperty(node) || ast.isTextSizeProperty(node)) {
149
+ if (ast.isElementStringProperty(node) || ast.isRelationStringProperty(node) || ast.isViewStringProperty(node) || ast.isNotationProperty(node) || ast.isNotesProperty(node) || ast.isSpecificationElementStringProperty(node) || ast.isSpecificationRelationshipStringProperty(node) || ast.isColorProperty(node) || ast.isLineProperty(node) || ast.isArrowProperty(node) || ast.isIconProperty(node) || ast.isShapeProperty(node) || ast.isBorderProperty(node) || ast.isOpacityProperty(node) || ast.isMultipleProperty(node) || ast.isShapeSizeProperty(node) || ast.isPaddingSizeProperty(node) || ast.isTextSizeProperty(node)) {
124
150
  const formatter = this.getNodeFormatter(node);
125
151
  const colon = formatter.keyword(":");
126
152
  const propertyName = formatter.keywords(
@@ -128,6 +154,7 @@ export class LikeC4Formatter extends AbstractFormatter {
128
154
  "description",
129
155
  "technology",
130
156
  "notation",
157
+ "notes",
131
158
  "color",
132
159
  "line",
133
160
  "head",
@@ -189,6 +216,11 @@ export class LikeC4Formatter extends AbstractFormatter {
189
216
  f.properties("props").prepend(FormattingOptions.oneSpace);
190
217
  });
191
218
  }
219
+ formatExtendElement(node) {
220
+ this.on(node, ast.isExtendElement, (n, f) => {
221
+ f.keywords("extend").append(FormattingOptions.oneSpace);
222
+ });
223
+ }
192
224
  formatGlobals(node) {
193
225
  this.on(node, ast.isGlobalStyle, (n, f) => {
194
226
  f.keyword("style").append(FormattingOptions.oneSpace);
@@ -204,6 +236,15 @@ export class LikeC4Formatter extends AbstractFormatter {
204
236
  f.keyword("dynamicPredicateGroup").append(FormattingOptions.oneSpace);
205
237
  });
206
238
  }
239
+ formatImports(node) {
240
+ this.on(node, ast.isImportsFromPoject, (n, f) => {
241
+ f.keyword("import").append(FormattingOptions.oneSpace);
242
+ f.keywords("{", "}", "from").surround(FormattingOptions.oneSpace);
243
+ });
244
+ this.on(node, ast.isImported, (n, f) => {
245
+ f.keywords(",").prepend(FormattingOptions.noSpace).append(FormattingOptions.oneSpace);
246
+ });
247
+ }
207
248
  formatSpecificationRule(node) {
208
249
  if (ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node) || ast.isSpecificationTag(node) || ast.isSpecificationDeploymentNodeKind(node)) {
209
250
  const formatter = this.getNodeFormatter(node);
@@ -349,4 +390,60 @@ export class LikeC4Formatter extends AbstractFormatter {
349
390
  format && formatter && format(node, formatter);
350
391
  return formatter;
351
392
  }
393
+ doExtendedFormatting(edits) {
394
+ const quotesNormalizer = this.quotesNormalizerFactory(this.extendedFormattingCommands);
395
+ for (let command of this.extendedFormattingCommands) {
396
+ switch (command.type) {
397
+ case "normalizeQuotes":
398
+ quotesNormalizer(command, edits);
399
+ break;
400
+ default:
401
+ nonexhaustive(command.type);
402
+ }
403
+ }
404
+ }
405
+ normalizeQuotes(node) {
406
+ if (this.options.quoteStyle === "ignore") {
407
+ return;
408
+ }
409
+ let region = null;
410
+ region = region ?? this.on(node, ast.isStringProperty)?.property("value");
411
+ region = region ?? this.on(node, ast.isElement)?.properties("props");
412
+ region = region ?? this.on(node, ast.isImportsFromPoject)?.properties("project");
413
+ region = region ?? this.on(node, ast.isRelation)?.properties("title", "technology");
414
+ region = region ?? this.on(node, ast.isViewRuleGroup)?.properties("title");
415
+ region = region ?? this.on(node, ast.isDynamicViewStep)?.properties("title");
416
+ region = region ?? this.on(node, ast.isDeploymentNode)?.properties("title");
417
+ region = region ?? this.on(node, ast.isDeployedInstance)?.properties("title");
418
+ region = region ?? this.on(node, ast.isDeploymentRelation)?.properties("title", "technology");
419
+ region = region ?? this.on(node, ast.isLinkProperty)?.properties("title");
420
+ if (region) {
421
+ this.extendedFormattingCommands.push({ type: "normalizeQuotes", region });
422
+ }
423
+ }
424
+ quotesNormalizerFactory(commands) {
425
+ const quoteStyle = this.options.quoteStyle != "auto" ? this.options.quoteStyle : this.getAutoQuoteStyle(commands);
426
+ return (command, edits) => {
427
+ const quotesToReplace = quoteStyle === "single" ? '"' : "'";
428
+ const quotesToInsert = quoteStyle === "single" ? "'" : '"';
429
+ const newEdits = command.region.nodes.map((node) => ({
430
+ range: node.range,
431
+ newText: node.text.replaceAll(quotesToReplace, quotesToInsert)
432
+ }));
433
+ edits.push(...newEdits);
434
+ };
435
+ }
436
+ getAutoQuoteStyle(commands) {
437
+ const nodes = commands.flatMap((x) => x.region.nodes);
438
+ const doubleQuotesCount = nodes.filter((x) => x.text[0] == '"').length;
439
+ return doubleQuotesCount * 2 >= nodes.length ? "double" : "single";
440
+ }
441
+ onConfigurationUpdate(options) {
442
+ this.options = {
443
+ ...this.options,
444
+ ...options ?? {
445
+ quoteStyle: "auto"
446
+ }
447
+ };
448
+ }
352
449
  }