@likec4/language-server 1.9.0 → 1.10.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.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/model-graph/index.cjs +1 -1
- package/dist/model-graph/index.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/shared/{language-server.86lmJ8ZN.d.cts → language-server.CjFzaJwI.d.cts} +42 -13
- package/dist/shared/{language-server.RjhrBZS0.d.ts → language-server.CtKHXJDD.d.ts} +42 -13
- package/dist/shared/{language-server.CFTY6j4e.d.mts → language-server.D-84I33F.d.mts} +42 -13
- package/dist/shared/{language-server.Q-wtPShM.mjs → language-server.DBJJUUgF.mjs} +485 -108
- package/dist/shared/{language-server.CCB4ESN5.mjs → language-server.DtBRb9os.mjs} +166 -116
- package/dist/shared/{language-server.D0bOlrCi.cjs → language-server.DwyCJvXm.cjs} +164 -114
- package/dist/shared/{language-server.B1TZgyoH.cjs → language-server.JWkqVjGv.cjs} +481 -104
- package/package.json +6 -5
- package/src/ast.ts +8 -6
- package/src/formatting/LikeC4Formatter.ts +388 -0
- package/src/formatting/utils.ts +26 -0
- package/src/generated/ast.ts +104 -10
- package/src/generated/grammar.ts +1 -1
- package/src/like-c4.langium +34 -7
- package/src/lsp/DocumentLinkProvider.ts +27 -15
- package/src/lsp/SemanticTokenProvider.ts +1 -1
- package/src/lsp/index.ts +1 -1
- package/src/model/fqn-index.ts +0 -1
- package/src/model/model-builder.ts +43 -32
- package/src/model/model-parser.ts +43 -21
- package/src/model-graph/compute-view/compute.ts +104 -78
- package/src/model-graph/compute-view/predicates.ts +3 -5
- package/src/model-graph/dynamic-view/compute.ts +96 -60
- package/src/model-graph/utils/buildElementNotations.ts +1 -1
- package/src/module.ts +6 -9
- package/src/test/testServices.ts +27 -7
- package/src/validation/index.ts +2 -1
- package/src/validation/property-checks.ts +13 -1
- package/src/validation/specification.ts +3 -3
- package/src/view-utils/resolve-relative-paths.ts +14 -17
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.
|
|
4
|
+
"version": "1.10.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -102,13 +102,14 @@
|
|
|
102
102
|
"lint": "run -T eslint src/ --fix",
|
|
103
103
|
"clean": "run -T rimraf dist contrib",
|
|
104
104
|
"test": "vitest run --no-isolate",
|
|
105
|
+
"test-dbg": "vitest run --no-isolate -t formating",
|
|
105
106
|
"vitest:ui": "vitest --no-isolate --ui",
|
|
106
107
|
"test:watch": "vitest"
|
|
107
108
|
},
|
|
108
109
|
"dependencies": {
|
|
109
110
|
"@dagrejs/graphlib": "^2.2.4",
|
|
110
|
-
"@likec4/core": "1.
|
|
111
|
-
"@likec4/log": "1.
|
|
111
|
+
"@likec4/core": "1.10.0",
|
|
112
|
+
"@likec4/log": "1.10.0",
|
|
112
113
|
"@msgpack/msgpack": "^3.0.0-beta2",
|
|
113
114
|
"@smithy/util-base64": "^3.0.0",
|
|
114
115
|
"fast-equals": "^5.0.1",
|
|
@@ -128,8 +129,8 @@
|
|
|
128
129
|
"vscode-uri": "3.0.8"
|
|
129
130
|
},
|
|
130
131
|
"devDependencies": {
|
|
131
|
-
"@likec4/icons": "1.
|
|
132
|
-
"@likec4/tsconfig": "1.
|
|
132
|
+
"@likec4/icons": "1.10.0",
|
|
133
|
+
"@likec4/tsconfig": "1.10.0",
|
|
133
134
|
"@types/node": "^20.16.1",
|
|
134
135
|
"@types/object-hash": "^3.0.6",
|
|
135
136
|
"@types/string-hash": "^1.1.3",
|
package/src/ast.ts
CHANGED
|
@@ -89,6 +89,7 @@ export interface ParsedAstRelation {
|
|
|
89
89
|
head?: c4.RelationshipArrowType
|
|
90
90
|
tail?: c4.RelationshipArrowType
|
|
91
91
|
links?: c4.NonEmptyArray<ParsedLink>
|
|
92
|
+
navigateTo?: c4.ViewID
|
|
92
93
|
metadata?: { [key: string]: string }
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -114,7 +115,7 @@ export interface ParsedAstDynamicView {
|
|
|
114
115
|
description: string | null
|
|
115
116
|
tags: c4.NonEmptyArray<c4.Tag> | null
|
|
116
117
|
links: c4.NonEmptyArray<ParsedLink> | null
|
|
117
|
-
steps: c4.
|
|
118
|
+
steps: c4.DynamicViewStepOrParallel[]
|
|
118
119
|
rules: Array<c4.DynamicViewRule>
|
|
119
120
|
manualLayout?: c4.ViewManualLayout
|
|
120
121
|
}
|
|
@@ -194,14 +195,14 @@ export function cleanParsedModel(doc: LikeC4LangiumDocument) {
|
|
|
194
195
|
return Object.assign(doc, props) as ParsedLikeC4LangiumDocument
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
export function isFqnIndexedDocument(doc: LangiumDocument): doc is FqnIndexedDocument {
|
|
198
|
-
return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !!doc.c4fqnIndex
|
|
199
|
-
}
|
|
200
|
-
|
|
201
198
|
export function isLikeC4LangiumDocument(doc: LangiumDocument): doc is LikeC4LangiumDocument {
|
|
202
199
|
return doc.textDocument.languageId === LikeC4LanguageMetaData.languageId
|
|
203
200
|
}
|
|
204
201
|
|
|
202
|
+
export function isFqnIndexedDocument(doc: LangiumDocument): doc is FqnIndexedDocument {
|
|
203
|
+
return isLikeC4LangiumDocument(doc) && doc.state >= DocumentState.IndexedContent && !!doc.c4fqnIndex
|
|
204
|
+
}
|
|
205
|
+
|
|
205
206
|
export function isParsedLikeC4LangiumDocument(
|
|
206
207
|
doc: LangiumDocument
|
|
207
208
|
): doc is ParsedLikeC4LangiumDocument {
|
|
@@ -230,6 +231,7 @@ const isValidatableAstNode = validatableAstNodeGuards([
|
|
|
230
231
|
ast.isRelationPredicateWith,
|
|
231
232
|
ast.isElementExpression,
|
|
232
233
|
ast.isRelationExpression,
|
|
234
|
+
ast.isDynamicViewParallelSteps,
|
|
233
235
|
ast.isDynamicViewStep,
|
|
234
236
|
ast.isViewProperty,
|
|
235
237
|
ast.isStyleProperty,
|
|
@@ -265,7 +267,7 @@ const findInvalidContainer = (node: LikeC4AstNode): ValidatableAstNode | undefin
|
|
|
265
267
|
}
|
|
266
268
|
nd = nd.$container
|
|
267
269
|
}
|
|
268
|
-
return
|
|
270
|
+
return undefined
|
|
269
271
|
}
|
|
270
272
|
|
|
271
273
|
export function checksFromDiagnostics(doc: LikeC4LangiumDocument) {
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { type AstNode, GrammarUtils } from 'langium'
|
|
2
|
+
import { AbstractFormatter, Formatting, type NodeFormatter } from 'langium/lsp'
|
|
3
|
+
import * as ast from '../generated/ast'
|
|
4
|
+
import * as utils from './utils'
|
|
5
|
+
|
|
6
|
+
const FormattingOptions = {
|
|
7
|
+
newLine: Formatting.newLine({ allowMore: true }),
|
|
8
|
+
oneSpace: Formatting.oneSpace(),
|
|
9
|
+
noSpace: Formatting.noSpace(),
|
|
10
|
+
indent: Formatting.indent({ allowMore: true }),
|
|
11
|
+
noIndent: Formatting.noIndent()
|
|
12
|
+
}
|
|
13
|
+
type Predicate<T extends AstNode> = (x: unknown) => x is T
|
|
14
|
+
|
|
15
|
+
export class LikeC4Formatter extends AbstractFormatter {
|
|
16
|
+
protected format(node: AstNode): void {
|
|
17
|
+
this.removeIndentFromTopLevelStatements(node)
|
|
18
|
+
this.indentContentInBraces(node)
|
|
19
|
+
|
|
20
|
+
this.formatSpecificationRule(node)
|
|
21
|
+
this.formatElementDeclaration(node)
|
|
22
|
+
this.formatRelation(node)
|
|
23
|
+
this.formatView(node)
|
|
24
|
+
this.formatViewRuleStyle(node)
|
|
25
|
+
this.formatIncludeExcludeExpressions(node)
|
|
26
|
+
this.formatWhereExpression(node)
|
|
27
|
+
this.formatWithPredicate(node)
|
|
28
|
+
this.formatLeafProperty(node)
|
|
29
|
+
this.formatMetadataProperty(node)
|
|
30
|
+
this.formatAutolayoutProperty(node)
|
|
31
|
+
this.formatLinkProperty(node)
|
|
32
|
+
this.formatNavigateToProperty(node)
|
|
33
|
+
this.formatTags(node)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected formatTags(node: AstNode) {
|
|
37
|
+
this.on(node, ast.isTags, (n, f) => {
|
|
38
|
+
f.cst(GrammarUtils.findNodesForProperty(n.$cstNode, 'values').slice(1))
|
|
39
|
+
.prepend(FormattingOptions.oneSpace)
|
|
40
|
+
|
|
41
|
+
f.keywords(',')
|
|
42
|
+
.prepend(FormattingOptions.noSpace)
|
|
43
|
+
.append(FormattingOptions.oneSpace)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected formatRelation(node: AstNode) {
|
|
48
|
+
this.on(node, ast.isRelation, (n, f) => {
|
|
49
|
+
f.property('source').append(FormattingOptions.oneSpace)
|
|
50
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
51
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
52
|
+
|
|
53
|
+
f.properties('target', 'title', 'technology', 'tags').prepend(FormattingOptions.oneSpace)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
this.on(node, ast.isDynamicViewStep, (n, f) => {
|
|
57
|
+
f.properties('source').append(FormattingOptions.oneSpace)
|
|
58
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
59
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
60
|
+
f.properties('target', 'title').prepend(FormattingOptions.oneSpace)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
this.on(node, ast.isDirectedRelationExpression)
|
|
64
|
+
?.property('target').prepend(FormattingOptions.oneSpace)
|
|
65
|
+
|
|
66
|
+
this.on(node, ast.isOutgoingRelationExpression, (n, f) => {
|
|
67
|
+
f.property('from').append(FormattingOptions.oneSpace)
|
|
68
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
69
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
this.on(node, ast.isIncomingRelationExpression)
|
|
73
|
+
?.keywords('->').append(FormattingOptions.oneSpace)
|
|
74
|
+
|
|
75
|
+
this.on(node, ast.isInOutRelationExpression)
|
|
76
|
+
?.property('inout').append(FormattingOptions.oneSpace)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
protected removeIndentFromTopLevelStatements(node: AstNode) {
|
|
80
|
+
if (
|
|
81
|
+
ast.isModel(node)
|
|
82
|
+
|| ast.isSpecificationRule(node)
|
|
83
|
+
|| ast.isModelViews(node)
|
|
84
|
+
|| ast.isLikeC4Lib(node)
|
|
85
|
+
) {
|
|
86
|
+
const formatter = this.getNodeFormatter(node)
|
|
87
|
+
formatter.keywords('specification', 'model', 'views', 'likec4lib')
|
|
88
|
+
.prepend(FormattingOptions.noIndent)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
protected indentContentInBraces(node: AstNode) {
|
|
93
|
+
if (
|
|
94
|
+
ast.isLikeC4Lib(node)
|
|
95
|
+
|| ast.isSpecificationRule(node)
|
|
96
|
+
|| ast.isSpecificationElementKind(node)
|
|
97
|
+
|| ast.isSpecificationRelationshipKind(node)
|
|
98
|
+
|| ast.isModel(node)
|
|
99
|
+
|| ast.isElementBody(node)
|
|
100
|
+
|| ast.isExtendElementBody(node)
|
|
101
|
+
|| ast.isRelationBody(node)
|
|
102
|
+
|| ast.isRelationStyleProperty(node)
|
|
103
|
+
|| ast.isMetadataBody(node)
|
|
104
|
+
|| ast.isModelViews(node)
|
|
105
|
+
|| ast.isElementViewBody(node)
|
|
106
|
+
|| ast.isDynamicViewBody(node)
|
|
107
|
+
|| ast.isViewRuleStyle(node)
|
|
108
|
+
|| ast.isCustomElementProperties(node)
|
|
109
|
+
|| ast.isCustomRelationProperties(node)
|
|
110
|
+
|| ast.isElementStyleProperty(node)
|
|
111
|
+
) {
|
|
112
|
+
const formatter = this.getNodeFormatter(node)
|
|
113
|
+
const openBrace = formatter.keywords('{')
|
|
114
|
+
const closeBrace = formatter.keywords('}')
|
|
115
|
+
|
|
116
|
+
const interiorNodes = formatter.interior(openBrace, closeBrace)
|
|
117
|
+
|
|
118
|
+
// Workaround for tags as they are parsed as overlapping regions.
|
|
119
|
+
// E.g. '#tag1, #tag2' will be parsed as two nodes: '#tag1' and '#tag1, #tag2'
|
|
120
|
+
let perviousNode = null
|
|
121
|
+
for (const interiorNode of interiorNodes.nodes) {
|
|
122
|
+
if (!perviousNode || !utils.areOverlap(perviousNode, interiorNode)) {
|
|
123
|
+
formatter.cst([interiorNode]).prepend(FormattingOptions.indent)
|
|
124
|
+
}
|
|
125
|
+
perviousNode = interiorNode
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
openBrace
|
|
129
|
+
.prepend(FormattingOptions.noIndent)
|
|
130
|
+
.prepend(FormattingOptions.oneSpace)
|
|
131
|
+
closeBrace
|
|
132
|
+
.prepend(FormattingOptions.noIndent)
|
|
133
|
+
.prepend(Formatting.newLine({ allowMore: true }))
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
protected appendKeywordsWithSpace(node: AstNode) {
|
|
138
|
+
this.on(node, ast.isElementKind)
|
|
139
|
+
?.keywords('element').append(FormattingOptions.oneSpace)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
protected formatView(node: AstNode) {
|
|
143
|
+
this.on(node, ast.isElementView, (n, f) => {
|
|
144
|
+
if (n.extends || n.viewOf || n.name) {
|
|
145
|
+
f.keywords('view').append(FormattingOptions.oneSpace)
|
|
146
|
+
}
|
|
147
|
+
f.keywords('of', 'extends').surround(FormattingOptions.oneSpace)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
this.on(node, ast.isDynamicView)
|
|
151
|
+
?.keywords('dynamic', 'view').append(FormattingOptions.oneSpace)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
protected formatLeafProperty(node: AstNode) {
|
|
155
|
+
if (
|
|
156
|
+
ast.isElementStringProperty(node)
|
|
157
|
+
|| ast.isRelationStringProperty(node)
|
|
158
|
+
|| ast.isViewStringProperty(node)
|
|
159
|
+
|| ast.isNotationProperty(node)
|
|
160
|
+
|| ast.isSpecificationElementStringProperty(node)
|
|
161
|
+
|| ast.isSpecificationRelationshipStringProperty(node)
|
|
162
|
+
|| ast.isColorProperty(node)
|
|
163
|
+
|| ast.isLineProperty(node)
|
|
164
|
+
|| ast.isArrowProperty(node)
|
|
165
|
+
|| ast.isIconProperty(node)
|
|
166
|
+
|| ast.isShapeProperty(node)
|
|
167
|
+
|| ast.isBorderProperty(node)
|
|
168
|
+
|| ast.isOpacityProperty(node)
|
|
169
|
+
) {
|
|
170
|
+
const formatter = this.getNodeFormatter(node)
|
|
171
|
+
formatter.keywords(
|
|
172
|
+
'title',
|
|
173
|
+
'description',
|
|
174
|
+
'technology',
|
|
175
|
+
'notation',
|
|
176
|
+
'color',
|
|
177
|
+
'line',
|
|
178
|
+
'head',
|
|
179
|
+
'tail',
|
|
180
|
+
'icon',
|
|
181
|
+
'shape',
|
|
182
|
+
'border',
|
|
183
|
+
'opacity'
|
|
184
|
+
)
|
|
185
|
+
.append(FormattingOptions.oneSpace)
|
|
186
|
+
|
|
187
|
+
formatter.keyword(':')
|
|
188
|
+
.prepend(FormattingOptions.noSpace)
|
|
189
|
+
.append(FormattingOptions.oneSpace)
|
|
190
|
+
|
|
191
|
+
formatter.keyword(';')
|
|
192
|
+
.prepend(FormattingOptions.noSpace)
|
|
193
|
+
.append(FormattingOptions.newLine)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
protected formatLinkProperty(node: AstNode) {
|
|
198
|
+
this.on(node, ast.isLinkProperty, (n, f) => {
|
|
199
|
+
f.keyword('link').append(FormattingOptions.oneSpace)
|
|
200
|
+
f.property('value').append(FormattingOptions.oneSpace)
|
|
201
|
+
f.keyword(':')
|
|
202
|
+
.prepend(FormattingOptions.noSpace)
|
|
203
|
+
.append(FormattingOptions.oneSpace)
|
|
204
|
+
|
|
205
|
+
f.keyword(';')
|
|
206
|
+
.prepend(FormattingOptions.noSpace)
|
|
207
|
+
.append(FormattingOptions.newLine)
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
protected formatNavigateToProperty(node: AstNode) {
|
|
212
|
+
this.on(node, ast.isNavigateToProperty)
|
|
213
|
+
?.property('key').append(FormattingOptions.oneSpace)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
protected formatAutolayoutProperty(node: AstNode) {
|
|
217
|
+
this.on(node, ast.isViewRuleAutoLayout)
|
|
218
|
+
?.keyword('autoLayout').append(FormattingOptions.oneSpace)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
protected formatMetadataProperty(node: AstNode) {
|
|
222
|
+
this.on(node, ast.isMetadataAttribute, (n, f) => {
|
|
223
|
+
f.property('key').append(FormattingOptions.oneSpace)
|
|
224
|
+
f.keyword(':')
|
|
225
|
+
.prepend(FormattingOptions.noSpace)
|
|
226
|
+
.append(FormattingOptions.oneSpace)
|
|
227
|
+
f.keyword(';')
|
|
228
|
+
.prepend(FormattingOptions.noSpace)
|
|
229
|
+
.append(FormattingOptions.newLine)
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
protected formatElementDeclaration(node: AstNode) {
|
|
234
|
+
this.on(node, ast.isElement, (n, f) => {
|
|
235
|
+
const kind = GrammarUtils.findNodeForProperty(n.$cstNode, 'kind')
|
|
236
|
+
const name = GrammarUtils.findNodeForProperty(n.$cstNode, 'name')
|
|
237
|
+
|
|
238
|
+
if (name && kind) {
|
|
239
|
+
// system sys1
|
|
240
|
+
if (utils.compareRanges(name, kind) > 0) {
|
|
241
|
+
f.cst([kind]).append(FormattingOptions.oneSpace)
|
|
242
|
+
}
|
|
243
|
+
// sys1 = system
|
|
244
|
+
else {
|
|
245
|
+
f.cst([name]).append(FormattingOptions.oneSpace)
|
|
246
|
+
f.cst([kind]).prepend(FormattingOptions.oneSpace)
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
f.properties('props').prepend(FormattingOptions.oneSpace)
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
protected formatSpecificationRule(node: AstNode) {
|
|
255
|
+
if (
|
|
256
|
+
ast.isSpecificationElementKind(node)
|
|
257
|
+
|| ast.isSpecificationRelationshipKind(node)
|
|
258
|
+
|| ast.isSpecificationTag(node)
|
|
259
|
+
) {
|
|
260
|
+
const formatter = this.getNodeFormatter(node)
|
|
261
|
+
|
|
262
|
+
formatter.keywords('element', 'relationship', 'tag')
|
|
263
|
+
.append(FormattingOptions.oneSpace)
|
|
264
|
+
}
|
|
265
|
+
if (
|
|
266
|
+
ast.isSpecificationColor(node)
|
|
267
|
+
) {
|
|
268
|
+
const formatter = this.getNodeFormatter(node)
|
|
269
|
+
formatter.keyword('color').append(FormattingOptions.oneSpace)
|
|
270
|
+
formatter.property('name').append(FormattingOptions.oneSpace)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
protected formatWithPredicate(node: AstNode) {
|
|
275
|
+
const formatter = this.getNodeFormatter(node)
|
|
276
|
+
if (
|
|
277
|
+
ast.isElementPredicateWith(node)
|
|
278
|
+
|| ast.isRelationPredicateWith(node)
|
|
279
|
+
) {
|
|
280
|
+
formatter.keyword('with').prepend(FormattingOptions.oneSpace)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
protected formatViewRuleStyle(node: AstNode) {
|
|
285
|
+
this.on(node, ast.isViewRuleStyle)
|
|
286
|
+
?.keyword('style').append(FormattingOptions.oneSpace)
|
|
287
|
+
|
|
288
|
+
this.on(node, ast.isElementExpressionsIterator)
|
|
289
|
+
?.keyword(',')
|
|
290
|
+
.prepend(FormattingOptions.noSpace)
|
|
291
|
+
.append(FormattingOptions.oneSpace)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected formatWhereExpression(node: AstNode) {
|
|
295
|
+
if (
|
|
296
|
+
ast.isRelationPredicateOrWhere(node)
|
|
297
|
+
|| ast.isElementPredicateOrWhere(node)
|
|
298
|
+
) {
|
|
299
|
+
const formatter = this.getNodeFormatter(node)
|
|
300
|
+
formatter.keyword('where').append(FormattingOptions.oneSpace)
|
|
301
|
+
}
|
|
302
|
+
if (
|
|
303
|
+
ast.isWhereRelationExpression(node)
|
|
304
|
+
|| ast.isWhereElementExpression(node)
|
|
305
|
+
) {
|
|
306
|
+
const formatter = this.getNodeFormatter(node)
|
|
307
|
+
formatter.property('operator').surround(FormattingOptions.oneSpace)
|
|
308
|
+
}
|
|
309
|
+
if (
|
|
310
|
+
ast.isWhereElementNegation(node)
|
|
311
|
+
|| ast.isWhereRelationNegation(node)
|
|
312
|
+
) {
|
|
313
|
+
const formatter = this.getNodeFormatter(node)
|
|
314
|
+
formatter.keyword('not').append(FormattingOptions.oneSpace)
|
|
315
|
+
}
|
|
316
|
+
if (
|
|
317
|
+
ast.isWhereElement(node)
|
|
318
|
+
|| ast.isWhereElementTag(node)
|
|
319
|
+
|| ast.isWhereElementKind(node)
|
|
320
|
+
|| ast.isWhereRelation(node)
|
|
321
|
+
|| ast.isWhereRelationTag(node)
|
|
322
|
+
|| ast.isWhereRelationKind(node)
|
|
323
|
+
) {
|
|
324
|
+
const formatter = this.getNodeFormatter(node)
|
|
325
|
+
formatter.property('operator').surround(FormattingOptions.oneSpace)
|
|
326
|
+
formatter.property('not').surround(FormattingOptions.oneSpace)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
protected formatIncludeExcludeExpressions(node: AstNode) {
|
|
331
|
+
if (
|
|
332
|
+
ast.isDynamicViewRule(node)
|
|
333
|
+
|| ast.isIncludePredicate(node)
|
|
334
|
+
|| ast.isExcludePredicate(node)
|
|
335
|
+
) {
|
|
336
|
+
const formatter = this.getNodeFormatter(node)
|
|
337
|
+
|
|
338
|
+
if (!node.$cstNode || !utils.isMultiline(node.$cstNode)) {
|
|
339
|
+
formatter.keywords('include', 'exclude')
|
|
340
|
+
.append(FormattingOptions.oneSpace)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (
|
|
344
|
+
ast.isDynamicViewPredicateIterator(node)
|
|
345
|
+
|| ast.isPredicates(node)
|
|
346
|
+
|| ast.isPredicates(node)
|
|
347
|
+
) {
|
|
348
|
+
const formatter = this.getNodeFormatter(node)
|
|
349
|
+
const parent = this.findPredicateExpressionRoot(node)
|
|
350
|
+
const isMultiline = parent?.$cstNode && utils.isMultiline(parent?.$cstNode)
|
|
351
|
+
|
|
352
|
+
if (isMultiline) {
|
|
353
|
+
formatter.property('value').prepend(FormattingOptions.indent)
|
|
354
|
+
}
|
|
355
|
+
formatter.keyword(',')
|
|
356
|
+
.prepend(FormattingOptions.noSpace)
|
|
357
|
+
.append(isMultiline ? FormattingOptions.newLine : FormattingOptions.oneSpace)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private findPredicateExpressionRoot(node: AstNode): AstNode | undefined {
|
|
362
|
+
let parent = node.$container
|
|
363
|
+
while (true) {
|
|
364
|
+
if (
|
|
365
|
+
!parent
|
|
366
|
+
|| ast.isDynamicViewRule(parent)
|
|
367
|
+
|| ast.isIncludePredicate(parent)
|
|
368
|
+
|| ast.isExcludePredicate(parent)
|
|
369
|
+
) {
|
|
370
|
+
return parent
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
parent = parent.$container
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private on<T extends AstNode>(
|
|
378
|
+
node: AstNode,
|
|
379
|
+
predicate: Predicate<T>,
|
|
380
|
+
format?: (node: T, f: NodeFormatter<T>) => void
|
|
381
|
+
): NodeFormatter<T> | undefined {
|
|
382
|
+
const formatter = predicate(node) ? this.getNodeFormatter(node) : undefined
|
|
383
|
+
|
|
384
|
+
format && formatter && format(node as T, formatter)
|
|
385
|
+
|
|
386
|
+
return formatter
|
|
387
|
+
}
|
|
388
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CstNode } from "langium"
|
|
2
|
+
import type { Position, Range } from "vscode-languageserver-types"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export function areOverlap(a: CstNode, b: CstNode): boolean {
|
|
6
|
+
;[a, b] = compareRanges(a, b) > 0 ? [b, a] : [a, b]
|
|
7
|
+
|
|
8
|
+
return isInRagne(a.range, b.range.start)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function compareRanges(a: CstNode, b: CstNode): number {
|
|
12
|
+
const lineDiff = a.range.start.line - b.range.start.line
|
|
13
|
+
|
|
14
|
+
return lineDiff !== 0 ? lineDiff : a.range.start.character - b.range.start.character
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isInRagne(range: Range, pos: Position): boolean {
|
|
18
|
+
return !(pos.line < range.start.line
|
|
19
|
+
|| pos.line > range.end.line
|
|
20
|
+
|| pos.line == range.start.line && pos.character < range.start.character
|
|
21
|
+
|| pos.line == range.end.line && pos.character > range.end.character)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isMultiline(node: CstNode): boolean {
|
|
25
|
+
return node.range.start.line != node.range.end.line
|
|
26
|
+
}
|