@hydranium/cli 1.0.0-next.42 → 1.0.0-next.421
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 +125 -204
- package/bin/hydranium-cli.js +13 -0
- package/heap-analysis/heap/core/load.mjs +2 -2
- package/heap-analysis/heap/langium/langium.classifier.mjs +22 -11
- package/lib/cli-version.js.map +1 -1
- package/lib/cli.js +11 -2
- package/lib/cli.js.map +1 -1
- package/lib/commands/analyze-heap-args.d.ts.map +1 -1
- package/lib/commands/analyze-heap-args.js +1 -2
- package/lib/commands/analyze-heap-args.js.map +1 -1
- package/lib/commands/analyze-heap.d.ts +3 -0
- package/lib/commands/analyze-heap.d.ts.map +1 -1
- package/lib/commands/analyze-heap.js +2 -1
- package/lib/commands/analyze-heap.js.map +1 -1
- package/lib/commands/ast-ground-truth-args.d.ts +3 -2
- package/lib/commands/ast-ground-truth-args.d.ts.map +1 -1
- package/lib/commands/ast-ground-truth-args.js +12 -10
- package/lib/commands/ast-ground-truth-args.js.map +1 -1
- package/lib/commands/ast-ground-truth-driver.js +4 -2
- package/lib/commands/ast-ground-truth-driver.js.map +1 -1
- package/lib/commands/ast-ground-truth.js +1 -1
- package/lib/commands/ast-ground-truth.js.map +1 -1
- package/lib/commands/check-skills-args.d.ts +11 -0
- package/lib/commands/check-skills-args.d.ts.map +1 -0
- package/lib/commands/check-skills-args.js +71 -0
- package/lib/commands/check-skills-args.js.map +1 -0
- package/lib/commands/client-session.d.ts +17 -0
- package/lib/commands/client-session.d.ts.map +1 -0
- package/lib/commands/client-session.js +27 -0
- package/lib/commands/client-session.js.map +1 -0
- package/lib/commands/create-args.d.ts +17 -0
- package/lib/commands/create-args.d.ts.map +1 -0
- package/lib/commands/create-args.js +53 -0
- package/lib/commands/create-args.js.map +1 -0
- package/lib/commands/create.d.ts +38 -0
- package/lib/commands/create.d.ts.map +1 -0
- package/lib/commands/create.js +43 -0
- package/lib/commands/create.js.map +1 -0
- package/lib/commands/delete-args.d.ts +17 -0
- package/lib/commands/delete-args.d.ts.map +1 -0
- package/lib/commands/delete-args.js +49 -0
- package/lib/commands/delete-args.js.map +1 -0
- package/lib/commands/delete.d.ts +30 -0
- package/lib/commands/delete.d.ts.map +1 -0
- package/lib/commands/delete.js +32 -0
- package/lib/commands/delete.js.map +1 -0
- package/lib/commands/generate-ast-builder.d.ts +1 -1
- package/lib/commands/generate-ast-builder.d.ts.map +1 -1
- package/lib/commands/generate-ast-builder.js +2 -11
- package/lib/commands/generate-ast-builder.js.map +1 -1
- package/lib/commands/generate-languages.d.ts +35 -0
- package/lib/commands/generate-languages.d.ts.map +1 -0
- package/lib/commands/generate-languages.js +105 -0
- package/lib/commands/generate-languages.js.map +1 -0
- package/lib/commands/generate-transfer-builder.d.ts +33 -0
- package/lib/commands/generate-transfer-builder.d.ts.map +1 -0
- package/lib/commands/generate-transfer-builder.js +86 -0
- package/lib/commands/generate-transfer-builder.js.map +1 -0
- package/lib/commands/generate-transfer-model-args.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model-args.js +20 -5
- package/lib/commands/generate-transfer-model-args.js.map +1 -1
- package/lib/commands/generate-transfer-model-config.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model-config.js +8 -3
- package/lib/commands/generate-transfer-model-config.js.map +1 -1
- package/lib/commands/generate-transfer-model.d.ts +41 -9
- package/lib/commands/generate-transfer-model.d.ts.map +1 -1
- package/lib/commands/generate-transfer-model.js +225 -68
- package/lib/commands/generate-transfer-model.js.map +1 -1
- package/lib/commands/generated-module-path.d.ts +16 -0
- package/lib/commands/generated-module-path.d.ts.map +1 -0
- package/lib/commands/generated-module-path.js +21 -0
- package/lib/commands/generated-module-path.js.map +1 -0
- package/lib/commands/harness-args.d.ts +46 -43
- package/lib/commands/harness-args.d.ts.map +1 -1
- package/lib/commands/harness-args.js +170 -29
- package/lib/commands/harness-args.js.map +1 -1
- package/lib/commands/headless-harness.d.ts +37 -20
- package/lib/commands/headless-harness.d.ts.map +1 -1
- package/lib/commands/headless-harness.js +71 -19
- package/lib/commands/headless-harness.js.map +1 -1
- package/lib/commands/init-args.d.ts +5 -18
- package/lib/commands/init-args.d.ts.map +1 -1
- package/lib/commands/init-args.js +131 -35
- package/lib/commands/init-args.js.map +1 -1
- package/lib/commands/init-hosts.d.ts +24 -0
- package/lib/commands/init-hosts.d.ts.map +1 -0
- package/lib/commands/init-hosts.js +37 -0
- package/lib/commands/init-hosts.js.map +1 -0
- package/lib/commands/init-prompt.d.ts +3 -3
- package/lib/commands/init-prompt.d.ts.map +1 -1
- package/lib/commands/init-prompt.js +4 -5
- package/lib/commands/init-prompt.js.map +1 -1
- package/lib/commands/init-skills.d.ts +41 -0
- package/lib/commands/init-skills.d.ts.map +1 -0
- package/lib/commands/init-skills.js +110 -0
- package/lib/commands/init-skills.js.map +1 -0
- package/lib/commands/init-templates.d.ts +20 -14
- package/lib/commands/init-templates.d.ts.map +1 -1
- package/lib/commands/init-templates.js +3134 -763
- package/lib/commands/init-templates.js.map +1 -1
- package/lib/commands/init-wizard.d.ts +10 -0
- package/lib/commands/init-wizard.d.ts.map +1 -1
- package/lib/commands/init-wizard.js +76 -31
- package/lib/commands/init-wizard.js.map +1 -1
- package/lib/commands/init-workspace.d.ts +13 -11
- package/lib/commands/init-workspace.d.ts.map +1 -1
- package/lib/commands/init-workspace.js +21 -7
- package/lib/commands/init-workspace.js.map +1 -1
- package/lib/commands/init.d.ts +61 -24
- package/lib/commands/init.d.ts.map +1 -1
- package/lib/commands/init.js +244 -50
- package/lib/commands/init.js.map +1 -1
- package/lib/commands/kebab.d.ts +15 -0
- package/lib/commands/kebab.d.ts.map +1 -0
- package/lib/commands/kebab.js +17 -0
- package/lib/commands/kebab.js.map +1 -0
- package/lib/commands/lint-grammar-args.d.ts +3 -2
- package/lib/commands/lint-grammar-args.d.ts.map +1 -1
- package/lib/commands/lint-grammar-args.js +14 -11
- package/lib/commands/lint-grammar-args.js.map +1 -1
- package/lib/commands/lint-grammar-driver.js +2 -1
- package/lib/commands/lint-grammar-driver.js.map +1 -1
- package/lib/commands/lint-grammar-report.js.map +1 -1
- package/lib/commands/lint-grammar.js +1 -1
- package/lib/commands/lint-grammar.js.map +1 -1
- package/lib/commands/measure-memory-args.d.ts +3 -2
- package/lib/commands/measure-memory-args.d.ts.map +1 -1
- package/lib/commands/measure-memory-args.js +26 -14
- package/lib/commands/measure-memory-args.js.map +1 -1
- package/lib/commands/measure-memory-driver.js +5 -3
- package/lib/commands/measure-memory-driver.js.map +1 -1
- package/lib/commands/measure-memory.d.ts +1 -1
- package/lib/commands/measure-memory.d.ts.map +1 -1
- package/lib/commands/measure-memory.js +6 -3
- package/lib/commands/measure-memory.js.map +1 -1
- package/lib/commands/message-catalogue-args.d.ts +16 -0
- package/lib/commands/message-catalogue-args.d.ts.map +1 -0
- package/lib/commands/message-catalogue-args.js +53 -0
- package/lib/commands/message-catalogue-args.js.map +1 -0
- package/lib/commands/message-catalogue.d.ts +21 -0
- package/lib/commands/message-catalogue.d.ts.map +1 -0
- package/lib/commands/message-catalogue.js +48 -0
- package/lib/commands/message-catalogue.js.map +1 -0
- package/lib/commands/model-docs-args.d.ts +4 -3
- package/lib/commands/model-docs-args.d.ts.map +1 -1
- package/lib/commands/model-docs-args.js +13 -11
- package/lib/commands/model-docs-args.js.map +1 -1
- package/lib/commands/model-docs-driver.js +2 -1
- package/lib/commands/model-docs-driver.js.map +1 -1
- package/lib/commands/model-docs.js +1 -1
- package/lib/commands/model-docs.js.map +1 -1
- package/lib/commands/projects-args.d.ts.map +1 -1
- package/lib/commands/projects-args.js +5 -5
- package/lib/commands/projects-args.js.map +1 -1
- package/lib/commands/projects.d.ts +3 -4
- package/lib/commands/projects.d.ts.map +1 -1
- package/lib/commands/projects.js +2 -2
- package/lib/commands/projects.js.map +1 -1
- package/lib/commands/query-args.d.ts.map +1 -1
- package/lib/commands/query-args.js +6 -5
- package/lib/commands/query-args.js.map +1 -1
- package/lib/commands/query.d.ts +6 -5
- package/lib/commands/query.d.ts.map +1 -1
- package/lib/commands/query.js +5 -4
- package/lib/commands/query.js.map +1 -1
- package/lib/commands/reflect-args.d.ts +3 -2
- package/lib/commands/reflect-args.d.ts.map +1 -1
- package/lib/commands/reflect-args.js +14 -11
- package/lib/commands/reflect-args.js.map +1 -1
- package/lib/commands/reflect-driver.js +2 -1
- package/lib/commands/reflect-driver.js.map +1 -1
- package/lib/commands/reflect-report.js.map +1 -1
- package/lib/commands/reflect.js +1 -1
- package/lib/commands/reflect.js.map +1 -1
- package/lib/commands/save-args.d.ts.map +1 -1
- package/lib/commands/save-args.js +7 -4
- package/lib/commands/save-args.js.map +1 -1
- package/lib/commands/save.d.ts +8 -4
- package/lib/commands/save.d.ts.map +1 -1
- package/lib/commands/save.js +12 -23
- package/lib/commands/save.js.map +1 -1
- package/lib/commands/skills.d.ts +22 -0
- package/lib/commands/skills.d.ts.map +1 -0
- package/lib/commands/skills.js +129 -0
- package/lib/commands/skills.js.map +1 -0
- package/lib/commands/validate-args.d.ts +3 -2
- package/lib/commands/validate-args.d.ts.map +1 -1
- package/lib/commands/validate-args.js +14 -11
- package/lib/commands/validate-args.js.map +1 -1
- package/lib/commands/validate-driver.js +2 -1
- package/lib/commands/validate-driver.js.map +1 -1
- package/lib/commands/validate-report.js.map +1 -1
- package/lib/commands/validate.js +1 -1
- package/lib/commands/validate.js.map +1 -1
- package/lib/commands/watch-args.d.ts.map +1 -1
- package/lib/commands/watch-args.js +6 -4
- package/lib/commands/watch-args.js.map +1 -1
- package/lib/commands/watch.d.ts +3 -4
- package/lib/commands/watch.d.ts.map +1 -1
- package/lib/commands/watch.js +5 -2
- package/lib/commands/watch.js.map +1 -1
- package/lib/driver-heap.d.ts +52 -0
- package/lib/driver-heap.d.ts.map +1 -0
- package/lib/driver-heap.js +51 -0
- package/lib/driver-heap.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/log-level.d.ts +21 -11
- package/lib/log-level.d.ts.map +1 -1
- package/lib/log-level.js +25 -14
- package/lib/log-level.js.map +1 -1
- package/lib/spawn-data-server.d.ts +9 -4
- package/lib/spawn-data-server.d.ts.map +1 -1
- package/lib/spawn-data-server.js +5 -4
- package/lib/spawn-data-server.js.map +1 -1
- package/lib/stderr-connection-logger.d.ts +12 -0
- package/lib/stderr-connection-logger.d.ts.map +1 -0
- package/lib/stderr-connection-logger.js +16 -0
- package/lib/stderr-connection-logger.js.map +1 -0
- package/lib/testing/echo-server.js +9 -1
- package/lib/testing/echo-server.js.map +1 -1
- package/package.json +99 -41
- package/src/cli.ts +11 -2
- package/src/commands/analyze-heap-args.ts +1 -2
- package/src/commands/analyze-heap.ts +4 -1
- package/src/commands/ast-ground-truth-args.ts +16 -13
- package/src/commands/ast-ground-truth-driver.ts +4 -2
- package/src/commands/ast-ground-truth.ts +1 -1
- package/src/commands/check-skills-args.ts +70 -0
- package/src/commands/client-session.ts +33 -0
- package/src/commands/create-args.ts +68 -0
- package/src/commands/create.ts +83 -0
- package/src/commands/delete-args.ts +64 -0
- package/src/commands/delete.ts +61 -0
- package/src/commands/generate-ast-builder.ts +2 -12
- package/src/commands/generate-languages.ts +132 -0
- package/src/commands/generate-transfer-builder.ts +101 -0
- package/src/commands/generate-transfer-model-args.ts +20 -5
- package/src/commands/generate-transfer-model-config.ts +8 -3
- package/src/commands/generate-transfer-model.ts +297 -66
- package/src/commands/generated-module-path.ts +22 -0
- package/src/commands/harness-args.ts +163 -33
- package/src/commands/headless-harness.ts +89 -28
- package/src/commands/init-args.ts +135 -35
- package/src/commands/init-hosts.ts +51 -0
- package/src/commands/init-prompt.ts +5 -6
- package/src/commands/init-skills.ts +129 -0
- package/src/commands/init-templates.ts +3326 -785
- package/src/commands/init-wizard.ts +97 -32
- package/src/commands/init-workspace.ts +31 -16
- package/src/commands/init.ts +311 -65
- package/src/commands/kebab.ts +17 -0
- package/src/commands/lint-grammar-args.ts +18 -14
- package/src/commands/lint-grammar-driver.ts +2 -1
- package/src/commands/lint-grammar.ts +1 -1
- package/src/commands/measure-memory-args.ts +31 -17
- package/src/commands/measure-memory-driver.ts +5 -3
- package/src/commands/measure-memory.ts +6 -3
- package/src/commands/message-catalogue-args.ts +55 -0
- package/src/commands/message-catalogue.ts +56 -0
- package/src/commands/model-docs-args.ts +17 -14
- package/src/commands/model-docs-driver.ts +2 -1
- package/src/commands/model-docs.ts +1 -1
- package/src/commands/projects-args.ts +5 -4
- package/src/commands/projects.ts +4 -6
- package/src/commands/query-args.ts +6 -4
- package/src/commands/query.ts +9 -9
- package/src/commands/reflect-args.ts +18 -14
- package/src/commands/reflect-driver.ts +2 -1
- package/src/commands/reflect.ts +1 -1
- package/src/commands/save-args.ts +7 -3
- package/src/commands/save.ts +24 -27
- package/src/commands/skills.ts +146 -0
- package/src/commands/validate-args.ts +18 -14
- package/src/commands/validate-driver.ts +2 -1
- package/src/commands/validate.ts +1 -1
- package/src/commands/watch-args.ts +6 -3
- package/src/commands/watch.ts +7 -6
- package/src/driver-heap.ts +65 -0
- package/src/index.ts +3 -0
- package/src/log-level.ts +42 -14
- package/src/spawn-data-server.ts +17 -5
- package/src/stderr-connection-logger.ts +18 -0
- package/src/testing/echo-server.ts +11 -2
- package/templates/client/gitignore +3 -0
- package/templates/client/src/__GRAMMAR_ID__-diagram-module.ts +82 -0
- package/templates/editorconfig +15 -0
- package/templates/gitignore +1 -0
- package/templates/host-browser/esbuild.mjs +82 -0
- package/templates/host-browser/gitignore +4 -0
- package/templates/host-browser/hydranium-mark.svg +40 -0
- package/templates/host-browser/index.html +287 -0
- package/templates/host-browser/playwright.config.mts +24 -0
- package/templates/host-browser/src/data-status.ts +15 -0
- package/templates/host-browser/src/diagram-save.ts +60 -0
- package/templates/host-browser/src/diagram.ts +121 -0
- package/templates/host-browser/src/editor.ts +116 -0
- package/templates/host-browser/src/log.ts +14 -0
- package/templates/host-browser/src/monaco-editor-core-worker.d.ts +7 -0
- package/templates/host-browser/src/monaco-editor-worker.ts +10 -0
- package/templates/host-browser/src/posix-path-shim.ts +23 -0
- package/templates/host-browser/src/shown-file.ts +61 -0
- package/templates/host-browser/src/workspace.ts +5 -0
- package/templates/host-browser/tsconfig.e2e.json +16 -0
- package/templates/host-browser/tsconfig.page.json +16 -0
- package/templates/host-browser/tsconfig.worker.json +16 -0
- package/templates/host-theia-browser/esbuild.mjs +24 -0
- package/templates/host-theia-browser/gitignore +9 -0
- package/templates/host-theia-browser/playwright.config.mts +42 -0
- package/templates/host-theia-browser/start.mjs +50 -0
- package/templates/host-theia-browser/tsconfig.e2e.json +16 -0
- package/templates/host-theia-electron/esbuild.mjs +27 -0
- package/templates/host-theia-electron/gitignore +8 -0
- package/templates/host-theia-electron/playwright.config.mts +12 -0
- package/templates/host-theia-electron/start.mjs +47 -0
- package/templates/host-theia-electron/tsconfig.e2e.json +16 -0
- package/templates/host-vscode/esbuild.mjs +38 -0
- package/templates/host-vscode/gitignore +7 -0
- package/templates/host-vscode/playwright.config.mts +14 -0
- package/templates/host-vscode/src/__GRAMMAR_ID__-diagram-editor.ts +19 -0
- package/templates/host-vscode/src/show-model-command.ts +24 -0
- package/templates/host-vscode/src/webview/__GRAMMAR_ID__-diagram.ts +18 -0
- package/templates/host-vscode/src/webview/diagram.css +40 -0
- package/templates/host-vscode/start.mjs +22 -0
- package/templates/host-vscode/test/content-server.cjs +40 -0
- package/templates/host-vscode/test/install-vscode.mts +21 -0
- package/templates/host-vscode/test/virtual-documents.spec.mts +28 -0
- package/templates/host-vscode/test/vscode-version.mts +10 -0
- package/templates/host-vscode/tsconfig.e2e.json +17 -0
- package/templates/host-vscode/tsconfig.json +19 -0
- package/templates/host-vscode/tsconfig.webview.json +17 -0
- package/templates/oxlintrc.json +4 -0
- package/templates/protocol/gitignore +3 -0
- package/templates/protocol/src/__GRAMMAR_ID__-diagram-types.ts +47 -0
- package/templates/server/gitignore +6 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/create-node-operation-handler.ts +62 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/delete-operation-handler.ts +63 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/gmodel-factory.ts +144 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/state.ts +18 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/storage.ts +16 -0
- package/templates/server/src/glsp/__GRAMMAR_ID__/submission-handler.ts +24 -0
- package/templates/server/src/grammar/common.langium +12 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-semantic-tokens.ts +10 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-serializer.ts +53 -0
- package/templates/server/src/language-server/__GRAMMAR_ID__-validation.ts +30 -0
- package/templates/server/src/language-server/ast.ts +23 -0
- package/templates/server/src/node/services.ts +16 -0
- package/templates/server/test/__GRAMMAR_ID__-diagram.test.ts +403 -0
- package/templates/server/test/__GRAMMAR_ID__-semantic-tokens.test.ts +34 -0
- package/templates/server/test/tsconfig.json +4 -0
- package/templates/server/tsconfig.test.json +10 -0
- package/templates/server/vitest.config.ts +14 -0
- package/templates/theia/gitignore +3 -0
- package/templates/theia/src/browser/__GRAMMAR_ID__-diagram-frontend-module.ts +65 -0
- package/templates/theia/src/browser/__PROJECT_ID__-connection-preload-module.ts +15 -0
- package/templates/theia/src/browser/__PROJECT_ID__-data-connection.ts +15 -0
- package/templates/theia/src/browser/__PROJECT_ID__-data-port.ts +11 -0
- package/templates/theia/src/browser/__PROJECT_ID__-glsp-client-contribution.ts +13 -0
- package/templates/theia/src/browser/__PROJECT_ID__-show-model-command.ts +51 -0
- package/templates/theia/src/common/__PROJECT_ID__-glsp-contribution.ts +3 -0
- package/templates/theia/src/node/__PROJECT_ID__-connection-backend-module.ts +12 -0
- package/templates/theia/src/node/__PROJECT_ID__-data-backend-module.ts +15 -0
- package/templates/theia/src/node/__PROJECT_ID__-glsp-backend-module.ts +16 -0
- package/templates/theia/style/diagram.css +20 -0
- package/templates/theia/tsconfig.json +24 -0
- package/templates/vscode-servers/gitignore +4 -0
- package/templates/vscode-servers/tsconfig.json +19 -0
- package/templates/workspace/nodes.__EXTENSION__ +5 -0
- package/templates/workspace/references.__EXTENSION__ +2 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Downloads the pinned VS Code into `.vscode-test/`, retrying, so the smoke
|
|
2
|
+
// test finds it there and asks the update server nothing.
|
|
3
|
+
|
|
4
|
+
import { downloadAndUnzipVSCode } from '@vscode/test-electron';
|
|
5
|
+
import { setTimeout } from 'node:timers/promises';
|
|
6
|
+
import { VSCODE_CACHE, VSCODE_VERSION } from './vscode-version.mts';
|
|
7
|
+
|
|
8
|
+
const ATTEMPTS = 3;
|
|
9
|
+
|
|
10
|
+
for (let attempt = 1; ; attempt++) {
|
|
11
|
+
try {
|
|
12
|
+
await downloadAndUnzipVSCode({ version: VSCODE_VERSION, cachePath: VSCODE_CACHE });
|
|
13
|
+
break;
|
|
14
|
+
} catch (error: unknown) {
|
|
15
|
+
if (attempt === ATTEMPTS) {
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
console.warn(`Downloading VS Code ${VSCODE_VERSION} failed, attempt ${attempt} of ${ATTEMPTS}.`, error);
|
|
19
|
+
await setTimeout(attempt * 5_000);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { test } from '@playwright/test';
|
|
2
|
+
import { runTests } from '@vscode/test-electron';
|
|
3
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { VSCODE_CACHE, VSCODE_VERSION } from './vscode-version.mts';
|
|
8
|
+
|
|
9
|
+
test('serves virtual documents without synchronizing their text', async () => {
|
|
10
|
+
const scratch = mkdtempSync(join(tmpdir(), 'virtual-host-'));
|
|
11
|
+
try {
|
|
12
|
+
await runTests({
|
|
13
|
+
version: VSCODE_VERSION,
|
|
14
|
+
cachePath: VSCODE_CACHE,
|
|
15
|
+
extensionDevelopmentPath: fileURLToPath(new URL('..', import.meta.url)),
|
|
16
|
+
extensionTestsPath: fileURLToPath(new URL('./virtual-documents.cjs', import.meta.url)),
|
|
17
|
+
launchArgs: [
|
|
18
|
+
'--no-sandbox',
|
|
19
|
+
'--disable-extensions',
|
|
20
|
+
'--disable-workspace-trust',
|
|
21
|
+
`--user-data-dir=${join(scratch, 'profile')}`,
|
|
22
|
+
`--extensions-dir=${join(scratch, 'extensions')}`
|
|
23
|
+
]
|
|
24
|
+
});
|
|
25
|
+
} finally {
|
|
26
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The VS Code build the smoke test downloads and runs. Pinned rather than
|
|
5
|
+
* `stable`, so a VS Code release cannot fail a run with no change here.
|
|
6
|
+
*/
|
|
7
|
+
export const VSCODE_VERSION = '1.141.0';
|
|
8
|
+
|
|
9
|
+
/** Where `@vscode/test-electron` keeps the download, below this package. */
|
|
10
|
+
export const VSCODE_CACHE = fileURLToPath(new URL('../.vscode-test', import.meta.url));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The smoke tier's half, which runs in Node under Playwright. `test:e2e` passes
|
|
2
|
+
// it with `--tsconfig`, since Playwright otherwise looks for a `tsconfig.json`
|
|
3
|
+
// in the directories above.
|
|
4
|
+
{
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"target": "ES2022",
|
|
7
|
+
"lib": ["ES2022", "DOM"],
|
|
8
|
+
"module": "NodeNext",
|
|
9
|
+
"moduleResolution": "NodeNext",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"types": ["node"]
|
|
15
|
+
},
|
|
16
|
+
"include": ["test", "playwright.config.mts"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The extension, compiled to CommonJS in `out/`, which the extension host
|
|
2
|
+
// loads. It runs in Node, so it has no DOM; a diagram webview under
|
|
3
|
+
// `src/webview/` is a project of its own, bundled by esbuild.
|
|
4
|
+
{
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"target": "ES2022",
|
|
7
|
+
"lib": ["ES2022"],
|
|
8
|
+
"module": "NodeNext",
|
|
9
|
+
"moduleResolution": "NodeNext",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"outDir": "out",
|
|
12
|
+
"strict": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"types": ["node"]
|
|
16
|
+
},
|
|
17
|
+
"include": ["src"],
|
|
18
|
+
"exclude": ["src/webview"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The diagram webviews' half: a browser page, so it has the DOM. esbuild
|
|
2
|
+
// emits, so this only type-checks, and the stylesheet imports are esbuild's to
|
|
3
|
+
// resolve.
|
|
4
|
+
{
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"target": "ES2022",
|
|
7
|
+
"lib": ["ES2022", "DOM"],
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"moduleResolution": "Bundler",
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"noUncheckedSideEffectImports": false,
|
|
14
|
+
"types": []
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/webview"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// GLSP diagram-type and element-type ids for the __GRAMMAR__ diagram, which the
|
|
2
|
+
// server stamps onto the graph and every client registers.
|
|
3
|
+
//
|
|
4
|
+
// Declared once, here, for both ends. A drift between them fails silently:
|
|
5
|
+
// sprotty's registries are exact-key maps with no prefix fallback, so an id the
|
|
6
|
+
// client does not know yields an element with none of a node's features,
|
|
7
|
+
// rendered by `MissingView` with only a `no registered view for type '…'`
|
|
8
|
+
// console warning. Nothing fails server-side.
|
|
9
|
+
//
|
|
10
|
+
// The ids stay namespaced under the GLSP defaults (`node:` / `edge:`) so a
|
|
11
|
+
// reader can tell a node id from an edge id at a glance.
|
|
12
|
+
|
|
13
|
+
import { DefaultTypes } from '@eclipse-glsp/protocol';
|
|
14
|
+
import { __GRAMMAR_ID_CONST___LANGUAGE } from './generated-hydranium/languages.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The GLSP diagram type — the string GLSP routes every per-diagram-type request
|
|
18
|
+
* by. A mismatch silently DROPS the request rather than reporting an unknown
|
|
19
|
+
* diagram type. Read from the language id, so renaming the language renames
|
|
20
|
+
* the diagram type at the next `generate`.
|
|
21
|
+
*/
|
|
22
|
+
export const __GRAMMAR_ID_CONST___DIAGRAM_TYPE = __GRAMMAR_ID_CONST___LANGUAGE.languageId;
|
|
23
|
+
|
|
24
|
+
/** A `__NODE_RULE__` — the starter grammar's one named, referenceable node. */
|
|
25
|
+
export const __GRAMMAR_ID_CONST___NODE_TYPE = `${DefaultTypes.NODE}:__GRAMMAR_ID__-node`;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A `__NODE_RULE__` declared in another document, drawn because a node of this
|
|
29
|
+
* one references it. It belongs to that document, so nothing edits it here.
|
|
30
|
+
*/
|
|
31
|
+
export const __GRAMMAR_ID_CONST___EXTERNAL_NODE_TYPE = `${DefaultTypes.NODE}:__GRAMMAR_ID__-external-node`;
|
|
32
|
+
|
|
33
|
+
/** A resolved `target` reference, drawn as a connection between two nodes. */
|
|
34
|
+
export const __GRAMMAR_ID_CONST___EDGE_TYPE = `${DefaultTypes.EDGE}:__GRAMMAR_ID__-target`;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Every element type id the server stamps.
|
|
38
|
+
*
|
|
39
|
+
* A tuple, so {@link __GRAMMAR__ElementType} is a union of the ids rather than
|
|
40
|
+
* `string`: a client table keyed by that union does not compile while an id
|
|
41
|
+
* lacks its registration. Nothing holds the tuple to the constants above, so
|
|
42
|
+
* an id declared without joining it escapes that check.
|
|
43
|
+
*/
|
|
44
|
+
export const __GRAMMAR_ID_CONST___ELEMENT_TYPES = [__GRAMMAR_ID_CONST___NODE_TYPE, __GRAMMAR_ID_CONST___EXTERNAL_NODE_TYPE, __GRAMMAR_ID_CONST___EDGE_TYPE] as const;
|
|
45
|
+
|
|
46
|
+
/** One of the element type ids in {@link __GRAMMAR_ID_CONST___ELEMENT_TYPES}. */
|
|
47
|
+
export type __GRAMMAR__ElementType = (typeof __GRAMMAR_ID_CONST___ELEMENT_TYPES)[number];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// The starter operation handler for the __GRAMMAR__ diagram: the tool-palette
|
|
2
|
+
// entry that creates a `__NODE_RULE__`.
|
|
3
|
+
//
|
|
4
|
+
// **Deleting this file, `delete-operation-handler.ts` and their
|
|
5
|
+
// `configureOperationHandlers` registrations in `diagram-module.ts`, and setting
|
|
6
|
+
// `deletable` back to `false` in `diagram-configuration.ts`, gives a read-only
|
|
7
|
+
// viewer** — creation is offered through the palette rather than through a type
|
|
8
|
+
// hint. The scaffold emits the editable direction because that asymmetry runs one
|
|
9
|
+
// way: editable → read-only is a deletion the compiler checks, while read-only →
|
|
10
|
+
// editable is authoring against a seam you have not used yet.
|
|
11
|
+
//
|
|
12
|
+
// **It edits the operation's copy.** A handler runs inside an operation, where
|
|
13
|
+
// `sourceRoot` is a copy of the built root: the recording command's runnable
|
|
14
|
+
// appends to the copy, and the operation writes the copy's text through the
|
|
15
|
+
// grammar's `Serializer` once, gated on the version the copy was made at, with
|
|
16
|
+
// undo and redo applying that one change. A command that writes the document
|
|
17
|
+
// itself instead reads no text off the copy, which has no `$document`, and the
|
|
18
|
+
// operation's own write then overwrites it.
|
|
19
|
+
//
|
|
20
|
+
// **The drop location is discarded.** `needsClientLayout` is `true` and the
|
|
21
|
+
// starter grammar persists no bounds, so there is nowhere to put a coordinate: the
|
|
22
|
+
// node is appended at the end of the document and the client places it.
|
|
23
|
+
//
|
|
24
|
+
// Like protocol's `__GRAMMAR_ID__-diagram-types.ts`, `gmodel-factory.ts`,
|
|
25
|
+
// `delete-operation-handler.ts` and `diagram-configuration.ts`, this file knows
|
|
26
|
+
// the starter grammar's shape — the `nodes` list and the `__NODE_RULE__` it
|
|
27
|
+
// holds. Replacing the grammar means replacing them together.
|
|
28
|
+
|
|
29
|
+
import { type Command, type CreateNodeOperation, JsonCreateNodeOperationHandler, type MaybePromise } from '@eclipse-glsp/server';
|
|
30
|
+
import { appendChild } from '@hydranium/core';
|
|
31
|
+
import { HydraniumGlspRecordingCommand } from '@hydranium/glsp-server';
|
|
32
|
+
import { findNextUnique } from '@hydranium/protocol';
|
|
33
|
+
import { __GRAMMAR_ID_CONST___NODE_TYPE } from '__PROTOCOL_PACKAGE__';
|
|
34
|
+
import { injectable } from 'inversify';
|
|
35
|
+
import type { __NODE_RULE__ } from '../../language-server/ast.js';
|
|
36
|
+
import type { __GRAMMAR__GlspState } from './state.js';
|
|
37
|
+
|
|
38
|
+
/** Proposed name for a new node, uniquified against the ones the document already has. */
|
|
39
|
+
const NODE_NAME_STEM = 'Node';
|
|
40
|
+
|
|
41
|
+
@injectable()
|
|
42
|
+
export class __GRAMMAR__CreateNodeOperationHandler extends JsonCreateNodeOperationHandler {
|
|
43
|
+
declare protected modelState: __GRAMMAR__GlspState;
|
|
44
|
+
|
|
45
|
+
/** The palette's word for the thing it creates, so a noun rather than an action. */
|
|
46
|
+
override readonly label = '__NODE_RULE__';
|
|
47
|
+
elementTypeIds = [__GRAMMAR_ID_CONST___NODE_TYPE];
|
|
48
|
+
|
|
49
|
+
override createCommand(operation: CreateNodeOperation): MaybePromise<Command | undefined> {
|
|
50
|
+
if (!this.elementTypeIds.includes(operation.elementTypeId)) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
return new HydraniumGlspRecordingCommand(this.modelState, this.label, () => {
|
|
54
|
+
const root = this.modelState.sourceRoot;
|
|
55
|
+
const name = findNextUnique(
|
|
56
|
+
NODE_NAME_STEM,
|
|
57
|
+
root.nodes.map(node => node.name)
|
|
58
|
+
);
|
|
59
|
+
appendChild(root, 'nodes', root.nodes, { $type: '__NODE_RULE__', name } as __NODE_RULE__);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// The starter delete handler for the __GRAMMAR__ diagram: removes a
|
|
2
|
+
// `__NODE_RULE__`, or the reference an edge draws.
|
|
3
|
+
//
|
|
4
|
+
// Deleting a node also clears every reference to it in this document, so
|
|
5
|
+
// `node A -> B` becomes `node A` rather than keeping a reference that no longer
|
|
6
|
+
// resolves. A reference stays when the name still resolves: to another node of
|
|
7
|
+
// it here, or to one another document declares. A reference from another
|
|
8
|
+
// document is not this operation's to edit: it is left, and reported there as a
|
|
9
|
+
// link error. A node drawn for another document's target is not deletable, and
|
|
10
|
+
// its id finds no node here.
|
|
11
|
+
//
|
|
12
|
+
// Like the create handler, it edits the operation's copy, which the operation
|
|
13
|
+
// writes once, with undo and redo applying that one change.
|
|
14
|
+
|
|
15
|
+
import { type Command, DeleteElementOperation, GEdge, JsonOperationHandler, type MaybePromise } from '@eclipse-glsp/server';
|
|
16
|
+
import { removeChildren } from '@hydranium/core';
|
|
17
|
+
import { HydraniumGlspRecordingCommand } from '@hydranium/glsp-server';
|
|
18
|
+
import { UriUtils } from '@hydranium/langium';
|
|
19
|
+
import type { Mutable } from '@hydranium/protocol';
|
|
20
|
+
import { injectable } from 'inversify';
|
|
21
|
+
import type { __NODE_RULE__ } from '../../language-server/ast.js';
|
|
22
|
+
import type { __GRAMMAR__GlspState } from './state.js';
|
|
23
|
+
|
|
24
|
+
@injectable()
|
|
25
|
+
export class __GRAMMAR__DeleteOperationHandler extends JsonOperationHandler {
|
|
26
|
+
readonly operationType = DeleteElementOperation.KIND;
|
|
27
|
+
|
|
28
|
+
declare protected modelState: __GRAMMAR__GlspState;
|
|
29
|
+
|
|
30
|
+
override createCommand(operation: DeleteElementOperation): MaybePromise<Command | undefined> {
|
|
31
|
+
return new HydraniumGlspRecordingCommand(this.modelState, 'Delete', () => {
|
|
32
|
+
const index = this.modelState.index;
|
|
33
|
+
const root = this.modelState.sourceRoot;
|
|
34
|
+
// The first node with the id, which is the one the diagram draws for a repeated name.
|
|
35
|
+
const nodeOf = (id: string): __NODE_RULE__ | undefined => root.nodes.find(node => index.findId(node) === id);
|
|
36
|
+
const deleted = new Set<__NODE_RULE__>();
|
|
37
|
+
// The nodes whose reference a deleted edge drew.
|
|
38
|
+
const unlinked = new Set<__NODE_RULE__>();
|
|
39
|
+
for (const elementId of operation.elementIds) {
|
|
40
|
+
const element = index.find(elementId);
|
|
41
|
+
const node = nodeOf(element instanceof GEdge ? element.sourceId : elementId);
|
|
42
|
+
if (node !== undefined) {
|
|
43
|
+
(element instanceof GEdge ? unlinked : deleted).add(node);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const remaining = new Set(root.nodes.filter(node => !deleted.has(node)).map(node => node.name));
|
|
47
|
+
const indexManager = index.languageServicesFor(root)?.shared.workspace.IndexManager;
|
|
48
|
+
const stillResolves = (target: __NODE_RULE__): boolean =>
|
|
49
|
+
remaining.has(target.name) ||
|
|
50
|
+
(indexManager
|
|
51
|
+
?.allElements(target.$type)
|
|
52
|
+
.some(element => element.name === target.name && !UriUtils.equals(element.documentUri, this.modelState.sourceUri)) ??
|
|
53
|
+
false);
|
|
54
|
+
for (const node of root.nodes) {
|
|
55
|
+
const target = node.target?.ref;
|
|
56
|
+
if (unlinked.has(node) || (target !== undefined && deleted.has(target) && !stillResolves(target))) {
|
|
57
|
+
(node as Mutable<__NODE_RULE__>).target = undefined;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
removeChildren(root.nodes, deleted);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// AST → GModel for the __GRAMMAR__ diagram: one node per `__NODE_RULE__`, one
|
|
2
|
+
// edge per resolved `target` reference.
|
|
3
|
+
//
|
|
4
|
+
// Ids come from the index rather than being composed here, so the id strategy
|
|
5
|
+
// stays in one place. An id derived from a node's, for its label, its edge or an
|
|
6
|
+
// external node, appends a suffix starting with `-`, which the grammar's `ID`
|
|
7
|
+
// does not allow, so it never clashes with a node's own. Nodes are emitted
|
|
8
|
+
// before edges, because a GLSP edge pointing at an id that does not exist fails
|
|
9
|
+
// client-side with a far less obvious error than a missing edge. An unresolved
|
|
10
|
+
// reference is SKIPPED rather than treated as an error: a dangling reference is
|
|
11
|
+
// ordinary editing state, and the language already reports it as a diagnostic.
|
|
12
|
+
//
|
|
13
|
+
// A reference into another document is drawn to an external node standing for
|
|
14
|
+
// its target, once however many nodes reference it, so the diagram shows what
|
|
15
|
+
// the editor resolves. The target's own diagnostics stay with its document.
|
|
16
|
+
//
|
|
17
|
+
// The starter grammar persists no positions, so the nodes are placed in a grid:
|
|
18
|
+
// each one row above its target, and side by side in document order within a
|
|
19
|
+
// row. Without a cycle every edge spans one row; a cycle is cut where the walk
|
|
20
|
+
// meets a node it is still ranking, and its closing edge spans the cycle.
|
|
21
|
+
|
|
22
|
+
import { DefaultTypes, GEdge, GGraph, GLabel, type GModelFactory, GNode, ModelState } from '@eclipse-glsp/server';
|
|
23
|
+
import { AstUtils, UriUtils } from '@hydranium/langium';
|
|
24
|
+
import { __GRAMMAR_ID_CONST___EDGE_TYPE, __GRAMMAR_ID_CONST___EXTERNAL_NODE_TYPE, __GRAMMAR_ID_CONST___NODE_TYPE } from '__PROTOCOL_PACKAGE__';
|
|
25
|
+
import { inject, injectable } from 'inversify';
|
|
26
|
+
import type { __ENTRY_RULE__, __NODE_RULE__ } from '../../language-server/ast.js';
|
|
27
|
+
import type { __GRAMMAR__GlspState } from './state.js';
|
|
28
|
+
|
|
29
|
+
/** The size of one cell of the grid the nodes are placed in, a column wide and a row high. */
|
|
30
|
+
const CELL = { width: 200, height: 90 };
|
|
31
|
+
|
|
32
|
+
@injectable()
|
|
33
|
+
export class __GRAMMAR__GModelFactory implements GModelFactory {
|
|
34
|
+
@inject(ModelState) protected readonly modelState!: __GRAMMAR__GlspState;
|
|
35
|
+
|
|
36
|
+
createModel(): void {
|
|
37
|
+
const root = this.modelState.sourceRoot;
|
|
38
|
+
const graph = GGraph.builder().id(this.modelState.sourceUri).build();
|
|
39
|
+
if (root) {
|
|
40
|
+
this.buildGraph(root, graph);
|
|
41
|
+
}
|
|
42
|
+
this.modelState.updateRoot(graph);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected buildGraph(root: __ENTRY_RULE__, graph: GGraph): void {
|
|
46
|
+
const index = this.modelState.index;
|
|
47
|
+
// A repeated name repeats the id: only its first node is drawn, with that node's edge.
|
|
48
|
+
const drawn = new Map<string, __NODE_RULE__>();
|
|
49
|
+
for (const node of root.nodes) {
|
|
50
|
+
const id = index.createId(node);
|
|
51
|
+
if (!drawn.has(id)) {
|
|
52
|
+
drawn.set(id, node);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const nodes = [...drawn].map(([id, node]) => ({ id, builder: this.createNode(id, __GRAMMAR_ID_CONST___NODE_TYPE, node.name) }));
|
|
56
|
+
const edges: GEdge[] = [];
|
|
57
|
+
const targetOf = new Map<string, string>();
|
|
58
|
+
// By document and id: same-named targets of two documents get separate
|
|
59
|
+
// nodes, the second with a counter, not a uri in a client-visible id.
|
|
60
|
+
const externalIds = new Map<string, string>();
|
|
61
|
+
const externalCounts = new Map<string, number>();
|
|
62
|
+
for (const [sourceId, node] of drawn) {
|
|
63
|
+
const target = node.target?.ref;
|
|
64
|
+
if (!target) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
let targetId = index.createId(target);
|
|
68
|
+
if (AstUtils.findRootNode(target) !== root) {
|
|
69
|
+
const uri = AstUtils.getDocument(target).uri;
|
|
70
|
+
const key = `${uri.toString()} ${targetId}`;
|
|
71
|
+
const known = externalIds.get(key);
|
|
72
|
+
if (known !== undefined) {
|
|
73
|
+
targetId = known;
|
|
74
|
+
} else {
|
|
75
|
+
const count = (externalCounts.get(targetId) ?? 0) + 1;
|
|
76
|
+
externalCounts.set(targetId, count);
|
|
77
|
+
targetId = count === 1 ? `${targetId}-external` : `${targetId}-external-${count}`;
|
|
78
|
+
externalIds.set(key, targetId);
|
|
79
|
+
const file = UriUtils.basename(uri);
|
|
80
|
+
const external = this.createNode(targetId, __GRAMMAR_ID_CONST___EXTERNAL_NODE_TYPE, `${target.name} (${file})`);
|
|
81
|
+
nodes.push({ id: targetId, builder: external.addCssClass('external') });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
targetOf.set(sourceId, targetId);
|
|
85
|
+
edges.push(GEdge.builder().id(`${sourceId}-target`).type(__GRAMMAR_ID_CONST___EDGE_TYPE).sourceId(sourceId).targetId(targetId).build());
|
|
86
|
+
}
|
|
87
|
+
const ids = nodes.map(node => node.id);
|
|
88
|
+
const ranks = this.rank(targetOf, ids);
|
|
89
|
+
// Loops rather than spread arguments, which overflow the stack on a large diagram.
|
|
90
|
+
let lastRow = 0;
|
|
91
|
+
for (const rank of ranks.values()) {
|
|
92
|
+
lastRow = Math.max(lastRow, rank);
|
|
93
|
+
}
|
|
94
|
+
const columns = new Map<number, number>();
|
|
95
|
+
const placed = nodes.map(({ id, builder }) => {
|
|
96
|
+
const row = lastRow - (ranks.get(id) ?? 0);
|
|
97
|
+
const column = columns.get(row) ?? 0;
|
|
98
|
+
columns.set(row, column + 1);
|
|
99
|
+
return builder.position(column * CELL.width, row * CELL.height).build();
|
|
100
|
+
});
|
|
101
|
+
for (const child of [...placed, ...edges]) {
|
|
102
|
+
graph.children.push(child);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Each node's row counted up from the bottom: one more than its target's,
|
|
108
|
+
* and 0, left out of the map, for a node that references nothing here. A
|
|
109
|
+
* cycle is cut where the walk meets itself.
|
|
110
|
+
*/
|
|
111
|
+
protected rank(targetOf: ReadonlyMap<string, string>, ids: readonly string[]): Map<string, number> {
|
|
112
|
+
const ranks = new Map<string, number>();
|
|
113
|
+
for (const start of ids) {
|
|
114
|
+
// Walked in a loop, not recursively, so a long chain does not overflow the stack.
|
|
115
|
+
const walk = new Set<string>();
|
|
116
|
+
let id = start;
|
|
117
|
+
let target = targetOf.get(id);
|
|
118
|
+
while (target !== undefined && !walk.has(id) && !ranks.has(id)) {
|
|
119
|
+
walk.add(id);
|
|
120
|
+
id = target;
|
|
121
|
+
target = targetOf.get(id);
|
|
122
|
+
}
|
|
123
|
+
let rank = ranks.get(id) ?? 0;
|
|
124
|
+
for (const walked of [...walk].reverse()) {
|
|
125
|
+
rank += 1;
|
|
126
|
+
ranks.set(walked, rank);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return ranks;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** A node labelled `text`, placed and built by the caller. */
|
|
133
|
+
protected createNode(id: string, type: string, text: string) {
|
|
134
|
+
return (
|
|
135
|
+
GNode.builder()
|
|
136
|
+
.id(id)
|
|
137
|
+
.type(type)
|
|
138
|
+
// Sized by the client around its label, which it lays out.
|
|
139
|
+
.layout('vbox')
|
|
140
|
+
.addLayoutOptions({ paddingLeft: 12, paddingRight: 12, paddingTop: 8, paddingBottom: 8 })
|
|
141
|
+
.add(GLabel.builder().id(`${id}-name`).text(text).type(DefaultTypes.LABEL).build())
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// GLSP state for the __GRAMMAR__ diagram.
|
|
2
|
+
//
|
|
3
|
+
// `FullTextHydraniumGlspState` is the simplest of the framework source-model
|
|
4
|
+
// strategies (the others project a structured transfer model, over one document
|
|
5
|
+
// or several): the source model is the whole document text, serialised through
|
|
6
|
+
// the per-URI `Serializer` and round-tripped by re-parsing. Both seams resolve
|
|
7
|
+
// through shared services, so narrowing the root type is all an adopter adds.
|
|
8
|
+
//
|
|
9
|
+
// It cannot field-merge — every concurrent edit on a whole-document model is a
|
|
10
|
+
// same-document collision, so undo / redo degrade to drop-on-divergence. Move to
|
|
11
|
+
// `ReconcilingTransferHydraniumGlspState` when you need field-level undo.
|
|
12
|
+
|
|
13
|
+
import { FullTextHydraniumGlspState } from '@hydranium/glsp-server';
|
|
14
|
+
import { injectable } from 'inversify';
|
|
15
|
+
import type { __ENTRY_RULE__ } from '../../language-server/ast.js';
|
|
16
|
+
|
|
17
|
+
@injectable()
|
|
18
|
+
export class __GRAMMAR__GlspState extends FullTextHydraniumGlspState<__ENTRY_RULE__> {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Source-model storage for the __GRAMMAR__ diagram, inheriting both framework
|
|
2
|
+
// defaults: `loadSourceModel` (open + settle + `setSourceRoot`) and
|
|
3
|
+
// `saveSourceModel` (flush the store's text for the primary and every tracked
|
|
4
|
+
// secondary through the diagram's client session).
|
|
5
|
+
//
|
|
6
|
+
// No serializer runs on the save path — a save persists what the diagram's
|
|
7
|
+
// operations already wrote to the store.
|
|
8
|
+
//
|
|
9
|
+
// The subclass exists so a bespoke load or save has a stable place to land.
|
|
10
|
+
|
|
11
|
+
import { type FullTextSourceModel, HydraniumGlspStorage } from '@hydranium/glsp-server';
|
|
12
|
+
import { injectable } from 'inversify';
|
|
13
|
+
import type { __ENTRY_RULE__ } from '../../language-server/ast.js';
|
|
14
|
+
|
|
15
|
+
@injectable()
|
|
16
|
+
export class __GRAMMAR__GlspStorage extends HydraniumGlspStorage<__ENTRY_RULE__, FullTextSourceModel> {}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Submission handler for the __GRAMMAR__ diagram.
|
|
2
|
+
//
|
|
3
|
+
// Inherits the framework's `readyEvent = IntegrityService.SettledState`, which
|
|
4
|
+
// is load-bearing rather than incidental: the GModel factory resolves
|
|
5
|
+
// `target.ref`, so it needs a fully-linked AST. Without the gate those reads
|
|
6
|
+
// fire mid-build and warn about resolution before scopes are computed.
|
|
7
|
+
//
|
|
8
|
+
// Only `formatSourceRoot` is overridden, so the submit log names the model and
|
|
9
|
+
// its node count instead of a bare `$type`.
|
|
10
|
+
|
|
11
|
+
import { ModelState } from '@eclipse-glsp/server';
|
|
12
|
+
import { type FullTextSourceModel, HydraniumGlspSubmissionHandler } from '@hydranium/glsp-server';
|
|
13
|
+
import { inject, injectable } from 'inversify';
|
|
14
|
+
import type { __ENTRY_RULE__ } from '../../language-server/ast.js';
|
|
15
|
+
import type { __GRAMMAR__GlspState } from './state.js';
|
|
16
|
+
|
|
17
|
+
@injectable()
|
|
18
|
+
export class __GRAMMAR__SubmissionHandler extends HydraniumGlspSubmissionHandler<__ENTRY_RULE__, FullTextSourceModel> {
|
|
19
|
+
@inject(ModelState) declare protected modelState: __GRAMMAR__GlspState;
|
|
20
|
+
|
|
21
|
+
protected override formatSourceRoot(root: __ENTRY_RULE__ | undefined): string {
|
|
22
|
+
return root ? `__ENTRY_RULE__ nodes=${root.nodes.length}` : 'none';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Shared lexical basis for this project's grammars. Imported, never registered:
|
|
2
|
+
// it has no entry rule and no `langium-config.json` entry, so it is a fragment
|
|
3
|
+
// rather than a language.
|
|
4
|
+
//
|
|
5
|
+
// Keeping the tokens here rather than in one of the languages means a grammar
|
|
6
|
+
// imports `common` for its TOKENS and another language only when it needs that
|
|
7
|
+
// language's TYPES — separate reasons that are worth keeping separate.
|
|
8
|
+
|
|
9
|
+
hidden terminal WS: /\s+/;
|
|
10
|
+
terminal ID: /[_a-zA-Z][\w_]*/;
|
|
11
|
+
hidden terminal SL_COMMENT: /\/\/[^\n\r]*/;
|
|
12
|
+
hidden terminal ML_COMMENT: /\/\*[\s\S]*?\*\//;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AbstractHydraniumSemanticTokenProvider, type SemanticTokenKind } from '@hydranium/core/lsp';
|
|
2
|
+
import { SemanticTokenTypes } from 'vscode-languageserver';
|
|
3
|
+
import { __NODE_RULE__ } from './ast.js';
|
|
4
|
+
|
|
5
|
+
/** Declarations and references share the token kind of their target type. */
|
|
6
|
+
export class __GRAMMAR__SemanticTokenProvider extends AbstractHydraniumSemanticTokenProvider {
|
|
7
|
+
protected override getTokenKind(type: string): SemanticTokenKind | undefined {
|
|
8
|
+
return type === __NODE_RULE__.$type ? SemanticTokenTypes.class : undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Concrete-syntax emitter for __GRAMMAR__ — the parser's inverse, turning a
|
|
2
|
+
// model back into text this grammar accepts. Derived from the starter grammar
|
|
3
|
+
// exactly as `generated/ast.ts` is, so replacing the grammar replaces this too.
|
|
4
|
+
//
|
|
5
|
+
// Without it every structured write fails: the framework's default binding at
|
|
6
|
+
// `services.serializer.Serializer` THROWS, because a concrete syntax is
|
|
7
|
+
// language knowledge no framework can derive. A client session's `update` /
|
|
8
|
+
// `save`, the data head's `saveModelDocument` and a GLSP `SaveModelAction`
|
|
9
|
+
// all reach it.
|
|
10
|
+
//
|
|
11
|
+
// `AbstractSerializer`'s generic property walk lays out FORMAT-structured
|
|
12
|
+
// output — its YAML and JSON subclasses are what it exists for — and cannot
|
|
13
|
+
// produce a keyword-delimited syntax like `node a -> b`. So `serializeNode` is
|
|
14
|
+
// a per-`$type` emitter here and the two array hooks are unreachable.
|
|
15
|
+
|
|
16
|
+
import { AbstractSerializer } from '@hydranium/core';
|
|
17
|
+
import type { AstNode } from '@hydranium/langium';
|
|
18
|
+
import { type __ENTRY_RULE__, type __NODE_RULE__, is__ENTRY_RULE__, is__NODE_RULE__ } from './ast.js';
|
|
19
|
+
|
|
20
|
+
export class __GRAMMAR__Serializer extends AbstractSerializer<__ENTRY_RULE__> {
|
|
21
|
+
protected override serializeNode(node: AstNode | Record<string, unknown>): string {
|
|
22
|
+
if (is__ENTRY_RULE__(node)) {
|
|
23
|
+
return node.nodes.map(child => this.emitNode(child)).join('\n');
|
|
24
|
+
}
|
|
25
|
+
if (is__NODE_RULE__(node)) {
|
|
26
|
+
return this.emitNode(node);
|
|
27
|
+
}
|
|
28
|
+
// Defensive: a rule added to the grammar with no emitter added here.
|
|
29
|
+
throw new Error(`__GRAMMAR__Serializer: no emitter for $type ${(node as AstNode).$type}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Unreachable — this grammar's one list is emitted by its `__ENTRY_RULE__` parent. */
|
|
33
|
+
protected override serializeArray(): string {
|
|
34
|
+
throw new Error('__GRAMMAR__Serializer: arrays are emitted by the per-$type parent, not the generic dispatch.');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Unreachable — same reasoning as {@link serializeArray}. */
|
|
38
|
+
protected override serializeReferenceArray(): string {
|
|
39
|
+
throw new Error('__GRAMMAR__Serializer: reference arrays are emitted by the per-$type parent, not the generic dispatch.');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `serializeReferenceText` rather than `node.target?.$refText`: it is the one
|
|
44
|
+
* read that spans BOTH input shapes. A transfer model reaching
|
|
45
|
+
* `serializeTransfer` carries `target` as a plain string rather than a
|
|
46
|
+
* `Reference`, and a serializer that reaches for `$refText` directly emits
|
|
47
|
+
* the AST correctly and drops every reference on the transfer path.
|
|
48
|
+
*/
|
|
49
|
+
private emitNode(node: __NODE_RULE__): string {
|
|
50
|
+
const target = this.serializeReferenceText(node.target);
|
|
51
|
+
return target === undefined ? `node ${node.name}` : `node ${node.name} -> ${target}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Validation for __GRAMMAR__: a check the parser and linker cannot make.
|
|
2
|
+
//
|
|
3
|
+
// A name is what a reference resolves by and what the diagram keys a node by,
|
|
4
|
+
// so a second node of the same name is unreachable by reference and dropped
|
|
5
|
+
// from the diagram. Each one after the first gets a warning rather than an
|
|
6
|
+
// error, since a half-typed rename passes through this state.
|
|
7
|
+
|
|
8
|
+
import type { ValidationCheckContribution, ValidationCheckRegistry } from '@hydranium/core';
|
|
9
|
+
import { acceptMessage } from '@hydranium/core/messages';
|
|
10
|
+
import type { ValidationAcceptor } from '@hydranium/langium';
|
|
11
|
+
import { defineMessage } from '@hydranium/protocol';
|
|
12
|
+
import type { __ENTRY_RULE__, __NAME__AstType } from './ast.js';
|
|
13
|
+
|
|
14
|
+
export const DUPLICATE_NAME = defineMessage('__PROJECT_ID__/__GRAMMAR_ID__/duplicate-name', "A node named '{name}' is already declared.");
|
|
15
|
+
|
|
16
|
+
export class __GRAMMAR__ValidationContribution implements ValidationCheckContribution {
|
|
17
|
+
registerValidationChecks(registry: ValidationCheckRegistry): void {
|
|
18
|
+
registry.register<__NAME__AstType>({ __ENTRY_RULE__: this.checkUniqueNames }, this);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected checkUniqueNames(model: __ENTRY_RULE__, accept: ValidationAcceptor): void {
|
|
22
|
+
const seen = new Set<string>();
|
|
23
|
+
for (const node of model.nodes) {
|
|
24
|
+
if (seen.has(node.name)) {
|
|
25
|
+
acceptMessage(accept, 'warning', DUPLICATE_NAME, { node, property: 'name' }, { name: node.name });
|
|
26
|
+
}
|
|
27
|
+
seen.add(node.name);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|