@likec4/language-server 1.21.1 → 1.22.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/README.md +4 -1
- package/bin/likec4-language-server.mjs +5 -2
- package/dist/LikeC4FileSystem.js +2 -2
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +17 -2
- package/dist/bundled.d.ts +8 -0
- package/dist/bundled.js +25 -0
- package/dist/bundled.mjs +2555 -4022
- package/dist/index.d.ts +3 -3
- package/dist/index.js +23 -2
- package/dist/logger.d.ts +9 -3
- package/dist/logger.js +35 -55
- package/dist/model/fqn-computation.js +2 -2
- package/dist/model/model-builder.js +13 -14
- package/dist/model-change/ModelChanges.js +2 -2
- package/dist/module.js +1 -4
- package/dist/references/scope-provider.js +3 -3
- package/dist/view-utils/manual-layout.js +2 -2
- package/dist/views/configurable-layouter.js +4 -4
- package/dist/views/likec4-views.d.ts +2 -1
- package/dist/views/likec4-views.js +2 -2
- package/package.json +14 -12
- package/dist/test/setup.d.ts +0 -1
- package/dist/test/setup.js +0 -7
- package/src/LikeC4FileSystem.ts +0 -38
- package/src/Rpc.ts +0 -134
- package/src/ast.ts +0 -556
- package/src/browser.ts +0 -35
- package/src/documentation/documentation-provider.ts +0 -52
- package/src/documentation/index.ts +0 -1
- package/src/formatting/LikeC4Formatter.ts +0 -639
- package/src/formatting/utils.ts +0 -26
- package/src/generated/ast.ts +0 -3735
- package/src/generated/grammar.ts +0 -10
- package/src/generated/module.ts +0 -33
- package/src/generated-lib/icons.ts +0 -1538
- package/src/index.ts +0 -30
- package/src/like-c4.langium +0 -901
- package/src/likec4lib.ts +0 -6
- package/src/logger.ts +0 -80
- package/src/lsp/CodeLensProvider.ts +0 -50
- package/src/lsp/CompletionProvider.ts +0 -147
- package/src/lsp/DocumentHighlightProvider.ts +0 -12
- package/src/lsp/DocumentLinkProvider.ts +0 -65
- package/src/lsp/DocumentSymbolProvider.ts +0 -313
- package/src/lsp/HoverProvider.ts +0 -92
- package/src/lsp/RenameProvider.ts +0 -8
- package/src/lsp/SemanticTokenProvider.ts +0 -383
- package/src/lsp/index.ts +0 -8
- package/src/model/deployments-index.ts +0 -209
- package/src/model/fqn-computation.ts +0 -83
- package/src/model/fqn-index.ts +0 -138
- package/src/model/index.ts +0 -6
- package/src/model/model-builder.ts +0 -724
- package/src/model/model-locator.ts +0 -146
- package/src/model/model-parser-where.ts +0 -84
- package/src/model/model-parser.ts +0 -86
- package/src/model/parser/Base.ts +0 -113
- package/src/model/parser/DeploymentModelParser.ts +0 -192
- package/src/model/parser/DeploymentViewParser.ts +0 -122
- package/src/model/parser/FqnRefParser.ts +0 -143
- package/src/model/parser/GlobalsParser.ts +0 -96
- package/src/model/parser/ModelParser.ts +0 -170
- package/src/model/parser/PredicatesParser.ts +0 -315
- package/src/model/parser/SpecificationParser.ts +0 -133
- package/src/model/parser/ViewsParser.ts +0 -428
- package/src/model-change/ModelChanges.ts +0 -101
- package/src/model-change/changeElementStyle.ts +0 -172
- package/src/model-change/changeViewLayout.ts +0 -47
- package/src/model-change/saveManualLayout.ts +0 -41
- package/src/module.ts +0 -255
- package/src/protocol.ts +0 -93
- package/src/references/index.ts +0 -3
- package/src/references/name-provider.ts +0 -37
- package/src/references/scope-computation.ts +0 -364
- package/src/references/scope-provider.ts +0 -201
- package/src/shared/NodeKindProvider.ts +0 -121
- package/src/shared/WorkspaceManager.ts +0 -48
- package/src/shared/WorkspaceSymbolProvider.ts +0 -3
- package/src/shared/index.ts +0 -3
- package/src/test/index.ts +0 -1
- package/src/test/setup.ts +0 -8
- package/src/test/testServices.ts +0 -152
- package/src/utils/disposable.ts +0 -30
- package/src/utils/elementRef.ts +0 -26
- package/src/utils/fqnRef.ts +0 -56
- package/src/utils/index.ts +0 -2
- package/src/utils/printDocs.ts +0 -3
- package/src/utils/stringHash.ts +0 -6
- package/src/validation/_shared.ts +0 -29
- package/src/validation/deployment-checks.ts +0 -131
- package/src/validation/dynamic-view-rule.ts +0 -23
- package/src/validation/dynamic-view-step.ts +0 -36
- package/src/validation/element.ts +0 -56
- package/src/validation/index.ts +0 -171
- package/src/validation/property-checks.ts +0 -52
- package/src/validation/relation.ts +0 -63
- package/src/validation/specification.ts +0 -205
- package/src/validation/view-predicates/element-with.ts +0 -36
- package/src/validation/view-predicates/expanded-element.ts +0 -16
- package/src/validation/view-predicates/expression-v2.ts +0 -101
- package/src/validation/view-predicates/incoming.ts +0 -20
- package/src/validation/view-predicates/index.ts +0 -6
- package/src/validation/view-predicates/outgoing.ts +0 -20
- package/src/validation/view-predicates/relation-with.ts +0 -17
- package/src/validation/view.ts +0 -37
- package/src/view-utils/assignNavigateTo.ts +0 -31
- package/src/view-utils/index.ts +0 -2
- package/src/view-utils/manual-layout.ts +0 -116
- package/src/view-utils/resolve-relative-paths.ts +0 -90
- package/src/views/configurable-layouter.ts +0 -65
- package/src/views/index.ts +0 -1
- package/src/views/likec4-views.ts +0 -139
package/src/likec4lib.ts
DELETED
package/src/logger.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { nonexhaustive } from '@likec4/core'
|
|
2
|
-
import { type ConsolaReporter, formatLogObj, LogLevels, rootLogger as root } from '@likec4/log'
|
|
3
|
-
import { BROWSER } from 'esm-env'
|
|
4
|
-
import type { Connection } from 'vscode-languageserver'
|
|
5
|
-
|
|
6
|
-
export const logger = root.withTag('lsp')
|
|
7
|
-
// export const logger = root
|
|
8
|
-
|
|
9
|
-
export function logError(err: unknown): void {
|
|
10
|
-
logger.error(err)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Logs an error as warning (not critical)
|
|
15
|
-
*/
|
|
16
|
-
export function logWarnError(err: unknown): void {
|
|
17
|
-
logger.warn(err)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function setLogLevel(level: keyof typeof LogLevels): void {
|
|
21
|
-
logger.level = LogLevels[level]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function logToLspConnection(connection: Connection): void {
|
|
25
|
-
const reporter: ConsolaReporter = {
|
|
26
|
-
log: (logObj, _ctx) => {
|
|
27
|
-
const { message, error } = formatLogObj(logObj)
|
|
28
|
-
switch (logObj.type) {
|
|
29
|
-
case 'silent': {
|
|
30
|
-
// ignore
|
|
31
|
-
break
|
|
32
|
-
}
|
|
33
|
-
case 'verbose':
|
|
34
|
-
case 'trace': {
|
|
35
|
-
connection.tracer.log(message)
|
|
36
|
-
break
|
|
37
|
-
}
|
|
38
|
-
case 'debug': {
|
|
39
|
-
connection.console.debug(message)
|
|
40
|
-
break
|
|
41
|
-
}
|
|
42
|
-
case 'log': {
|
|
43
|
-
connection.console.log(message)
|
|
44
|
-
break
|
|
45
|
-
}
|
|
46
|
-
case 'info':
|
|
47
|
-
case 'box':
|
|
48
|
-
case 'ready':
|
|
49
|
-
case 'start':
|
|
50
|
-
case 'success': {
|
|
51
|
-
connection.console.info(message)
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
case 'warn': {
|
|
55
|
-
connection.console.warn(message)
|
|
56
|
-
break
|
|
57
|
-
}
|
|
58
|
-
case 'fail':
|
|
59
|
-
case 'error':
|
|
60
|
-
case 'fatal': {
|
|
61
|
-
connection.console.error(message)
|
|
62
|
-
if (error) {
|
|
63
|
-
connection.telemetry.logEvent({ eventName: 'error', ...error })
|
|
64
|
-
} else {
|
|
65
|
-
connection.telemetry.logEvent({ eventName: 'error', message })
|
|
66
|
-
}
|
|
67
|
-
break
|
|
68
|
-
}
|
|
69
|
-
default:
|
|
70
|
-
nonexhaustive(logObj.type)
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
}
|
|
74
|
-
if (BROWSER) {
|
|
75
|
-
root.addReporter(reporter)
|
|
76
|
-
} else {
|
|
77
|
-
root.setReporters([reporter])
|
|
78
|
-
}
|
|
79
|
-
logger.setReporters(root.options.reporters)
|
|
80
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { DocumentState, type LangiumDocument, type MaybePromise } from 'langium'
|
|
2
|
-
import type { CodeLensProvider } from 'langium/lsp'
|
|
3
|
-
import type { CancellationToken, CodeLens, CodeLensParams } from 'vscode-languageserver'
|
|
4
|
-
import { isParsedLikeC4LangiumDocument, ViewOps } from '../ast'
|
|
5
|
-
import { logger } from '../logger'
|
|
6
|
-
import type { LikeC4Services } from '../module'
|
|
7
|
-
|
|
8
|
-
export class LikeC4CodeLensProvider implements CodeLensProvider {
|
|
9
|
-
constructor(private services: LikeC4Services) {
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async provideCodeLens(
|
|
13
|
-
doc: LangiumDocument,
|
|
14
|
-
_params: CodeLensParams,
|
|
15
|
-
cancelToken?: CancellationToken
|
|
16
|
-
): Promise<CodeLens[] | undefined> {
|
|
17
|
-
if (doc.state !== DocumentState.Validated) {
|
|
18
|
-
logger.debug(`Waiting for document ${doc.uri.path} to be validated`)
|
|
19
|
-
await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Validated, doc.uri, cancelToken)
|
|
20
|
-
logger.debug(`Document ${doc.uri.path} is validated`)
|
|
21
|
-
}
|
|
22
|
-
if (!isParsedLikeC4LangiumDocument(doc)) {
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
const views = doc.parseResult.value.views.flatMap(v => v.views)
|
|
26
|
-
return views.flatMap<CodeLens>(ast => {
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
const viewId = ViewOps.readId(ast)
|
|
29
|
-
const range = ast.$cstNode?.range
|
|
30
|
-
if (!range || !viewId) {
|
|
31
|
-
return []
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
range: {
|
|
36
|
-
start: range.start,
|
|
37
|
-
end: {
|
|
38
|
-
line: range.start.line,
|
|
39
|
-
character: range.start.character + 4
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
command: {
|
|
43
|
-
command: 'likec4.open-preview',
|
|
44
|
-
arguments: [viewId],
|
|
45
|
-
title: 'open preview'
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { type GrammarAST, type MaybePromise, AstUtils } from 'langium'
|
|
2
|
-
import {
|
|
3
|
-
type CompletionAcceptor,
|
|
4
|
-
type CompletionContext,
|
|
5
|
-
type CompletionProviderOptions,
|
|
6
|
-
DefaultCompletionProvider,
|
|
7
|
-
} from 'langium/lsp'
|
|
8
|
-
import { anyPass } from 'remeda'
|
|
9
|
-
import { CompletionItemKind, InsertTextFormat } from 'vscode-languageserver-types'
|
|
10
|
-
import { ast } from '../ast'
|
|
11
|
-
|
|
12
|
-
const STYLE_FIELDS = [
|
|
13
|
-
'color',
|
|
14
|
-
'shape',
|
|
15
|
-
'icon',
|
|
16
|
-
'border',
|
|
17
|
-
'opacity',
|
|
18
|
-
'multiple',
|
|
19
|
-
'size',
|
|
20
|
-
].join(',')
|
|
21
|
-
|
|
22
|
-
export class LikeC4CompletionProvider extends DefaultCompletionProvider {
|
|
23
|
-
override readonly completionOptions = {
|
|
24
|
-
triggerCharacters: ['.'],
|
|
25
|
-
} satisfies CompletionProviderOptions
|
|
26
|
-
|
|
27
|
-
protected override completionForKeyword(
|
|
28
|
-
context: CompletionContext,
|
|
29
|
-
keyword: GrammarAST.Keyword,
|
|
30
|
-
acceptor: CompletionAcceptor,
|
|
31
|
-
): MaybePromise<void> {
|
|
32
|
-
if (!this.filterKeyword(context, keyword)) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
if (keyword.value === 'deployment' && AstUtils.hasContainerOfType(context.node, ast.isModelViews)) {
|
|
36
|
-
return acceptor(context, {
|
|
37
|
-
label: keyword.value,
|
|
38
|
-
detail: `Insert deployment view`,
|
|
39
|
-
kind: CompletionItemKind.Class,
|
|
40
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
41
|
-
insertText: [
|
|
42
|
-
'deployment view ${1:view_${TM_FILENAME_BASE}_${CURRENT_SECOND}} {',
|
|
43
|
-
'\ttitle \'${2:Untitled}\'',
|
|
44
|
-
'\t',
|
|
45
|
-
'\tinclude $0',
|
|
46
|
-
'}',
|
|
47
|
-
].join('\n'),
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
if (['title', 'description', 'technology'].includes(keyword.value)) {
|
|
51
|
-
return acceptor(context, {
|
|
52
|
-
label: keyword.value,
|
|
53
|
-
kind: CompletionItemKind.Property,
|
|
54
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
55
|
-
insertText: `${keyword.value} '\${0}'`,
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
if (['views', 'specification', 'model', 'deployment', 'with'].includes(keyword.value)) {
|
|
59
|
-
return acceptor(context, {
|
|
60
|
-
label: keyword.value,
|
|
61
|
-
detail: `Insert ${keyword.value} block`,
|
|
62
|
-
kind: CompletionItemKind.Module,
|
|
63
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
64
|
-
insertText: `${keyword.value} {\n\t$0\n}`,
|
|
65
|
-
})
|
|
66
|
-
}
|
|
67
|
-
if (keyword.value === 'group') {
|
|
68
|
-
return acceptor(context, {
|
|
69
|
-
label: keyword.value,
|
|
70
|
-
detail: `Insert group block`,
|
|
71
|
-
kind: CompletionItemKind.Class,
|
|
72
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
73
|
-
insertText: [
|
|
74
|
-
'group \'${1:Title}\' {',
|
|
75
|
-
'\t$0',
|
|
76
|
-
'}',
|
|
77
|
-
].join('\n'),
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
if (keyword.value === 'dynamic' && AstUtils.hasContainerOfType(context.node, ast.isModelViews)) {
|
|
81
|
-
return acceptor(context, {
|
|
82
|
-
label: keyword.value,
|
|
83
|
-
detail: `Insert dynamic view`,
|
|
84
|
-
kind: CompletionItemKind.Class,
|
|
85
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
86
|
-
insertText: [
|
|
87
|
-
'dynamic view ${1:view_${TM_FILENAME_BASE}_${CURRENT_SECOND}} {',
|
|
88
|
-
'\ttitle \'${2:Untitled}\'',
|
|
89
|
-
'\t',
|
|
90
|
-
'\t$0',
|
|
91
|
-
'}',
|
|
92
|
-
].join('\n'),
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
if (keyword.value === 'style' && context.node) {
|
|
96
|
-
if (AstUtils.hasContainerOfType(context.node, ast.isGlobalStyle)) {
|
|
97
|
-
return acceptor(context, {
|
|
98
|
-
label: keyword.value,
|
|
99
|
-
detail: `Insert ${keyword.value} block`,
|
|
100
|
-
kind: CompletionItemKind.Module,
|
|
101
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
102
|
-
insertText: `${keyword.value} \${1:name} \${2:*} {\n\t\${3|${STYLE_FIELDS}|} $0\n}`,
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
if (AstUtils.hasContainerOfType(context.node, anyPass([ast.isModelViews, ast.isGlobalStyleGroup]))) {
|
|
106
|
-
return acceptor(context, {
|
|
107
|
-
label: keyword.value,
|
|
108
|
-
detail: `Insert ${keyword.value} block`,
|
|
109
|
-
kind: CompletionItemKind.Module,
|
|
110
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
111
|
-
insertText: `${keyword.value} \${1:*} {\n\t\${2|${STYLE_FIELDS}|} $0\n}`,
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
return acceptor(context, {
|
|
115
|
-
label: keyword.value,
|
|
116
|
-
detail: `Insert ${keyword.value} block`,
|
|
117
|
-
kind: CompletionItemKind.Module,
|
|
118
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
119
|
-
insertText: `${keyword.value} {\n\t\${1|${STYLE_FIELDS}|} $0\n}`,
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
if (keyword.value === 'extend') {
|
|
123
|
-
return acceptor(context, {
|
|
124
|
-
label: keyword.value,
|
|
125
|
-
detail: `Extend another view`,
|
|
126
|
-
kind: CompletionItemKind.Class,
|
|
127
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
128
|
-
insertText: 'extend ${1:element} {\n\t$0\n}',
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (keyword.value === 'autoLayout') {
|
|
133
|
-
return acceptor(context, {
|
|
134
|
-
label: keyword.value,
|
|
135
|
-
kind: CompletionItemKind.Class,
|
|
136
|
-
insertTextFormat: InsertTextFormat.Snippet,
|
|
137
|
-
insertText: 'autoLayout ${1|TopBottom,BottomTop,LeftRight,RightLeft|}$0',
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
acceptor(context, {
|
|
141
|
-
label: keyword.value,
|
|
142
|
-
kind: this.getKeywordCompletionItemKind(keyword),
|
|
143
|
-
detail: 'Keyword',
|
|
144
|
-
sortText: '1',
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ReferenceDescription } from 'langium'
|
|
2
|
-
import { DefaultDocumentHighlightProvider } from 'langium/lsp'
|
|
3
|
-
import { DocumentHighlight, DocumentHighlightKind } from 'vscode-languageserver'
|
|
4
|
-
|
|
5
|
-
export class LikeC4DocumentHighlightProvider extends DefaultDocumentHighlightProvider {
|
|
6
|
-
/**
|
|
7
|
-
* Override this method to determine the highlight kind of the given reference.
|
|
8
|
-
*/
|
|
9
|
-
protected override createDocumentHighlight(reference: ReferenceDescription): DocumentHighlight {
|
|
10
|
-
return DocumentHighlight.create(reference.segment.range, DocumentHighlightKind.Read)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { LangiumDocument, MaybePromise } from 'langium'
|
|
2
|
-
import { AstUtils, GrammarUtils } from 'langium'
|
|
3
|
-
import type { DocumentLinkProvider } from 'langium/lsp'
|
|
4
|
-
import { hasLeadingSlash, hasProtocol, isRelative, withoutBase, withoutLeadingSlash } from 'ufo'
|
|
5
|
-
import type { DocumentLink, DocumentLinkParams } from 'vscode-languageserver'
|
|
6
|
-
import { ast, isParsedLikeC4LangiumDocument } from '../ast'
|
|
7
|
-
import { logWarnError } from '../logger'
|
|
8
|
-
import type { LikeC4Services } from '../module'
|
|
9
|
-
|
|
10
|
-
export class LikeC4DocumentLinkProvider implements DocumentLinkProvider {
|
|
11
|
-
constructor(private services: LikeC4Services) {
|
|
12
|
-
//
|
|
13
|
-
}
|
|
14
|
-
getDocumentLinks(
|
|
15
|
-
doc: LangiumDocument,
|
|
16
|
-
_params: DocumentLinkParams
|
|
17
|
-
): MaybePromise<DocumentLink[]> {
|
|
18
|
-
if (!isParsedLikeC4LangiumDocument(doc)) {
|
|
19
|
-
return []
|
|
20
|
-
}
|
|
21
|
-
return AstUtils.streamAllContents(doc.parseResult.value)
|
|
22
|
-
.filter(ast.isLinkProperty)
|
|
23
|
-
.map((n): DocumentLink | null => {
|
|
24
|
-
try {
|
|
25
|
-
const range = GrammarUtils.findNodeForProperty(n.$cstNode, 'value')?.range
|
|
26
|
-
const target = this.resolveLink(doc, n.value)
|
|
27
|
-
if (range && hasProtocol(target)) {
|
|
28
|
-
return {
|
|
29
|
-
range,
|
|
30
|
-
target
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
} catch (e) {
|
|
34
|
-
logWarnError(e)
|
|
35
|
-
}
|
|
36
|
-
return null
|
|
37
|
-
})
|
|
38
|
-
.nonNullable()
|
|
39
|
-
.toArray()
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
resolveLink(doc: LangiumDocument, link: string): string {
|
|
43
|
-
if (hasProtocol(link) || hasLeadingSlash(link)) {
|
|
44
|
-
return link
|
|
45
|
-
}
|
|
46
|
-
const base = isRelative(link)
|
|
47
|
-
? new URL(doc.uri.toString(true))
|
|
48
|
-
: this.services.shared.workspace.WorkspaceManager.workspaceURL
|
|
49
|
-
return new URL(link, base).toString()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
relativeLink(doc: LangiumDocument, link: string): string | null {
|
|
53
|
-
if (hasLeadingSlash(link)) {
|
|
54
|
-
return withoutLeadingSlash(link)
|
|
55
|
-
}
|
|
56
|
-
if (isRelative(link)) {
|
|
57
|
-
const base = new URL(doc.uri.toString(true))
|
|
58
|
-
const linkURL = new URL(link, base).toString()
|
|
59
|
-
return withoutLeadingSlash(
|
|
60
|
-
withoutBase(linkURL, this.services.shared.workspace.WorkspaceManager.workspaceURL.toString())
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
return null
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import { nonexhaustive } from '@likec4/core'
|
|
2
|
-
import { type AstNode, type MaybePromise, AstUtils, GrammarUtils } from 'langium'
|
|
3
|
-
import type { DocumentSymbolProvider, NodeKindProvider } from 'langium/lsp'
|
|
4
|
-
import { filter, isEmpty, isTruthy, map, pipe } from 'remeda'
|
|
5
|
-
import { type DocumentSymbol, SymbolKind } from 'vscode-languageserver-types'
|
|
6
|
-
import { type LikeC4LangiumDocument, ast } from '../ast'
|
|
7
|
-
import { logError, logWarnError } from '../logger'
|
|
8
|
-
import type { LikeC4ModelLocator, LikeC4ModelParser } from '../model'
|
|
9
|
-
import type { LikeC4Services } from '../module'
|
|
10
|
-
import type { LikeC4NameProvider } from '../references'
|
|
11
|
-
import { getFqnElementRef } from '../utils/elementRef'
|
|
12
|
-
|
|
13
|
-
export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
|
|
14
|
-
protected readonly nodeKindProvider: NodeKindProvider
|
|
15
|
-
protected readonly nameProvider: LikeC4NameProvider
|
|
16
|
-
protected readonly parser: LikeC4ModelParser
|
|
17
|
-
protected readonly locator: LikeC4ModelLocator
|
|
18
|
-
|
|
19
|
-
constructor(private services: LikeC4Services) {
|
|
20
|
-
this.nodeKindProvider = services.shared.lsp.NodeKindProvider
|
|
21
|
-
this.parser = services.likec4.ModelParser
|
|
22
|
-
this.locator = services.likec4.ModelLocator
|
|
23
|
-
this.nameProvider = services.references.NameProvider
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getSymbols({
|
|
27
|
-
parseResult: {
|
|
28
|
-
value: { specifications, models, deployments, views, likec4lib },
|
|
29
|
-
},
|
|
30
|
-
}: LikeC4LangiumDocument): MaybePromise<DocumentSymbol[]> {
|
|
31
|
-
return [
|
|
32
|
-
...likec4lib.map(l => () => this.getLikec4LibSymbol(l)),
|
|
33
|
-
...specifications.map(s => () => this.getSpecSymbol(s)),
|
|
34
|
-
...models.map(s => () => this.getModelSymbol(s)),
|
|
35
|
-
...deployments.map(s => () => this.getDeploymentModelSymbol(s)),
|
|
36
|
-
...views.map(s => () => this.getModelViewsSymbol(s)),
|
|
37
|
-
].flatMap(fn => {
|
|
38
|
-
try {
|
|
39
|
-
return fn() ?? []
|
|
40
|
-
} catch (e) {
|
|
41
|
-
logWarnError(e)
|
|
42
|
-
return []
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
protected getLikec4LibSymbol(astLib: ast.LikeC4Lib): DocumentSymbol[] {
|
|
48
|
-
const cstModel = astLib?.$cstNode
|
|
49
|
-
if (!cstModel) return []
|
|
50
|
-
const children = astLib.icons.map(i => this.getLibIconSymbol(i)).filter(isTruthy)
|
|
51
|
-
if (children.length === 0) return []
|
|
52
|
-
return [
|
|
53
|
-
{
|
|
54
|
-
kind: SymbolKind.Namespace,
|
|
55
|
-
name: 'icons',
|
|
56
|
-
range: cstModel.range,
|
|
57
|
-
selectionRange: GrammarUtils.findNodeForKeyword(cstModel, 'icons')?.range ?? cstModel.range,
|
|
58
|
-
children,
|
|
59
|
-
},
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
protected getSpecSymbol(astSpec: ast.SpecificationRule): DocumentSymbol[] {
|
|
64
|
-
const cstModel = astSpec?.$cstNode
|
|
65
|
-
if (!cstModel) return []
|
|
66
|
-
const specKeywordNode = GrammarUtils.findNodeForProperty(cstModel, 'name')
|
|
67
|
-
if (!specKeywordNode) return []
|
|
68
|
-
|
|
69
|
-
const specSymbols = pipe(
|
|
70
|
-
[...astSpec.elements, ...astSpec.tags, ...astSpec.relationships],
|
|
71
|
-
map(nd => {
|
|
72
|
-
try {
|
|
73
|
-
if (
|
|
74
|
-
ast.isSpecificationElementKind(nd) || ast.isSpecificationRelationshipKind(nd)
|
|
75
|
-
|| ast.isSpecificationDeploymentNodeKind(nd)
|
|
76
|
-
) {
|
|
77
|
-
return this.getKindSymbol(nd)
|
|
78
|
-
}
|
|
79
|
-
if (ast.isSpecificationTag(nd)) {
|
|
80
|
-
return this.getTagSymbol(nd)
|
|
81
|
-
}
|
|
82
|
-
} catch (e) {
|
|
83
|
-
logWarnError(e)
|
|
84
|
-
return null
|
|
85
|
-
}
|
|
86
|
-
nonexhaustive(nd)
|
|
87
|
-
}),
|
|
88
|
-
filter(isTruthy),
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
if (specSymbols.length === 0) return []
|
|
92
|
-
|
|
93
|
-
return [
|
|
94
|
-
{
|
|
95
|
-
kind: SymbolKind.Namespace,
|
|
96
|
-
name: astSpec.name,
|
|
97
|
-
range: cstModel.range,
|
|
98
|
-
selectionRange: specKeywordNode.range,
|
|
99
|
-
children: specSymbols,
|
|
100
|
-
},
|
|
101
|
-
]
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
protected getModelSymbol(astModel: ast.Model): DocumentSymbol[] {
|
|
105
|
-
const cstModel = astModel.$cstNode
|
|
106
|
-
if (!cstModel) return []
|
|
107
|
-
const nameNode = GrammarUtils.findNodeForProperty(cstModel, 'name')
|
|
108
|
-
if (!nameNode) return []
|
|
109
|
-
return [
|
|
110
|
-
{
|
|
111
|
-
kind: this.symbolKind(astModel),
|
|
112
|
-
name: astModel.name,
|
|
113
|
-
range: cstModel.range,
|
|
114
|
-
selectionRange: nameNode.range,
|
|
115
|
-
children: astModel.elements.flatMap(e => this.getElementsSymbol(e)),
|
|
116
|
-
},
|
|
117
|
-
]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
protected getDeploymentModelSymbol(astModel: ast.ModelDeployments): DocumentSymbol[] {
|
|
121
|
-
const cstModel = astModel.$cstNode
|
|
122
|
-
if (!cstModel) return []
|
|
123
|
-
const nameNode = GrammarUtils.findNodeForProperty(cstModel, 'name')
|
|
124
|
-
if (!nameNode) return []
|
|
125
|
-
return [
|
|
126
|
-
{
|
|
127
|
-
kind: this.symbolKind(astModel),
|
|
128
|
-
name: astModel.name,
|
|
129
|
-
range: cstModel.range,
|
|
130
|
-
selectionRange: nameNode.range,
|
|
131
|
-
children: astModel.elements.flatMap(e => this.getDeploymentElementSymbol(e)),
|
|
132
|
-
},
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
protected getElementsSymbol(
|
|
137
|
-
el: ast.Element | ast.Relation | ast.ExtendElement,
|
|
138
|
-
): DocumentSymbol[] {
|
|
139
|
-
try {
|
|
140
|
-
if (ast.isExtendElement(el)) {
|
|
141
|
-
return this.getExtendElementSymbol(el)
|
|
142
|
-
}
|
|
143
|
-
if (ast.isElement(el)) {
|
|
144
|
-
return this.getElementSymbol(el)
|
|
145
|
-
}
|
|
146
|
-
} catch (e) {
|
|
147
|
-
logWarnError(e)
|
|
148
|
-
}
|
|
149
|
-
return []
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
protected getExtendElementSymbol(astElement: ast.ExtendElement): DocumentSymbol[] {
|
|
153
|
-
const cst = astElement.$cstNode
|
|
154
|
-
const nameNode = astElement.element.$cstNode
|
|
155
|
-
const body = astElement.body
|
|
156
|
-
if (!cst || !nameNode) return []
|
|
157
|
-
|
|
158
|
-
return [
|
|
159
|
-
{
|
|
160
|
-
kind: this.symbolKind(astElement),
|
|
161
|
-
name: getFqnElementRef(astElement.element),
|
|
162
|
-
range: cst.range,
|
|
163
|
-
selectionRange: nameNode.range,
|
|
164
|
-
children: body.elements.flatMap(e => this.getElementsSymbol(e)),
|
|
165
|
-
},
|
|
166
|
-
]
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
protected getElementSymbol(astElement: ast.Element): DocumentSymbol[] {
|
|
170
|
-
const cst = astElement.$cstNode
|
|
171
|
-
const nameNode = GrammarUtils.findNodeForProperty(cst, 'name')
|
|
172
|
-
if (!nameNode || !cst) return []
|
|
173
|
-
|
|
174
|
-
const name = astElement.name
|
|
175
|
-
const kind = astElement.kind.$refText
|
|
176
|
-
// TODO: return the title as well
|
|
177
|
-
const detail = kind // + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim()
|
|
178
|
-
return [
|
|
179
|
-
{
|
|
180
|
-
kind: this.symbolKind(astElement),
|
|
181
|
-
name: name,
|
|
182
|
-
range: cst.range,
|
|
183
|
-
selectionRange: nameNode.range,
|
|
184
|
-
detail,
|
|
185
|
-
children: astElement.body?.elements.flatMap(e => this.getElementsSymbol(e)) ?? [],
|
|
186
|
-
},
|
|
187
|
-
]
|
|
188
|
-
}
|
|
189
|
-
protected getModelViewsSymbol(astViews: ast.ModelViews): DocumentSymbol[] {
|
|
190
|
-
const cst = astViews.$cstNode
|
|
191
|
-
const nameNode = GrammarUtils.findNodeForProperty(cst, 'name')
|
|
192
|
-
if (!nameNode || !cst) return []
|
|
193
|
-
return [
|
|
194
|
-
{
|
|
195
|
-
kind: this.symbolKind(astViews),
|
|
196
|
-
name: astViews.name,
|
|
197
|
-
range: cst.range,
|
|
198
|
-
selectionRange: nameNode.range,
|
|
199
|
-
children: astViews.views.flatMap(e => this.getViewSymbol(e)),
|
|
200
|
-
},
|
|
201
|
-
]
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
protected getKindSymbol(
|
|
205
|
-
astKind: ast.SpecificationElementKind | ast.SpecificationRelationshipKind,
|
|
206
|
-
): DocumentSymbol | null {
|
|
207
|
-
if (!astKind.$cstNode || !astKind.kind.$cstNode || isEmpty(astKind.kind.name)) return null
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
kind: this.symbolKind(astKind),
|
|
211
|
-
name: astKind.kind.name,
|
|
212
|
-
range: astKind.$cstNode.range,
|
|
213
|
-
selectionRange: astKind.kind.$cstNode.range,
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
protected getTagSymbol(astTag: ast.SpecificationTag): DocumentSymbol | null {
|
|
218
|
-
if (!astTag.$cstNode || !astTag.tag.$cstNode || isEmpty(astTag.tag.name)) return null
|
|
219
|
-
return {
|
|
220
|
-
kind: this.symbolKind(astTag),
|
|
221
|
-
name: '#' + astTag.tag.name,
|
|
222
|
-
range: astTag.$cstNode.range,
|
|
223
|
-
selectionRange: astTag.tag.$cstNode.range,
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
protected getLibIconSymbol(astTag: ast.LibIcon): DocumentSymbol | null {
|
|
228
|
-
if (!astTag.$cstNode || isEmpty(astTag.name)) return null
|
|
229
|
-
return {
|
|
230
|
-
kind: this.symbolKind(astTag),
|
|
231
|
-
name: astTag.name,
|
|
232
|
-
range: astTag.$cstNode.range,
|
|
233
|
-
selectionRange: astTag.$cstNode.range,
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
protected getViewSymbol(astView: ast.LikeC4View): DocumentSymbol[] {
|
|
238
|
-
const cst = astView?.$cstNode
|
|
239
|
-
if (!cst) return []
|
|
240
|
-
const nameNode = astView.name ? GrammarUtils.findNodeForProperty(cst, 'name') : null
|
|
241
|
-
if (!nameNode) return []
|
|
242
|
-
return [
|
|
243
|
-
{
|
|
244
|
-
kind: this.symbolKind(astView),
|
|
245
|
-
name: nameNode.text,
|
|
246
|
-
range: cst.range,
|
|
247
|
-
selectionRange: nameNode.range,
|
|
248
|
-
children: [],
|
|
249
|
-
},
|
|
250
|
-
]
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
protected getDeploymentElementSymbol(el: ast.DeploymentElement | ast.DeploymentRelation): DocumentSymbol[] {
|
|
254
|
-
try {
|
|
255
|
-
if (ast.isDeploymentNode(el)) {
|
|
256
|
-
return this.getDeploymentNodeSymbol(el)
|
|
257
|
-
}
|
|
258
|
-
if (ast.isDeployedInstance(el)) {
|
|
259
|
-
return this.getDeployedInstanceSymbol(el)
|
|
260
|
-
}
|
|
261
|
-
} catch (e) {
|
|
262
|
-
logWarnError(e)
|
|
263
|
-
}
|
|
264
|
-
return []
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
protected getDeploymentNodeSymbol(astElement: ast.DeploymentNode): DocumentSymbol[] {
|
|
268
|
-
const cst = astElement.$cstNode
|
|
269
|
-
const nameNode = this.nameProvider.getNameNode(astElement)
|
|
270
|
-
if (!nameNode || !cst) return []
|
|
271
|
-
|
|
272
|
-
const name = this.nameProvider.getNameStrict(astElement)
|
|
273
|
-
const kind = astElement.kind.$refText
|
|
274
|
-
// TODO: return the title as well
|
|
275
|
-
const detail = kind // + (astElement.title ? ': ' + astElement.title : '').replaceAll('\n', ' ').trim()
|
|
276
|
-
return [
|
|
277
|
-
{
|
|
278
|
-
kind: this.symbolKind(astElement),
|
|
279
|
-
name: name,
|
|
280
|
-
range: cst.range,
|
|
281
|
-
selectionRange: nameNode.range,
|
|
282
|
-
detail,
|
|
283
|
-
children: astElement.body?.elements.flatMap(e => this.getDeploymentElementSymbol(e)) ?? [],
|
|
284
|
-
},
|
|
285
|
-
]
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
protected getDeployedInstanceSymbol(astElement: ast.DeployedInstance): DocumentSymbol[] {
|
|
289
|
-
const cst = astElement.$cstNode
|
|
290
|
-
const nameNode = this.nameProvider.getNameNode(astElement)
|
|
291
|
-
if (!nameNode || !cst) return []
|
|
292
|
-
|
|
293
|
-
const doc = AstUtils.getDocument(astElement)
|
|
294
|
-
const instance = this.parser.forDocument(doc).parseDeployedInstance(astElement)
|
|
295
|
-
|
|
296
|
-
const name = this.nameProvider.getNameStrict(astElement)
|
|
297
|
-
const detail = 'instance of ' + instance.element
|
|
298
|
-
return [
|
|
299
|
-
{
|
|
300
|
-
kind: this.symbolKind(astElement),
|
|
301
|
-
name: name,
|
|
302
|
-
range: cst.range,
|
|
303
|
-
selectionRange: nameNode.range,
|
|
304
|
-
detail,
|
|
305
|
-
children: [],
|
|
306
|
-
},
|
|
307
|
-
]
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
protected symbolKind(node: AstNode): SymbolKind {
|
|
311
|
-
return this.nodeKindProvider.getSymbolKind(node)
|
|
312
|
-
}
|
|
313
|
-
}
|