@likec4/language-server 1.9.0 → 1.10.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.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +3 -4
- package/dist/browser.d.mts +3 -4
- package/dist/browser.d.ts +3 -4
- 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 +3 -4
- package/dist/node.d.mts +3 -4
- package/dist/node.d.ts +3 -4
- package/dist/node.mjs +1 -1
- package/dist/shared/{language-server.Q-wtPShM.mjs → language-server.BFBeyvV8.mjs} +486 -108
- package/dist/shared/{language-server.86lmJ8ZN.d.cts → language-server.BGy3FJPJ.d.cts} +43 -14
- package/dist/shared/{language-server.B1TZgyoH.cjs → language-server.Bfc-5M8A.cjs} +482 -104
- package/dist/shared/{language-server.CCB4ESN5.mjs → language-server.CbqwHp7Q.mjs} +184 -120
- package/dist/shared/{language-server.RjhrBZS0.d.ts → language-server.CnVuAxDh.d.ts} +43 -14
- package/dist/shared/{language-server.CFTY6j4e.d.mts → language-server.DEK39RmI.d.mts} +43 -14
- package/dist/shared/{language-server.D0bOlrCi.cjs → language-server.DJhoJBWh.cjs} +180 -116
- package/package.json +13 -11
- package/src/ast.ts +8 -6
- package/src/formatting/LikeC4Formatter.ts +390 -0
- package/src/formatting/utils.ts +26 -0
- package/src/generated/ast.ts +203 -11
- package/src/generated/grammar.ts +2 -2
- package/src/generated/module.ts +1 -1
- package/src/like-c4.langium +34 -7
- package/src/lsp/CompletionProvider.ts +1 -1
- 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 +111 -80
- 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/model-graph/utils/uniqueTags.test.ts +42 -0
- package/src/model-graph/utils/uniqueTags.ts +19 -0
- 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/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,390 @@
|
|
|
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
|
+
const sourceNodes = n?.source?.$cstNode ? [n?.source?.$cstNode] : []
|
|
50
|
+
|
|
51
|
+
f.cst(sourceNodes).append(FormattingOptions.oneSpace)
|
|
52
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
53
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
54
|
+
|
|
55
|
+
f.properties('target', 'title', 'technology', 'tags').prepend(FormattingOptions.oneSpace)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
this.on(node, ast.isDynamicViewStep, (n, f) => {
|
|
59
|
+
f.properties('source').append(FormattingOptions.oneSpace)
|
|
60
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
61
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
62
|
+
f.properties('target', 'title').prepend(FormattingOptions.oneSpace)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
this.on(node, ast.isDirectedRelationExpression)
|
|
66
|
+
?.property('target').prepend(FormattingOptions.oneSpace)
|
|
67
|
+
|
|
68
|
+
this.on(node, ast.isOutgoingRelationExpression, (n, f) => {
|
|
69
|
+
f.property('from').append(FormattingOptions.oneSpace)
|
|
70
|
+
f.keywords(']->').prepend(FormattingOptions.noSpace)
|
|
71
|
+
f.keywords('-[').append(FormattingOptions.noSpace)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
this.on(node, ast.isIncomingRelationExpression)
|
|
75
|
+
?.keywords('->').append(FormattingOptions.oneSpace)
|
|
76
|
+
|
|
77
|
+
this.on(node, ast.isInOutRelationExpression)
|
|
78
|
+
?.keyword('->').prepend(FormattingOptions.oneSpace)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected removeIndentFromTopLevelStatements(node: AstNode) {
|
|
82
|
+
if (
|
|
83
|
+
ast.isModel(node)
|
|
84
|
+
|| ast.isSpecificationRule(node)
|
|
85
|
+
|| ast.isModelViews(node)
|
|
86
|
+
|| ast.isLikeC4Lib(node)
|
|
87
|
+
) {
|
|
88
|
+
const formatter = this.getNodeFormatter(node)
|
|
89
|
+
formatter.keywords('specification', 'model', 'views', 'likec4lib')
|
|
90
|
+
.prepend(FormattingOptions.noIndent)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected indentContentInBraces(node: AstNode) {
|
|
95
|
+
if (
|
|
96
|
+
ast.isLikeC4Lib(node)
|
|
97
|
+
|| ast.isSpecificationRule(node)
|
|
98
|
+
|| ast.isSpecificationElementKind(node)
|
|
99
|
+
|| ast.isSpecificationRelationshipKind(node)
|
|
100
|
+
|| ast.isModel(node)
|
|
101
|
+
|| ast.isElementBody(node)
|
|
102
|
+
|| ast.isExtendElementBody(node)
|
|
103
|
+
|| ast.isRelationBody(node)
|
|
104
|
+
|| ast.isRelationStyleProperty(node)
|
|
105
|
+
|| ast.isMetadataBody(node)
|
|
106
|
+
|| ast.isModelViews(node)
|
|
107
|
+
|| ast.isElementViewBody(node)
|
|
108
|
+
|| ast.isDynamicViewBody(node)
|
|
109
|
+
|| ast.isViewRuleStyle(node)
|
|
110
|
+
|| ast.isCustomElementProperties(node)
|
|
111
|
+
|| ast.isCustomRelationProperties(node)
|
|
112
|
+
|| ast.isElementStyleProperty(node)
|
|
113
|
+
) {
|
|
114
|
+
const formatter = this.getNodeFormatter(node)
|
|
115
|
+
const openBrace = formatter.keywords('{')
|
|
116
|
+
const closeBrace = formatter.keywords('}')
|
|
117
|
+
|
|
118
|
+
const interiorNodes = formatter.interior(openBrace, closeBrace)
|
|
119
|
+
|
|
120
|
+
// Workaround for tags as they are parsed as overlapping regions.
|
|
121
|
+
// E.g. '#tag1, #tag2' will be parsed as two nodes: '#tag1' and '#tag1, #tag2'
|
|
122
|
+
let perviousNode = null
|
|
123
|
+
for (const interiorNode of interiorNodes.nodes) {
|
|
124
|
+
if (!perviousNode || !utils.areOverlap(perviousNode, interiorNode)) {
|
|
125
|
+
formatter.cst([interiorNode]).prepend(FormattingOptions.indent)
|
|
126
|
+
}
|
|
127
|
+
perviousNode = interiorNode
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
openBrace
|
|
131
|
+
.prepend(FormattingOptions.noIndent)
|
|
132
|
+
.prepend(FormattingOptions.oneSpace)
|
|
133
|
+
closeBrace
|
|
134
|
+
.prepend(FormattingOptions.noIndent)
|
|
135
|
+
.prepend(Formatting.newLine({ allowMore: true }))
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
protected appendKeywordsWithSpace(node: AstNode) {
|
|
140
|
+
this.on(node, ast.isElementKind)
|
|
141
|
+
?.keywords('element').append(FormattingOptions.oneSpace)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
protected formatView(node: AstNode) {
|
|
145
|
+
this.on(node, ast.isElementView, (n, f) => {
|
|
146
|
+
if (n.extends || n.viewOf || n.name) {
|
|
147
|
+
f.keywords('view').append(FormattingOptions.oneSpace)
|
|
148
|
+
}
|
|
149
|
+
f.keywords('of', 'extends').surround(FormattingOptions.oneSpace)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
this.on(node, ast.isDynamicView)
|
|
153
|
+
?.keywords('dynamic', 'view').append(FormattingOptions.oneSpace)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected formatLeafProperty(node: AstNode) {
|
|
157
|
+
if (
|
|
158
|
+
ast.isElementStringProperty(node)
|
|
159
|
+
|| ast.isRelationStringProperty(node)
|
|
160
|
+
|| ast.isViewStringProperty(node)
|
|
161
|
+
|| ast.isNotationProperty(node)
|
|
162
|
+
|| ast.isSpecificationElementStringProperty(node)
|
|
163
|
+
|| ast.isSpecificationRelationshipStringProperty(node)
|
|
164
|
+
|| ast.isColorProperty(node)
|
|
165
|
+
|| ast.isLineProperty(node)
|
|
166
|
+
|| ast.isArrowProperty(node)
|
|
167
|
+
|| ast.isIconProperty(node)
|
|
168
|
+
|| ast.isShapeProperty(node)
|
|
169
|
+
|| ast.isBorderProperty(node)
|
|
170
|
+
|| ast.isOpacityProperty(node)
|
|
171
|
+
) {
|
|
172
|
+
const formatter = this.getNodeFormatter(node)
|
|
173
|
+
formatter.keywords(
|
|
174
|
+
'title',
|
|
175
|
+
'description',
|
|
176
|
+
'technology',
|
|
177
|
+
'notation',
|
|
178
|
+
'color',
|
|
179
|
+
'line',
|
|
180
|
+
'head',
|
|
181
|
+
'tail',
|
|
182
|
+
'icon',
|
|
183
|
+
'shape',
|
|
184
|
+
'border',
|
|
185
|
+
'opacity'
|
|
186
|
+
)
|
|
187
|
+
.append(FormattingOptions.oneSpace)
|
|
188
|
+
|
|
189
|
+
formatter.keyword(':')
|
|
190
|
+
.prepend(FormattingOptions.noSpace)
|
|
191
|
+
.append(FormattingOptions.oneSpace)
|
|
192
|
+
|
|
193
|
+
formatter.keyword(';')
|
|
194
|
+
.prepend(FormattingOptions.noSpace)
|
|
195
|
+
.append(FormattingOptions.newLine)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
protected formatLinkProperty(node: AstNode) {
|
|
200
|
+
this.on(node, ast.isLinkProperty, (n, f) => {
|
|
201
|
+
f.keyword('link').append(FormattingOptions.oneSpace)
|
|
202
|
+
f.property('value').append(FormattingOptions.oneSpace)
|
|
203
|
+
f.keyword(':')
|
|
204
|
+
.prepend(FormattingOptions.noSpace)
|
|
205
|
+
.append(FormattingOptions.oneSpace)
|
|
206
|
+
|
|
207
|
+
f.keyword(';')
|
|
208
|
+
.prepend(FormattingOptions.noSpace)
|
|
209
|
+
.append(FormattingOptions.newLine)
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
protected formatNavigateToProperty(node: AstNode) {
|
|
214
|
+
this.on(node, ast.isNavigateToProperty)
|
|
215
|
+
?.property('key').append(FormattingOptions.oneSpace)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
protected formatAutolayoutProperty(node: AstNode) {
|
|
219
|
+
this.on(node, ast.isViewRuleAutoLayout)
|
|
220
|
+
?.keyword('autoLayout').append(FormattingOptions.oneSpace)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
protected formatMetadataProperty(node: AstNode) {
|
|
224
|
+
this.on(node, ast.isMetadataAttribute, (n, f) => {
|
|
225
|
+
f.property('key').append(FormattingOptions.oneSpace)
|
|
226
|
+
f.keyword(':')
|
|
227
|
+
.prepend(FormattingOptions.noSpace)
|
|
228
|
+
.append(FormattingOptions.oneSpace)
|
|
229
|
+
f.keyword(';')
|
|
230
|
+
.prepend(FormattingOptions.noSpace)
|
|
231
|
+
.append(FormattingOptions.newLine)
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
protected formatElementDeclaration(node: AstNode) {
|
|
236
|
+
this.on(node, ast.isElement, (n, f) => {
|
|
237
|
+
const kind = GrammarUtils.findNodeForProperty(n.$cstNode, 'kind')
|
|
238
|
+
const name = GrammarUtils.findNodeForProperty(n.$cstNode, 'name')
|
|
239
|
+
|
|
240
|
+
if (name && kind) {
|
|
241
|
+
// system sys1
|
|
242
|
+
if (utils.compareRanges(name, kind) > 0) {
|
|
243
|
+
f.cst([kind]).append(FormattingOptions.oneSpace)
|
|
244
|
+
}
|
|
245
|
+
// sys1 = system
|
|
246
|
+
else {
|
|
247
|
+
f.cst([name]).append(FormattingOptions.oneSpace)
|
|
248
|
+
f.cst([kind]).prepend(FormattingOptions.oneSpace)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
f.properties('props').prepend(FormattingOptions.oneSpace)
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected formatSpecificationRule(node: AstNode) {
|
|
257
|
+
if (
|
|
258
|
+
ast.isSpecificationElementKind(node)
|
|
259
|
+
|| ast.isSpecificationRelationshipKind(node)
|
|
260
|
+
|| ast.isSpecificationTag(node)
|
|
261
|
+
) {
|
|
262
|
+
const formatter = this.getNodeFormatter(node)
|
|
263
|
+
|
|
264
|
+
formatter.keywords('element', 'relationship', 'tag')
|
|
265
|
+
.append(FormattingOptions.oneSpace)
|
|
266
|
+
}
|
|
267
|
+
if (
|
|
268
|
+
ast.isSpecificationColor(node)
|
|
269
|
+
) {
|
|
270
|
+
const formatter = this.getNodeFormatter(node)
|
|
271
|
+
formatter.keyword('color').append(FormattingOptions.oneSpace)
|
|
272
|
+
formatter.property('name').append(FormattingOptions.oneSpace)
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
protected formatWithPredicate(node: AstNode) {
|
|
277
|
+
const formatter = this.getNodeFormatter(node)
|
|
278
|
+
if (
|
|
279
|
+
ast.isElementPredicateWith(node)
|
|
280
|
+
|| ast.isRelationPredicateWith(node)
|
|
281
|
+
) {
|
|
282
|
+
formatter.keyword('with').prepend(FormattingOptions.oneSpace)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
protected formatViewRuleStyle(node: AstNode) {
|
|
287
|
+
this.on(node, ast.isViewRuleStyle)
|
|
288
|
+
?.keyword('style').append(FormattingOptions.oneSpace)
|
|
289
|
+
|
|
290
|
+
this.on(node, ast.isElementExpressionsIterator)
|
|
291
|
+
?.keyword(',')
|
|
292
|
+
.prepend(FormattingOptions.noSpace)
|
|
293
|
+
.append(FormattingOptions.oneSpace)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
protected formatWhereExpression(node: AstNode) {
|
|
297
|
+
if (
|
|
298
|
+
ast.isRelationPredicateOrWhere(node)
|
|
299
|
+
|| ast.isElementPredicateOrWhere(node)
|
|
300
|
+
) {
|
|
301
|
+
const formatter = this.getNodeFormatter(node)
|
|
302
|
+
formatter.keyword('where').append(FormattingOptions.oneSpace)
|
|
303
|
+
}
|
|
304
|
+
if (
|
|
305
|
+
ast.isWhereRelationExpression(node)
|
|
306
|
+
|| ast.isWhereElementExpression(node)
|
|
307
|
+
) {
|
|
308
|
+
const formatter = this.getNodeFormatter(node)
|
|
309
|
+
formatter.property('operator').surround(FormattingOptions.oneSpace)
|
|
310
|
+
}
|
|
311
|
+
if (
|
|
312
|
+
ast.isWhereElementNegation(node)
|
|
313
|
+
|| ast.isWhereRelationNegation(node)
|
|
314
|
+
) {
|
|
315
|
+
const formatter = this.getNodeFormatter(node)
|
|
316
|
+
formatter.keyword('not').append(FormattingOptions.oneSpace)
|
|
317
|
+
}
|
|
318
|
+
if (
|
|
319
|
+
ast.isWhereElement(node)
|
|
320
|
+
|| ast.isWhereElementTag(node)
|
|
321
|
+
|| ast.isWhereElementKind(node)
|
|
322
|
+
|| ast.isWhereRelation(node)
|
|
323
|
+
|| ast.isWhereRelationTag(node)
|
|
324
|
+
|| ast.isWhereRelationKind(node)
|
|
325
|
+
) {
|
|
326
|
+
const formatter = this.getNodeFormatter(node)
|
|
327
|
+
formatter.property('operator').surround(FormattingOptions.oneSpace)
|
|
328
|
+
formatter.property('not').surround(FormattingOptions.oneSpace)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
protected formatIncludeExcludeExpressions(node: AstNode) {
|
|
333
|
+
if (
|
|
334
|
+
ast.isDynamicViewRule(node)
|
|
335
|
+
|| ast.isIncludePredicate(node)
|
|
336
|
+
|| ast.isExcludePredicate(node)
|
|
337
|
+
) {
|
|
338
|
+
const formatter = this.getNodeFormatter(node)
|
|
339
|
+
|
|
340
|
+
if (!node.$cstNode || !utils.isMultiline(node.$cstNode)) {
|
|
341
|
+
formatter.keywords('include', 'exclude')
|
|
342
|
+
.append(FormattingOptions.oneSpace)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (
|
|
346
|
+
ast.isDynamicViewPredicateIterator(node)
|
|
347
|
+
|| ast.isPredicates(node)
|
|
348
|
+
|| ast.isPredicates(node)
|
|
349
|
+
) {
|
|
350
|
+
const formatter = this.getNodeFormatter(node)
|
|
351
|
+
const parent = this.findPredicateExpressionRoot(node)
|
|
352
|
+
const isMultiline = parent?.$cstNode && utils.isMultiline(parent?.$cstNode)
|
|
353
|
+
|
|
354
|
+
if (isMultiline) {
|
|
355
|
+
formatter.property('value').prepend(FormattingOptions.indent)
|
|
356
|
+
}
|
|
357
|
+
formatter.keyword(',')
|
|
358
|
+
.prepend(FormattingOptions.noSpace)
|
|
359
|
+
.append(isMultiline ? FormattingOptions.newLine : FormattingOptions.oneSpace)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private findPredicateExpressionRoot(node: AstNode): AstNode | undefined {
|
|
364
|
+
let parent = node.$container
|
|
365
|
+
while (true) {
|
|
366
|
+
if (
|
|
367
|
+
!parent
|
|
368
|
+
|| ast.isDynamicViewRule(parent)
|
|
369
|
+
|| ast.isIncludePredicate(parent)
|
|
370
|
+
|| ast.isExcludePredicate(parent)
|
|
371
|
+
) {
|
|
372
|
+
return parent
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
parent = parent.$container
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private on<T extends AstNode>(
|
|
380
|
+
node: AstNode,
|
|
381
|
+
predicate: Predicate<T>,
|
|
382
|
+
format?: (node: T, f: NodeFormatter<T>) => void
|
|
383
|
+
): NodeFormatter<T> | undefined {
|
|
384
|
+
const formatter = predicate(node) ? this.getNodeFormatter(node) : undefined
|
|
385
|
+
|
|
386
|
+
format && formatter && format(node as T, formatter)
|
|
387
|
+
|
|
388
|
+
return formatter
|
|
389
|
+
}
|
|
390
|
+
}
|
|
@@ -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
|
+
}
|