@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
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type ValidationCheck, AstUtils } from 'langium'
|
|
2
|
-
import { ast } from '../../ast'
|
|
3
|
-
import type { LikeC4Services } from '../../module'
|
|
4
|
-
import { tryOrLog } from '../_shared'
|
|
5
|
-
|
|
6
|
-
export const relationPredicateWithChecks = (
|
|
7
|
-
_services: LikeC4Services,
|
|
8
|
-
): ValidationCheck<ast.RelationPredicateWith> => {
|
|
9
|
-
return tryOrLog((el, accept) => {
|
|
10
|
-
const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate)
|
|
11
|
-
if (ast.isExcludePredicate(container)) {
|
|
12
|
-
accept('error', 'Invalid usage inside "exclude"', {
|
|
13
|
-
node: el,
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
}
|
package/src/validation/view.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { type ValidationCheck } from 'langium'
|
|
2
|
-
import { ast } from '../ast'
|
|
3
|
-
import type { LikeC4Services } from '../module'
|
|
4
|
-
import { RESERVED_WORDS, tryOrLog } from './_shared'
|
|
5
|
-
|
|
6
|
-
export const viewChecks = (services: LikeC4Services): ValidationCheck<ast.LikeC4View> => {
|
|
7
|
-
const index = services.shared.workspace.IndexManager
|
|
8
|
-
return tryOrLog((el, accept) => {
|
|
9
|
-
// const commentNode = CstUtils.findCommentNode(el.$cstNode, ['BLOCK_COMMENT'])
|
|
10
|
-
// if (commentNode && hasManualLayout(commentNode.text) && !deserializeFromComment(commentNode.text)) {
|
|
11
|
-
// accept('warning', `Malformed @likec4-generated (ignored)`, {
|
|
12
|
-
// node: el,
|
|
13
|
-
// range: commentNode.range
|
|
14
|
-
// })
|
|
15
|
-
// }
|
|
16
|
-
if (!el.name) {
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
if (RESERVED_WORDS.includes(el.name)) {
|
|
20
|
-
accept('error', `Reserved word: ${el.name}`, {
|
|
21
|
-
node: el,
|
|
22
|
-
property: 'name',
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
const anotherViews = index
|
|
26
|
-
.allElements(ast.LikeC4View)
|
|
27
|
-
.filter(n => n.name === el.name)
|
|
28
|
-
.limit(2)
|
|
29
|
-
.count()
|
|
30
|
-
if (anotherViews > 1) {
|
|
31
|
-
accept('error', `Duplicate view '${el.name}'`, {
|
|
32
|
-
node: el,
|
|
33
|
-
property: 'name',
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { ComputedNode, ComputedView, type Fqn, type ViewId } from '@likec4/core'
|
|
2
|
-
import { find, isNullish } from 'remeda'
|
|
3
|
-
|
|
4
|
-
export function assignNavigateTo<R extends Iterable<ComputedView>>(views: R): R {
|
|
5
|
-
const allElementViews = new Map<Fqn, ViewId[]>()
|
|
6
|
-
|
|
7
|
-
for (const v of views) {
|
|
8
|
-
if (ComputedView.isElement(v) && v.viewOf && isNullish(v.extends)) {
|
|
9
|
-
const viewsOf = allElementViews.get(v.viewOf) ?? []
|
|
10
|
-
viewsOf.push(v.id)
|
|
11
|
-
allElementViews.set(v.viewOf, viewsOf)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// set default navigateTo
|
|
16
|
-
for (const { id, nodes } of views) {
|
|
17
|
-
for (const node of nodes) {
|
|
18
|
-
const modelRef = ComputedNode.modelRef(node)
|
|
19
|
-
if (node.navigateTo || !modelRef) {
|
|
20
|
-
continue
|
|
21
|
-
}
|
|
22
|
-
// find first element view that is not the current one
|
|
23
|
-
const navigateTo = find(allElementViews.get(modelRef) ?? [], v => v !== id)
|
|
24
|
-
if (navigateTo) {
|
|
25
|
-
node.navigateTo = navigateTo
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return views
|
|
31
|
-
}
|
package/src/view-utils/index.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import type * as c4 from '@likec4/core'
|
|
2
|
-
import { isAutoLayoutDirection, type ViewManualLayout } from '@likec4/core'
|
|
3
|
-
import { decode, encode } from '@msgpack/msgpack'
|
|
4
|
-
import { fromBase64, toBase64 } from '@smithy/util-base64'
|
|
5
|
-
import { AstUtils, CstUtils } from 'langium'
|
|
6
|
-
import { mapValues } from 'remeda'
|
|
7
|
-
import type { ast } from '../ast'
|
|
8
|
-
import { logger } from '../logger'
|
|
9
|
-
|
|
10
|
-
const { getDocument } = AstUtils
|
|
11
|
-
|
|
12
|
-
function pack({
|
|
13
|
-
nodes,
|
|
14
|
-
edges,
|
|
15
|
-
...rest
|
|
16
|
-
}: ViewManualLayout) {
|
|
17
|
-
return {
|
|
18
|
-
...rest,
|
|
19
|
-
nodes: mapValues(nodes, ({ x, y, width, height, isCompound, ...n }) => ({
|
|
20
|
-
...n,
|
|
21
|
-
b: [x, y, width, height] as const,
|
|
22
|
-
c: isCompound
|
|
23
|
-
})),
|
|
24
|
-
edges: mapValues(edges, ({ points, controlPoints, labelBBox, dotpos, ...e }) => ({
|
|
25
|
-
...!!controlPoints && { cp: controlPoints },
|
|
26
|
-
...!!labelBBox && { l: labelBBox },
|
|
27
|
-
...!!dotpos && { dp: dotpos },
|
|
28
|
-
...e,
|
|
29
|
-
p: points
|
|
30
|
-
}))
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function unpack({
|
|
35
|
-
nodes,
|
|
36
|
-
edges,
|
|
37
|
-
autoLayout,
|
|
38
|
-
...rest
|
|
39
|
-
}: ReturnType<typeof pack>): ViewManualLayout {
|
|
40
|
-
return {
|
|
41
|
-
...rest,
|
|
42
|
-
/// Try to parse the old format for backward compatibility
|
|
43
|
-
autoLayout: isAutoLayoutDirection(autoLayout) ? { direction: autoLayout } : autoLayout,
|
|
44
|
-
nodes: mapValues(nodes, ({ b, c, ...n }) => ({
|
|
45
|
-
x: b[0],
|
|
46
|
-
y: b[1],
|
|
47
|
-
width: b[2],
|
|
48
|
-
height: b[3],
|
|
49
|
-
isCompound: c,
|
|
50
|
-
...n
|
|
51
|
-
})),
|
|
52
|
-
edges: mapValues(edges, ({ p, cp, l, dp, ...e }) => ({
|
|
53
|
-
...!!cp && { controlPoints: cp },
|
|
54
|
-
...!!l && { labelBBox: l },
|
|
55
|
-
...!!dp && { dotpos: dp },
|
|
56
|
-
...e,
|
|
57
|
-
points: p
|
|
58
|
-
}))
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const MAX_LINE_LENGTH = 500
|
|
63
|
-
export function serializeToComment(layout: ViewManualLayout) {
|
|
64
|
-
const bytes = encode(pack(layout))
|
|
65
|
-
const base64 = toBase64(bytes)
|
|
66
|
-
const lines = [] as string[]
|
|
67
|
-
let offset = 0
|
|
68
|
-
while (offset < base64.length) {
|
|
69
|
-
lines.push(' * ' + base64.slice(offset, Math.min(offset + MAX_LINE_LENGTH, base64.length)))
|
|
70
|
-
offset += MAX_LINE_LENGTH
|
|
71
|
-
}
|
|
72
|
-
lines.unshift(
|
|
73
|
-
'/**',
|
|
74
|
-
' * @likec4-generated(v1)'
|
|
75
|
-
)
|
|
76
|
-
lines.push(' */')
|
|
77
|
-
|
|
78
|
-
return lines.join('\n')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function hasManualLayout(comment: string) {
|
|
82
|
-
return comment.includes('@likec4-generated')
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function deserializeFromComment(comment: string): ViewManualLayout {
|
|
86
|
-
if (!hasManualLayout(comment)) {
|
|
87
|
-
throw new Error(`Not a likec4-generated comment: ${comment}`)
|
|
88
|
-
}
|
|
89
|
-
const b64 = comment
|
|
90
|
-
.trim()
|
|
91
|
-
.split('\n')
|
|
92
|
-
.filter(l => !l.includes('**') && !l.includes('@likec4-') && !l.includes('*/'))
|
|
93
|
-
.map(l => l.replaceAll('*', '').trim())
|
|
94
|
-
.join('')
|
|
95
|
-
const decodedb64 = fromBase64(b64)
|
|
96
|
-
return unpack(decode(decodedb64) as any) as ViewManualLayout
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function parseViewManualLayout(node: ast.LikeC4View): c4.ViewManualLayout | undefined {
|
|
100
|
-
const commentNode = CstUtils.findCommentNode(node.$cstNode, ['BLOCK_COMMENT'])
|
|
101
|
-
if (!commentNode || !hasManualLayout(commentNode.text)) {
|
|
102
|
-
return undefined
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
return deserializeFromComment(commentNode.text)
|
|
106
|
-
} catch (e) {
|
|
107
|
-
const doc = getDocument(node)
|
|
108
|
-
logger.warn(e)
|
|
109
|
-
logger.warn(
|
|
110
|
-
`Ignoring manual layout of "${node.name ?? 'unnamed'}" at ${doc.uri.fsPath}:${
|
|
111
|
-
1 + (commentNode.range.start.line || 0)
|
|
112
|
-
}`
|
|
113
|
-
)
|
|
114
|
-
return undefined
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { LikeC4View } from '@likec4/core'
|
|
2
|
-
import { compareNatural, invariant } from '@likec4/core'
|
|
3
|
-
import { filter, hasAtLeast, isTruthy, map, pipe, unique } from 'remeda'
|
|
4
|
-
import { parsePath } from 'ufo'
|
|
5
|
-
|
|
6
|
-
function commonAncestorPath(views: LikeC4View[], sep = '/') {
|
|
7
|
-
const uniqURIs = pipe(
|
|
8
|
-
views,
|
|
9
|
-
map(v => v.docUri),
|
|
10
|
-
filter(isTruthy),
|
|
11
|
-
unique()
|
|
12
|
-
)
|
|
13
|
-
if (uniqURIs.length === 0) return ''
|
|
14
|
-
if (uniqURIs.length === 1) {
|
|
15
|
-
const parts = parsePath(uniqURIs[0]).pathname.split(sep)
|
|
16
|
-
if (parts.length <= 1) return sep
|
|
17
|
-
parts.pop() // remove filename
|
|
18
|
-
return parts.join(sep) + sep
|
|
19
|
-
}
|
|
20
|
-
invariant(hasAtLeast(uniqURIs, 2), 'Expected at least 2 unique URIs')
|
|
21
|
-
const [baseUri, ...tail] = uniqURIs
|
|
22
|
-
const parts = parsePath(baseUri).pathname.split(sep)
|
|
23
|
-
let endOfPrefix = parts.length
|
|
24
|
-
for (const uri of tail) {
|
|
25
|
-
if (uri === baseUri) {
|
|
26
|
-
continue
|
|
27
|
-
}
|
|
28
|
-
const compare = parsePath(uri).pathname.split(sep)
|
|
29
|
-
for (let i = 0; i < endOfPrefix; i++) {
|
|
30
|
-
if (compare[i] !== parts[i]) {
|
|
31
|
-
endOfPrefix = i
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (endOfPrefix === 0) return ''
|
|
35
|
-
}
|
|
36
|
-
const prefix = parts.slice(0, endOfPrefix).join(sep)
|
|
37
|
-
return prefix.endsWith(sep) ? prefix : prefix + sep
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function resolveRelativePaths(views: LikeC4View[]): LikeC4View[] {
|
|
41
|
-
const sep = '/'
|
|
42
|
-
const commonPrefix = commonAncestorPath(views, sep)
|
|
43
|
-
return (
|
|
44
|
-
views
|
|
45
|
-
// For each view, compute the relative path to the common prefix
|
|
46
|
-
// Store array of path segments
|
|
47
|
-
.map(view => {
|
|
48
|
-
if (!view.docUri) {
|
|
49
|
-
return {
|
|
50
|
-
view,
|
|
51
|
-
parts: []
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
let path = parsePath(view.docUri).pathname
|
|
55
|
-
if (commonPrefix.length > 0) {
|
|
56
|
-
invariant(
|
|
57
|
-
path.startsWith(commonPrefix),
|
|
58
|
-
`Expect path "${path}" to start with common prefix: "${commonPrefix}"`
|
|
59
|
-
)
|
|
60
|
-
path = path.slice(commonPrefix.length)
|
|
61
|
-
} else {
|
|
62
|
-
path = path.includes(sep) ? path.slice(path.lastIndexOf(sep) + 1) : path
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
view,
|
|
66
|
-
parts: path.split(sep)
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
// Sort views by path segments
|
|
70
|
-
.sort((a, b) => {
|
|
71
|
-
if (a.parts.length !== b.parts.length) {
|
|
72
|
-
return a.parts.length - b.parts.length
|
|
73
|
-
}
|
|
74
|
-
for (let i = 0; i < a.parts.length; i++) {
|
|
75
|
-
const compare = compareNatural(a.parts[i], b.parts[i])
|
|
76
|
-
if (compare !== 0) {
|
|
77
|
-
return compare
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return compareNatural(a.view.title ?? a.view.id, b.view.title ?? b.view.id)
|
|
81
|
-
})
|
|
82
|
-
// Build relativePath from path segments
|
|
83
|
-
.map(({ parts, view }) => {
|
|
84
|
-
return {
|
|
85
|
-
...view,
|
|
86
|
-
relativePath: parts.join(sep)
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
)
|
|
90
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { GraphvizLayouter, GraphvizWasmAdapter } from '@likec4/layouts'
|
|
2
|
-
import { GraphvizBinaryAdapter } from '@likec4/layouts/graphviz/binary'
|
|
3
|
-
import { isEmpty } from 'remeda'
|
|
4
|
-
import which from 'which'
|
|
5
|
-
import { logger } from '../logger'
|
|
6
|
-
import type { LikeC4Services } from '../module'
|
|
7
|
-
|
|
8
|
-
function graphvizBinPath() {
|
|
9
|
-
try {
|
|
10
|
-
return which.sync('dot')
|
|
11
|
-
} catch (error) {
|
|
12
|
-
logger.error('Error checking for native Graphviz:', error)
|
|
13
|
-
return null
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const ConfigurableLayouter = {
|
|
18
|
-
likec4: {
|
|
19
|
-
Layouter(services: LikeC4Services): GraphvizLayouter {
|
|
20
|
-
logger.debug('Creating ConfigurableLayouter')
|
|
21
|
-
const wasmAdapter = new GraphvizWasmAdapter()
|
|
22
|
-
const layouter = new GraphvizLayouter(wasmAdapter)
|
|
23
|
-
const langId = services.LanguageMetaData.languageId
|
|
24
|
-
services.shared.workspace.ConfigurationProvider.onConfigurationSectionUpdate((update) => {
|
|
25
|
-
logger.debug('Configuration update', update)
|
|
26
|
-
if (update.section === langId) {
|
|
27
|
-
try {
|
|
28
|
-
const { mode, path } = update.configuration.graphviz ?? {
|
|
29
|
-
mode: 'wasm',
|
|
30
|
-
path: '',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (mode === 'wasm') {
|
|
34
|
-
layouter.changePort(wasmAdapter)
|
|
35
|
-
logger.info('use graphviz wasm')
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
let binaryPath = isEmpty(path) ? graphvizBinPath() : path
|
|
40
|
-
|
|
41
|
-
if (binaryPath === null) {
|
|
42
|
-
layouter.changePort(wasmAdapter)
|
|
43
|
-
logger.warn(`No Graphviz binaries found on PATH, use graphviz wasm`)
|
|
44
|
-
services.shared.lsp.Connection?.window.showWarningMessage(
|
|
45
|
-
'No Graphviz binaries found on PATH, set path to binaries in settings.',
|
|
46
|
-
)
|
|
47
|
-
return
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
layouter.changePort(new GraphvizBinaryAdapter(binaryPath))
|
|
51
|
-
|
|
52
|
-
logger.info(`use graphviz binary: ${binaryPath}`)
|
|
53
|
-
} catch (error) {
|
|
54
|
-
logger.error('Failed to update configuration', error)
|
|
55
|
-
}
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
logger.warn('Unexpected configuration update', update)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
return layouter
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
}
|
package/src/views/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { LikeC4Views } from './likec4-views'
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import type { ComputedView, DiagramView, OverviewGraph, ViewId } from '@likec4/core'
|
|
2
|
-
import { GraphvizLayouter } from '@likec4/layouts'
|
|
3
|
-
import { type Cancellation, type WorkspaceCache } from 'langium'
|
|
4
|
-
import { values } from 'remeda'
|
|
5
|
-
import { logger, logWarnError } from '../logger'
|
|
6
|
-
import type { LikeC4Services } from '../module'
|
|
7
|
-
|
|
8
|
-
export type GraphvizOut = {
|
|
9
|
-
dot: string
|
|
10
|
-
diagram: DiagramView
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type GraphvizSvgOut = {
|
|
14
|
-
id: ViewId
|
|
15
|
-
dot: string
|
|
16
|
-
svg: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class LikeC4Views {
|
|
20
|
-
private cache = new WeakMap<ComputedView, GraphvizOut>()
|
|
21
|
-
|
|
22
|
-
private viewsWithReportedErrors = new Set<ViewId>()
|
|
23
|
-
|
|
24
|
-
constructor(private services: LikeC4Services) {
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
private get layouter(): GraphvizLayouter {
|
|
28
|
-
return this.services.likec4.Layouter
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async computedViews(cancelToken?: Cancellation.CancellationToken): Promise<ComputedView[]> {
|
|
32
|
-
const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken)
|
|
33
|
-
return model ? values(model.views) : []
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async layoutAllViews(cancelToken?: Cancellation.CancellationToken): Promise<Array<Readonly<GraphvizOut>>> {
|
|
37
|
-
const views = await this.computedViews(cancelToken)
|
|
38
|
-
if (views.length === 0) {
|
|
39
|
-
return []
|
|
40
|
-
}
|
|
41
|
-
const results = [] as GraphvizOut[]
|
|
42
|
-
const tasks = [] as Promise<GraphvizOut>[]
|
|
43
|
-
for (const view of views) {
|
|
44
|
-
this.viewsWithReportedErrors.delete(view.id)
|
|
45
|
-
tasks.push(
|
|
46
|
-
this.layouter.layout(view)
|
|
47
|
-
.then(result => {
|
|
48
|
-
this.cache.set(view, result)
|
|
49
|
-
return result
|
|
50
|
-
})
|
|
51
|
-
.catch(e => {
|
|
52
|
-
this.cache.delete(view)
|
|
53
|
-
logWarnError(e)
|
|
54
|
-
return Promise.reject(e)
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
for (const task of await Promise.allSettled(tasks)) {
|
|
59
|
-
if (task.status === 'fulfilled') {
|
|
60
|
-
results.push(task.value)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return results
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async layoutView(viewId: ViewId, cancelToken?: Cancellation.CancellationToken): Promise<GraphvizOut | null> {
|
|
67
|
-
const model = await this.services.likec4.ModelBuilder.buildComputedModel(cancelToken)
|
|
68
|
-
if (!model) {
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
const view = model.views[viewId]
|
|
72
|
-
if (!view) {
|
|
73
|
-
return null
|
|
74
|
-
}
|
|
75
|
-
let cached = this.cache.get(view)
|
|
76
|
-
if (cached) {
|
|
77
|
-
return cached
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
const result = await this.layouter.layout(view)
|
|
81
|
-
this.viewsWithReportedErrors.delete(viewId)
|
|
82
|
-
this.cache.set(view, result)
|
|
83
|
-
return result
|
|
84
|
-
} catch (e) {
|
|
85
|
-
if (!this.viewsWithReportedErrors.has(viewId)) {
|
|
86
|
-
const errMessage = e instanceof Error ? e.message : '' + e
|
|
87
|
-
this.services.shared.lsp.Connection?.window.showErrorMessage(`LikeC4: ${errMessage}`)
|
|
88
|
-
this.viewsWithReportedErrors.add(viewId)
|
|
89
|
-
}
|
|
90
|
-
logger.error(e)
|
|
91
|
-
return Promise.reject(e)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async diagrams(): Promise<Array<DiagramView>> {
|
|
96
|
-
const layouted = await this.layoutAllViews()
|
|
97
|
-
return layouted.map(l => l.diagram)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async viewsAsGraphvizOut(): Promise<Array<GraphvizSvgOut>> {
|
|
101
|
-
const KEY = 'All-LayoutedViews-DotWithSvg'
|
|
102
|
-
const cache = this.services.WorkspaceCache as WorkspaceCache<string, GraphvizSvgOut[]>
|
|
103
|
-
if (cache.has(KEY)) {
|
|
104
|
-
return await Promise.resolve(cache.get(KEY)!)
|
|
105
|
-
}
|
|
106
|
-
const views = await this.computedViews()
|
|
107
|
-
const tasks = views.map(async view => {
|
|
108
|
-
const { dot, svg } = await this.layouter.svg(view)
|
|
109
|
-
return {
|
|
110
|
-
id: view.id,
|
|
111
|
-
dot,
|
|
112
|
-
svg,
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
const succeed = [] as GraphvizSvgOut[]
|
|
116
|
-
const settledResult = await Promise.allSettled(tasks)
|
|
117
|
-
for (const result of settledResult) {
|
|
118
|
-
if (result.status === 'fulfilled') {
|
|
119
|
-
succeed.push(result.value)
|
|
120
|
-
} else {
|
|
121
|
-
logWarnError(result.reason)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
cache.set(KEY, succeed)
|
|
125
|
-
return succeed
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async overviewGraph(): Promise<OverviewGraph> {
|
|
129
|
-
const KEY = 'OverviewGraph'
|
|
130
|
-
const cache = this.services.WorkspaceCache as WorkspaceCache<string, OverviewGraph>
|
|
131
|
-
if (cache.has(KEY)) {
|
|
132
|
-
return await Promise.resolve(cache.get(KEY)!)
|
|
133
|
-
}
|
|
134
|
-
const views = await this.computedViews()
|
|
135
|
-
const overviewGraph = await this.layouter.layoutOverviewGraph(views)
|
|
136
|
-
cache.set(KEY, overviewGraph)
|
|
137
|
-
return overviewGraph
|
|
138
|
-
}
|
|
139
|
-
}
|