@reqlan/language 1.4.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 +24 -0
- package/out/file-path-rewrite.d.ts +25 -0
- package/out/file-path-rewrite.js +56 -0
- package/out/file-path-rewrite.js.map +1 -0
- package/out/generated/ast.d.ts +1157 -0
- package/out/generated/ast.js +980 -0
- package/out/generated/ast.js.map +1 -0
- package/out/generated/grammar.d.ts +7 -0
- package/out/generated/grammar.js +3071 -0
- package/out/generated/grammar.js.map +1 -0
- package/out/generated/module.d.ts +20 -0
- package/out/generated/module.js +32 -0
- package/out/generated/module.js.map +1 -0
- package/out/index.d.ts +34 -0
- package/out/index.js +35 -0
- package/out/index.js.map +1 -0
- package/out/reqlan-attribute-catalog.d.ts +19 -0
- package/out/reqlan-attribute-catalog.js +87 -0
- package/out/reqlan-attribute-catalog.js.map +1 -0
- package/out/reqlan-code-action-provider.d.ts +55 -0
- package/out/reqlan-code-action-provider.js +269 -0
- package/out/reqlan-code-action-provider.js.map +1 -0
- package/out/reqlan-code-lens-provider.d.ts +13 -0
- package/out/reqlan-code-lens-provider.js +27 -0
- package/out/reqlan-code-lens-provider.js.map +1 -0
- package/out/reqlan-code-lens-settings.d.ts +34 -0
- package/out/reqlan-code-lens-settings.js +12 -0
- package/out/reqlan-code-lens-settings.js.map +1 -0
- package/out/reqlan-comment-resolver.d.ts +35 -0
- package/out/reqlan-comment-resolver.js +308 -0
- package/out/reqlan-comment-resolver.js.map +1 -0
- package/out/reqlan-completion-context.d.ts +46 -0
- package/out/reqlan-completion-context.js +244 -0
- package/out/reqlan-completion-context.js.map +1 -0
- package/out/reqlan-completion-provider.d.ts +41 -0
- package/out/reqlan-completion-provider.js +317 -0
- package/out/reqlan-completion-provider.js.map +1 -0
- package/out/reqlan-definition-provider.d.ts +31 -0
- package/out/reqlan-definition-provider.js +240 -0
- package/out/reqlan-definition-provider.js.map +1 -0
- package/out/reqlan-document-highlight-provider.d.ts +10 -0
- package/out/reqlan-document-highlight-provider.js +11 -0
- package/out/reqlan-document-highlight-provider.js.map +1 -0
- package/out/reqlan-document-link-provider.d.ts +14 -0
- package/out/reqlan-document-link-provider.js +24 -0
- package/out/reqlan-document-link-provider.js.map +1 -0
- package/out/reqlan-embedded-file-references.d.ts +11 -0
- package/out/reqlan-embedded-file-references.js +50 -0
- package/out/reqlan-embedded-file-references.js.map +1 -0
- package/out/reqlan-file-link-resolver.d.ts +44 -0
- package/out/reqlan-file-link-resolver.js +347 -0
- package/out/reqlan-file-link-resolver.js.map +1 -0
- package/out/reqlan-file-references.d.ts +15 -0
- package/out/reqlan-file-references.js +72 -0
- package/out/reqlan-file-references.js.map +1 -0
- package/out/reqlan-grammar-config.d.ts +5 -0
- package/out/reqlan-grammar-config.js +9 -0
- package/out/reqlan-grammar-config.js.map +1 -0
- package/out/reqlan-idea-reference-site.d.ts +14 -0
- package/out/reqlan-idea-reference-site.js +47 -0
- package/out/reqlan-idea-reference-site.js.map +1 -0
- package/out/reqlan-idea-summary.d.ts +6 -0
- package/out/reqlan-idea-summary.js +134 -0
- package/out/reqlan-idea-summary.js.map +1 -0
- package/out/reqlan-ignore-error.d.ts +10 -0
- package/out/reqlan-ignore-error.js +42 -0
- package/out/reqlan-ignore-error.js.map +1 -0
- package/out/reqlan-import-bindings.d.ts +12 -0
- package/out/reqlan-import-bindings.js +65 -0
- package/out/reqlan-import-bindings.js.map +1 -0
- package/out/reqlan-import-edits.d.ts +17 -0
- package/out/reqlan-import-edits.js +96 -0
- package/out/reqlan-import-edits.js.map +1 -0
- package/out/reqlan-imports.d.ts +22 -0
- package/out/reqlan-imports.js +61 -0
- package/out/reqlan-imports.js.map +1 -0
- package/out/reqlan-inbound-reference-inlay-label.d.ts +27 -0
- package/out/reqlan-inbound-reference-inlay-label.js +142 -0
- package/out/reqlan-inbound-reference-inlay-label.js.map +1 -0
- package/out/reqlan-inlay-hint-provider.d.ts +15 -0
- package/out/reqlan-inlay-hint-provider.js +54 -0
- package/out/reqlan-inlay-hint-provider.js.map +1 -0
- package/out/reqlan-inlay-hint-settings.d.ts +10 -0
- package/out/reqlan-inlay-hint-settings.js +10 -0
- package/out/reqlan-inlay-hint-settings.js.map +1 -0
- package/out/reqlan-linker.d.ts +26 -0
- package/out/reqlan-linker.js +83 -0
- package/out/reqlan-linker.js.map +1 -0
- package/out/reqlan-markdown-links.d.ts +28 -0
- package/out/reqlan-markdown-links.js +113 -0
- package/out/reqlan-markdown-links.js.map +1 -0
- package/out/reqlan-module.d.ts +41 -0
- package/out/reqlan-module.js +78 -0
- package/out/reqlan-module.js.map +1 -0
- package/out/reqlan-name-catalog.d.ts +23 -0
- package/out/reqlan-name-catalog.js +63 -0
- package/out/reqlan-name-catalog.js.map +1 -0
- package/out/reqlan-name-provider.d.ts +10 -0
- package/out/reqlan-name-provider.js +38 -0
- package/out/reqlan-name-provider.js.map +1 -0
- package/out/reqlan-namespace-import-links.d.ts +10 -0
- package/out/reqlan-namespace-import-links.js +69 -0
- package/out/reqlan-namespace-import-links.js.map +1 -0
- package/out/reqlan-path-completion.d.ts +50 -0
- package/out/reqlan-path-completion.js +304 -0
- package/out/reqlan-path-completion.js.map +1 -0
- package/out/reqlan-path-references.d.ts +5 -0
- package/out/reqlan-path-references.js +124 -0
- package/out/reqlan-path-references.js.map +1 -0
- package/out/reqlan-path-resolve.d.ts +81 -0
- package/out/reqlan-path-resolve.js +245 -0
- package/out/reqlan-path-resolve.js.map +1 -0
- package/out/reqlan-quoted-strings.d.ts +9 -0
- package/out/reqlan-quoted-strings.js +26 -0
- package/out/reqlan-quoted-strings.js.map +1 -0
- package/out/reqlan-reference-at-position.d.ts +20 -0
- package/out/reqlan-reference-at-position.js +89 -0
- package/out/reqlan-reference-at-position.js.map +1 -0
- package/out/reqlan-reference-code-lens.d.ts +32 -0
- package/out/reqlan-reference-code-lens.js +240 -0
- package/out/reqlan-reference-code-lens.js.map +1 -0
- package/out/reqlan-reference-search-site.d.ts +34 -0
- package/out/reqlan-reference-search-site.js +136 -0
- package/out/reqlan-reference-search-site.js.map +1 -0
- package/out/reqlan-references.d.ts +16 -0
- package/out/reqlan-references.js +51 -0
- package/out/reqlan-references.js.map +1 -0
- package/out/reqlan-scope.d.ts +25 -0
- package/out/reqlan-scope.js +259 -0
- package/out/reqlan-scope.js.map +1 -0
- package/out/reqlan-semantic-token-provider.d.ts +8 -0
- package/out/reqlan-semantic-token-provider.js +91 -0
- package/out/reqlan-semantic-token-provider.js.map +1 -0
- package/out/reqlan-token-builder.d.ts +12 -0
- package/out/reqlan-token-builder.js +459 -0
- package/out/reqlan-token-builder.js.map +1 -0
- package/out/reqlan-validator.d.ts +25 -0
- package/out/reqlan-validator.js +119 -0
- package/out/reqlan-validator.js.map +1 -0
- package/out/reqlan-workspace-attribute-catalog.d.ts +6 -0
- package/out/reqlan-workspace-attribute-catalog.js +54 -0
- package/out/reqlan-workspace-attribute-catalog.js.map +1 -0
- package/out/src/file-path-rewrite.d.ts +25 -0
- package/out/src/file-path-rewrite.js +56 -0
- package/out/src/file-path-rewrite.js.map +1 -0
- package/out/src/generated/ast.d.ts +1173 -0
- package/out/src/generated/ast.js +994 -0
- package/out/src/generated/ast.js.map +1 -0
- package/out/src/generated/grammar.d.ts +7 -0
- package/out/src/generated/grammar.js +3045 -0
- package/out/src/generated/grammar.js.map +1 -0
- package/out/src/generated/module.d.ts +20 -0
- package/out/src/generated/module.js +32 -0
- package/out/src/generated/module.js.map +1 -0
- package/out/src/index.d.ts +29 -0
- package/out/src/index.js +30 -0
- package/out/src/index.js.map +1 -0
- package/out/src/reqlan-attribute-catalog.d.ts +19 -0
- package/out/src/reqlan-attribute-catalog.js +87 -0
- package/out/src/reqlan-attribute-catalog.js.map +1 -0
- package/out/src/reqlan-code-action-provider.d.ts +34 -0
- package/out/src/reqlan-code-action-provider.js +206 -0
- package/out/src/reqlan-code-action-provider.js.map +1 -0
- package/out/src/reqlan-comment-resolver.d.ts +35 -0
- package/out/src/reqlan-comment-resolver.js +308 -0
- package/out/src/reqlan-comment-resolver.js.map +1 -0
- package/out/src/reqlan-completion-context.d.ts +46 -0
- package/out/src/reqlan-completion-context.js +244 -0
- package/out/src/reqlan-completion-context.js.map +1 -0
- package/out/src/reqlan-completion-provider.d.ts +41 -0
- package/out/src/reqlan-completion-provider.js +320 -0
- package/out/src/reqlan-completion-provider.js.map +1 -0
- package/out/src/reqlan-definition-provider.d.ts +31 -0
- package/out/src/reqlan-definition-provider.js +240 -0
- package/out/src/reqlan-definition-provider.js.map +1 -0
- package/out/src/reqlan-document-highlight-provider.d.ts +10 -0
- package/out/src/reqlan-document-highlight-provider.js +11 -0
- package/out/src/reqlan-document-highlight-provider.js.map +1 -0
- package/out/src/reqlan-document-link-provider.d.ts +14 -0
- package/out/src/reqlan-document-link-provider.js +24 -0
- package/out/src/reqlan-document-link-provider.js.map +1 -0
- package/out/src/reqlan-embedded-file-references.d.ts +11 -0
- package/out/src/reqlan-embedded-file-references.js +50 -0
- package/out/src/reqlan-embedded-file-references.js.map +1 -0
- package/out/src/reqlan-file-link-resolver.d.ts +44 -0
- package/out/src/reqlan-file-link-resolver.js +347 -0
- package/out/src/reqlan-file-link-resolver.js.map +1 -0
- package/out/src/reqlan-file-references.d.ts +15 -0
- package/out/src/reqlan-file-references.js +72 -0
- package/out/src/reqlan-file-references.js.map +1 -0
- package/out/src/reqlan-grammar-config.d.ts +5 -0
- package/out/src/reqlan-grammar-config.js +9 -0
- package/out/src/reqlan-grammar-config.js.map +1 -0
- package/out/src/reqlan-idea-summary.d.ts +6 -0
- package/out/src/reqlan-idea-summary.js +78 -0
- package/out/src/reqlan-idea-summary.js.map +1 -0
- package/out/src/reqlan-ignore-error.d.ts +10 -0
- package/out/src/reqlan-ignore-error.js +42 -0
- package/out/src/reqlan-ignore-error.js.map +1 -0
- package/out/src/reqlan-import-bindings.d.ts +12 -0
- package/out/src/reqlan-import-bindings.js +65 -0
- package/out/src/reqlan-import-bindings.js.map +1 -0
- package/out/src/reqlan-import-edits.d.ts +17 -0
- package/out/src/reqlan-import-edits.js +96 -0
- package/out/src/reqlan-import-edits.js.map +1 -0
- package/out/src/reqlan-imports.d.ts +22 -0
- package/out/src/reqlan-imports.js +61 -0
- package/out/src/reqlan-imports.js.map +1 -0
- package/out/src/reqlan-inbound-reference-inlay-label.d.ts +23 -0
- package/out/src/reqlan-inbound-reference-inlay-label.js +128 -0
- package/out/src/reqlan-inbound-reference-inlay-label.js.map +1 -0
- package/out/src/reqlan-inlay-hint-provider.d.ts +15 -0
- package/out/src/reqlan-inlay-hint-provider.js +54 -0
- package/out/src/reqlan-inlay-hint-provider.js.map +1 -0
- package/out/src/reqlan-inlay-hint-settings.d.ts +10 -0
- package/out/src/reqlan-inlay-hint-settings.js +10 -0
- package/out/src/reqlan-inlay-hint-settings.js.map +1 -0
- package/out/src/reqlan-linker.d.ts +26 -0
- package/out/src/reqlan-linker.js +86 -0
- package/out/src/reqlan-linker.js.map +1 -0
- package/out/src/reqlan-markdown-links.d.ts +28 -0
- package/out/src/reqlan-markdown-links.js +113 -0
- package/out/src/reqlan-markdown-links.js.map +1 -0
- package/out/src/reqlan-module.d.ts +41 -0
- package/out/src/reqlan-module.js +76 -0
- package/out/src/reqlan-module.js.map +1 -0
- package/out/src/reqlan-name-catalog.d.ts +23 -0
- package/out/src/reqlan-name-catalog.js +63 -0
- package/out/src/reqlan-name-catalog.js.map +1 -0
- package/out/src/reqlan-name-provider.d.ts +10 -0
- package/out/src/reqlan-name-provider.js +38 -0
- package/out/src/reqlan-name-provider.js.map +1 -0
- package/out/src/reqlan-namespace-import-links.d.ts +10 -0
- package/out/src/reqlan-namespace-import-links.js +81 -0
- package/out/src/reqlan-namespace-import-links.js.map +1 -0
- package/out/src/reqlan-path-completion.d.ts +50 -0
- package/out/src/reqlan-path-completion.js +304 -0
- package/out/src/reqlan-path-completion.js.map +1 -0
- package/out/src/reqlan-path-references.d.ts +5 -0
- package/out/src/reqlan-path-references.js +124 -0
- package/out/src/reqlan-path-references.js.map +1 -0
- package/out/src/reqlan-path-resolve.d.ts +59 -0
- package/out/src/reqlan-path-resolve.js +173 -0
- package/out/src/reqlan-path-resolve.js.map +1 -0
- package/out/src/reqlan-quoted-strings.d.ts +9 -0
- package/out/src/reqlan-quoted-strings.js +26 -0
- package/out/src/reqlan-quoted-strings.js.map +1 -0
- package/out/src/reqlan-reference-at-position.d.ts +20 -0
- package/out/src/reqlan-reference-at-position.js +89 -0
- package/out/src/reqlan-reference-at-position.js.map +1 -0
- package/out/src/reqlan-references.d.ts +16 -0
- package/out/src/reqlan-references.js +51 -0
- package/out/src/reqlan-references.js.map +1 -0
- package/out/src/reqlan-scope.d.ts +25 -0
- package/out/src/reqlan-scope.js +266 -0
- package/out/src/reqlan-scope.js.map +1 -0
- package/out/src/reqlan-semantic-token-provider.d.ts +8 -0
- package/out/src/reqlan-semantic-token-provider.js +97 -0
- package/out/src/reqlan-semantic-token-provider.js.map +1 -0
- package/out/src/reqlan-token-builder.d.ts +12 -0
- package/out/src/reqlan-token-builder.js +459 -0
- package/out/src/reqlan-token-builder.js.map +1 -0
- package/out/src/reqlan-validator.d.ts +25 -0
- package/out/src/reqlan-validator.js +119 -0
- package/out/src/reqlan-validator.js.map +1 -0
- package/out/src/reqlan-workspace-attribute-catalog.d.ts +6 -0
- package/out/src/reqlan-workspace-attribute-catalog.js +54 -0
- package/out/src/reqlan-workspace-attribute-catalog.js.map +1 -0
- package/out/test/code-actions.test.d.ts +1 -0
- package/out/test/code-actions.test.js +126 -0
- package/out/test/code-actions.test.js.map +1 -0
- package/out/test/comment-in-string.test.d.ts +1 -0
- package/out/test/comment-in-string.test.js +100 -0
- package/out/test/comment-in-string.test.js.map +1 -0
- package/out/test/completion.test.d.ts +1 -0
- package/out/test/completion.test.js +467 -0
- package/out/test/completion.test.js.map +1 -0
- package/out/test/file-path-rewrite.test.d.ts +1 -0
- package/out/test/file-path-rewrite.test.js +64 -0
- package/out/test/file-path-rewrite.test.js.map +1 -0
- package/out/test/ignore-error.test.d.ts +1 -0
- package/out/test/ignore-error.test.js +87 -0
- package/out/test/ignore-error.test.js.map +1 -0
- package/out/test/implicit-import-extension.test.d.ts +1 -0
- package/out/test/implicit-import-extension.test.js +203 -0
- package/out/test/implicit-import-extension.test.js.map +1 -0
- package/out/test/import-edits.test.d.ts +1 -0
- package/out/test/import-edits.test.js +20 -0
- package/out/test/import-edits.test.js.map +1 -0
- package/out/test/inlay-hints.test.d.ts +1 -0
- package/out/test/inlay-hints.test.js +238 -0
- package/out/test/inlay-hints.test.js.map +1 -0
- package/out/test/linking.test.d.ts +1 -0
- package/out/test/linking.test.js +754 -0
- package/out/test/linking.test.js.map +1 -0
- package/out/test/naked-strings.test.d.ts +1 -0
- package/out/test/naked-strings.test.js +50 -0
- package/out/test/naked-strings.test.js.map +1 -0
- package/out/test/parsing.test.d.ts +1 -0
- package/out/test/parsing.test.js +773 -0
- package/out/test/parsing.test.js.map +1 -0
- package/out/test/path-resolve.test.d.ts +1 -0
- package/out/test/path-resolve.test.js +88 -0
- package/out/test/path-resolve.test.js.map +1 -0
- package/out/test/references.test.d.ts +1 -0
- package/out/test/references.test.js +168 -0
- package/out/test/references.test.js.map +1 -0
- package/out/test/rqconfig.test.d.ts +1 -0
- package/out/test/rqconfig.test.js +275 -0
- package/out/test/rqconfig.test.js.map +1 -0
- package/out/test/test-requirement-refs.d.ts +8 -0
- package/out/test/test-requirement-refs.js +35 -0
- package/out/test/test-requirement-refs.js.map +1 -0
- package/out/test/testing.test.d.ts +1 -0
- package/out/test/testing.test.js +23 -0
- package/out/test/testing.test.js.map +1 -0
- package/out/test/validating.test.d.ts +1 -0
- package/out/test/validating.test.js +117 -0
- package/out/test/validating.test.js.map +1 -0
- package/out/tsconfig.tsbuildinfo +1 -0
- package/package.json +54 -0
- package/src/chevrotain.d.ts +8 -0
- package/src/file-path-rewrite.ts +96 -0
- package/src/generated/ast.ts +1481 -0
- package/src/generated/grammar.ts +3074 -0
- package/src/generated/module.ts +38 -0
- package/src/index.ts +34 -0
- package/src/reqlan-attribute-catalog.ts +100 -0
- package/src/reqlan-code-action-provider.ts +385 -0
- package/src/reqlan-code-lens-provider.ts +54 -0
- package/src/reqlan-code-lens-settings.ts +42 -0
- package/src/reqlan-comment-resolver.ts +372 -0
- package/src/reqlan-comment.langium +23 -0
- package/src/reqlan-completion-context.ts +330 -0
- package/src/reqlan-completion-provider.ts +400 -0
- package/src/reqlan-definition-provider.ts +319 -0
- package/src/reqlan-document-highlight-provider.ts +21 -0
- package/src/reqlan-document-link-provider.ts +45 -0
- package/src/reqlan-embedded-file-references.ts +72 -0
- package/src/reqlan-file-link-resolver.ts +485 -0
- package/src/reqlan-file-references.ts +84 -0
- package/src/reqlan-grammar-config.ts +14 -0
- package/src/reqlan-idea-reference-site.ts +74 -0
- package/src/reqlan-idea-summary.ts +162 -0
- package/src/reqlan-ignore-error.ts +50 -0
- package/src/reqlan-import-bindings.ts +87 -0
- package/src/reqlan-import-edits.ts +124 -0
- package/src/reqlan-imports.ts +115 -0
- package/src/reqlan-inbound-reference-inlay-label.ts +194 -0
- package/src/reqlan-inlay-hint-provider.ts +88 -0
- package/src/reqlan-inlay-hint-settings.ts +15 -0
- package/src/reqlan-linker.ts +108 -0
- package/src/reqlan-markdown-links.ts +136 -0
- package/src/reqlan-module.ts +104 -0
- package/src/reqlan-name-catalog.ts +81 -0
- package/src/reqlan-name-provider.ts +56 -0
- package/src/reqlan-namespace-import-links.ts +92 -0
- package/src/reqlan-path-completion.ts +420 -0
- package/src/reqlan-path-references.ts +140 -0
- package/src/reqlan-path-resolve.ts +346 -0
- package/src/reqlan-quoted-strings.ts +30 -0
- package/src/reqlan-reference-at-position.ts +154 -0
- package/src/reqlan-reference-code-lens.ts +323 -0
- package/src/reqlan-reference-search-site.ts +202 -0
- package/src/reqlan-references.ts +76 -0
- package/src/reqlan-scope.ts +312 -0
- package/src/reqlan-semantic-token-provider.ts +113 -0
- package/src/reqlan-token-builder.ts +492 -0
- package/src/reqlan-validator.ts +149 -0
- package/src/reqlan-workspace-attribute-catalog.ts +62 -0
- package/src/reqlan.langium +236 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
|
+
import { afterEach, beforeAll, describe, expect, test } from 'vitest';
|
|
5
|
+
import { AstUtils, EmptyFileSystem, GrammarUtils, URI } from 'langium';
|
|
6
|
+
import { NodeFileSystem } from 'langium/node';
|
|
7
|
+
import { expandToString as s } from 'langium/generate';
|
|
8
|
+
import { clearDocuments, parseHelper } from 'langium/test';
|
|
9
|
+
import { createReqlanServices, isBracketReference, isFromImport, isIdea, isIdeaSet, isLocalReference, isModel, isNamespaceImport, isOneLinerIdea, isQualifiedReference, isWikiLink } from 'reqlan-language';
|
|
10
|
+
import { ReqlanDocumentLinkProvider } from '../src/reqlan-document-link-provider.js';
|
|
11
|
+
import { classifyReferenceUri } from '../src/reqlan-file-link-resolver.js';
|
|
12
|
+
import { isNamespaceImportOnlyReference, resolveNamespaceImportReferenceLink } from '../src/reqlan-namespace-import-links.js';
|
|
13
|
+
import { createSourceTextDocument, findCommentReferencesInText, resolveFileUri } from '../src/reqlan-comment-resolver.js';
|
|
14
|
+
import { findCommentDefinitionAtPosition } from '../src/reqlan-reference-at-position.js';
|
|
15
|
+
import { findTestLineInText } from '../src/reqlan-file-references.js';
|
|
16
|
+
const repoDir = join(dirname(fileURLToPath(import.meta.url)), '../../..');
|
|
17
|
+
const exampleDir = join(repoDir, 'example_rq_project');
|
|
18
|
+
let services;
|
|
19
|
+
let parse;
|
|
20
|
+
let document;
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
services = createReqlanServices(EmptyFileSystem);
|
|
23
|
+
parse = parseHelper(services.Reqlan);
|
|
24
|
+
});
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
const documents = services.shared.workspace.LangiumDocuments.all.toArray();
|
|
27
|
+
if (documents.length > 0) {
|
|
28
|
+
clearDocuments(services.shared, documents);
|
|
29
|
+
}
|
|
30
|
+
document = undefined;
|
|
31
|
+
});
|
|
32
|
+
async function parseDocumentsTogether(filenames) {
|
|
33
|
+
const documents = [];
|
|
34
|
+
for (const filename of filenames) {
|
|
35
|
+
const path = join(exampleDir, filename);
|
|
36
|
+
const uri = URI.parse(pathToFileURL(path).href);
|
|
37
|
+
const doc = services.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(path, 'utf8'), uri);
|
|
38
|
+
services.shared.workspace.LangiumDocuments.addDocument(doc);
|
|
39
|
+
documents.push(doc);
|
|
40
|
+
}
|
|
41
|
+
await services.shared.workspace.DocumentBuilder.build(documents, { validation: false });
|
|
42
|
+
return documents;
|
|
43
|
+
}
|
|
44
|
+
function resolvedReferenceIdeaName(target) {
|
|
45
|
+
if (target.idea?.ref && (isIdea(target.idea.ref) || isOneLinerIdea(target.idea.ref))) {
|
|
46
|
+
return target.idea.ref.name;
|
|
47
|
+
}
|
|
48
|
+
const qualifierRef = isQualifiedReference(target) ? target.qualifier?.ref : undefined;
|
|
49
|
+
if (qualifierRef && (isIdea(qualifierRef) || isOneLinerIdea(qualifierRef))) {
|
|
50
|
+
return qualifierRef.name;
|
|
51
|
+
}
|
|
52
|
+
return target.idea?.error?.message;
|
|
53
|
+
}
|
|
54
|
+
describe('Linking tests', () => {
|
|
55
|
+
// rq:["../../../reqlan rq/language/syntax.rq".same_file_reference]
|
|
56
|
+
test('resolve same-file bracket reference to local idea', async () => {
|
|
57
|
+
document = await parse(s `
|
|
58
|
+
reference_brackets {
|
|
59
|
+
bracket syntax
|
|
60
|
+
}
|
|
61
|
+
same_file_reference {
|
|
62
|
+
[reference_brackets] should work in the same file.
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
66
|
+
const bracketRef = [...AstUtils.streamAst(document.parseResult.value)]
|
|
67
|
+
.filter(isBracketReference)[0];
|
|
68
|
+
expect(bracketRef).toBeDefined();
|
|
69
|
+
if (!bracketRef) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const target = bracketRef.target;
|
|
73
|
+
expect(isLocalReference(target)
|
|
74
|
+
? target.idea?.ref?.name
|
|
75
|
+
: isQualifiedReference(target)
|
|
76
|
+
? resolvedReferenceIdeaName(target)
|
|
77
|
+
: undefined).toBe('reference_brackets');
|
|
78
|
+
if (isLocalReference(target)) {
|
|
79
|
+
expect(target.idea?.error).toBeUndefined();
|
|
80
|
+
}
|
|
81
|
+
else if (isQualifiedReference(target)) {
|
|
82
|
+
expect(target.idea?.error).toBeUndefined();
|
|
83
|
+
expect(target.qualifier?.error).toBeUndefined();
|
|
84
|
+
}
|
|
85
|
+
const unresolved = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
86
|
+
&& diagnostic.message.includes('Could not resolve reference'));
|
|
87
|
+
expect(unresolved).toHaveLength(0);
|
|
88
|
+
const declaration = [...AstUtils.streamAst(document.parseResult.value)].find(node => isIdea(node) && node.name === 'reference_brackets');
|
|
89
|
+
expect(declaration && isIdea(declaration)).toBe(true);
|
|
90
|
+
if (!declaration || !isIdea(declaration)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const references = services.Reqlan.references.References
|
|
94
|
+
.findReferences(declaration, { includeDeclaration: true })
|
|
95
|
+
.toArray();
|
|
96
|
+
const texts = references.map(reference => document.textDocument.getText(reference.segment.range));
|
|
97
|
+
expect(texts.filter(text => text === 'reference_brackets').length).toBeGreaterThanOrEqual(2);
|
|
98
|
+
});
|
|
99
|
+
// rq:["../../../reqlan rq/language/syntax.rq".reference_wikilink]
|
|
100
|
+
test('resolve wikilink to idea declaration in main.rq', async () => {
|
|
101
|
+
document = await parse(readFileSync(join(exampleDir, 'main.rq'), 'utf8'));
|
|
102
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
103
|
+
const links = [...AstUtils.streamAst(document.parseResult.value)]
|
|
104
|
+
.filter(isWikiLink)
|
|
105
|
+
.map(link => {
|
|
106
|
+
const target = link.target;
|
|
107
|
+
if (isQualifiedReference(target) || isLocalReference(target)) {
|
|
108
|
+
return resolvedReferenceIdeaName(target);
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
});
|
|
112
|
+
expect(checkDocumentValid(document) || links.join('\n')).toBe(s `
|
|
113
|
+
myidea
|
|
114
|
+
myidea
|
|
115
|
+
`);
|
|
116
|
+
});
|
|
117
|
+
// rq:["../../../reqlan rq/language/syntax.rq".reference_qualified]
|
|
118
|
+
test('resolve import alias in qualified wikilink', async () => {
|
|
119
|
+
const documents = await parseDocumentsTogether(['exampleimport2.rq', 'sub idea.rq']);
|
|
120
|
+
document = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
121
|
+
expect(document).toBeDefined();
|
|
122
|
+
if (!document) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const aliasLink = [...AstUtils.streamAst(document.parseResult.value)]
|
|
126
|
+
.filter(isWikiLink)
|
|
127
|
+
.find(link => isQualifiedReference(link.target) && link.target.qualifier?.ref && isNamespaceImport(link.target.qualifier.ref));
|
|
128
|
+
expect(aliasLink && isQualifiedReference(aliasLink.target) && aliasLink.target.qualifier?.ref && isNamespaceImport(aliasLink.target.qualifier.ref) && aliasLink.target.qualifier.ref.alias).toBe('exampleimport2');
|
|
129
|
+
if (!aliasLink || !isQualifiedReference(aliasLink.target) || !aliasLink.target.idea) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
expect(aliasLink.target.idea.ref?.name).toBe('myimportableIdea');
|
|
133
|
+
expect(aliasLink.target.idea.error).toBeUndefined();
|
|
134
|
+
expect(aliasLink.target.qualifier?.error).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
// rq:["../../../reqlan rq/language/syntax.rq".reference_qualified]
|
|
137
|
+
test('resolve import path in qualified wikilink', async () => {
|
|
138
|
+
const documents = await parseDocumentsTogether(['main.rq', 'exampleimport.rq', 'sub idea.rq']);
|
|
139
|
+
document = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
140
|
+
expect(document).toBeDefined();
|
|
141
|
+
if (!document) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const pathLink = [...AstUtils.streamAst(document.parseResult.value)]
|
|
145
|
+
.filter(isWikiLink)
|
|
146
|
+
.find(link => isQualifiedReference(link.target) && link.target.path?.ref && isNamespaceImport(link.target.path.ref));
|
|
147
|
+
expect(pathLink && isQualifiedReference(pathLink.target) && pathLink.target.path?.ref && isNamespaceImport(pathLink.target.path.ref) && pathLink.target.path.ref.path).toBe('./exampleimport.rq');
|
|
148
|
+
if (!pathLink || !isQualifiedReference(pathLink.target) || !pathLink.target.idea) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
expect(pathLink.target.idea.ref?.name).toBe('myimportableIdea');
|
|
152
|
+
expect(pathLink.target.idea.error).toBeUndefined();
|
|
153
|
+
expect(pathLink.target.path?.error).toBeUndefined();
|
|
154
|
+
});
|
|
155
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".syntax_features]
|
|
156
|
+
test('go to definition on import path opens source file', async () => {
|
|
157
|
+
const documents = await parseDocumentsTogether(['exampleimport.rq', 'sub idea.rq']);
|
|
158
|
+
const subDocument = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
159
|
+
const importedDocument = documents.find(entry => entry.uri.path.endsWith('exampleimport.rq'));
|
|
160
|
+
expect(subDocument).toBeDefined();
|
|
161
|
+
expect(importedDocument).toBeDefined();
|
|
162
|
+
if (!subDocument || !importedDocument) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const importDecl = subDocument.parseResult.value.imports.find(entry => isNamespaceImport(entry) && entry.path === './exampleimport.rq' && !entry.alias);
|
|
166
|
+
expect(importDecl).toBeDefined();
|
|
167
|
+
if (!importDecl) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const pathNode = GrammarUtils.findNodeForProperty(importDecl.$cstNode, 'path');
|
|
171
|
+
expect(pathNode).toBeDefined();
|
|
172
|
+
if (!pathNode) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const links = await services.Reqlan.lsp.DefinitionProvider?.getDefinition(subDocument, {
|
|
176
|
+
textDocument: { uri: subDocument.textDocument.uri },
|
|
177
|
+
position: subDocument.textDocument.positionAt(pathNode.offset)
|
|
178
|
+
});
|
|
179
|
+
expect(links).toHaveLength(1);
|
|
180
|
+
expect(links?.[0].targetUri).toBe(importedDocument.textDocument.uri);
|
|
181
|
+
});
|
|
182
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".syntax_features]
|
|
183
|
+
test('go to definition on wikilink import path opens source file', async () => {
|
|
184
|
+
const documents = await parseDocumentsTogether(['main.rq', 'exampleimport.rq', 'sub idea.rq']);
|
|
185
|
+
const subDocument = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
186
|
+
const importedDocument = documents.find(entry => entry.uri.path.endsWith('exampleimport.rq'));
|
|
187
|
+
expect(subDocument).toBeDefined();
|
|
188
|
+
expect(importedDocument).toBeDefined();
|
|
189
|
+
if (!subDocument || !importedDocument) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const pathLink = [...AstUtils.streamAst(subDocument.parseResult.value)]
|
|
193
|
+
.filter(isWikiLink)
|
|
194
|
+
.map(link => link.target)
|
|
195
|
+
.find((target) => isQualifiedReference(target) && !!target.path?.ref);
|
|
196
|
+
expect(pathLink?.path?.ref).toBeDefined();
|
|
197
|
+
if (!pathLink?.path?.$refNode) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const links = await services.Reqlan.lsp.DefinitionProvider?.getDefinition(subDocument, {
|
|
201
|
+
textDocument: { uri: subDocument.textDocument.uri },
|
|
202
|
+
position: subDocument.textDocument.positionAt(pathLink.path.$refNode.offset)
|
|
203
|
+
});
|
|
204
|
+
expect(links).toHaveLength(1);
|
|
205
|
+
expect(links?.[0].targetUri).toBe(importedDocument.textDocument.uri);
|
|
206
|
+
});
|
|
207
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".syntax_features]
|
|
208
|
+
test('go to definition on wikilink idea still opens idea declaration', async () => {
|
|
209
|
+
const documents = await parseDocumentsTogether(['main.rq', 'exampleimport.rq', 'sub idea.rq']);
|
|
210
|
+
const subDocument = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
211
|
+
const importedDocument = documents.find(entry => entry.uri.path.endsWith('exampleimport.rq'));
|
|
212
|
+
expect(subDocument).toBeDefined();
|
|
213
|
+
expect(importedDocument).toBeDefined();
|
|
214
|
+
if (!subDocument || !importedDocument) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const ideaLink = [...AstUtils.streamAst(subDocument.parseResult.value)]
|
|
218
|
+
.filter(isWikiLink)
|
|
219
|
+
.map(link => link.target)
|
|
220
|
+
.find((target) => isQualifiedReference(target) && !!target.path?.ref);
|
|
221
|
+
expect(ideaLink?.idea?.$refNode).toBeDefined();
|
|
222
|
+
if (!ideaLink?.idea?.$refNode) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const links = await services.Reqlan.lsp.DefinitionProvider?.getDefinition(subDocument, {
|
|
226
|
+
textDocument: { uri: subDocument.textDocument.uri },
|
|
227
|
+
position: subDocument.textDocument.positionAt(ideaLink.idea.$refNode.offset)
|
|
228
|
+
});
|
|
229
|
+
expect(links).toHaveLength(1);
|
|
230
|
+
expect(links?.[0].targetUri).toBe(importedDocument.textDocument.uri);
|
|
231
|
+
expect(importedDocument.textDocument.getText(links[0].targetSelectionRange)).toBe('myimportableIdea');
|
|
232
|
+
});
|
|
233
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".refactor_support]
|
|
234
|
+
test('rename finds import path declaration and qualified wikilink references', async () => {
|
|
235
|
+
const documents = await parseDocumentsTogether(['main.rq', 'exampleimport.rq', 'sub idea.rq']);
|
|
236
|
+
document = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
237
|
+
expect(document).toBeDefined();
|
|
238
|
+
if (!document) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const importDecl = document.parseResult.value.imports.find(entry => isNamespaceImport(entry) && entry.path === './exampleimport.rq' && !entry.alias);
|
|
242
|
+
expect(importDecl).toBeDefined();
|
|
243
|
+
if (!importDecl) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const references = services.Reqlan.references.References
|
|
247
|
+
.findReferences(importDecl, { includeDeclaration: true })
|
|
248
|
+
.toArray();
|
|
249
|
+
const pathTexts = references
|
|
250
|
+
.map(reference => document.textDocument.getText(reference.segment.range))
|
|
251
|
+
.filter(text => text.includes('exampleimport.rq'));
|
|
252
|
+
expect(pathTexts.length).toBeGreaterThanOrEqual(2);
|
|
253
|
+
});
|
|
254
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".refactor_support]
|
|
255
|
+
test('rename finds import alias declaration and qualified wikilink references', async () => {
|
|
256
|
+
const documents = await parseDocumentsTogether(['exampleimport2.rq', 'sub idea.rq']);
|
|
257
|
+
document = documents.find(entry => entry.uri.path.endsWith('sub idea.rq'));
|
|
258
|
+
expect(document).toBeDefined();
|
|
259
|
+
if (!document) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const importDecl = document.parseResult.value.imports.find(entry => isNamespaceImport(entry) && entry.alias === 'exampleimport2');
|
|
263
|
+
expect(importDecl).toBeDefined();
|
|
264
|
+
if (!importDecl) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const references = services.Reqlan.references.References
|
|
268
|
+
.findReferences(importDecl, { includeDeclaration: true })
|
|
269
|
+
.toArray();
|
|
270
|
+
const texts = references.map(reference => document.textDocument.getText(reference.segment.range));
|
|
271
|
+
expect(texts.filter(text => text === 'exampleimport2').length).toBeGreaterThanOrEqual(2);
|
|
272
|
+
});
|
|
273
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".sensible_graph_resolution]
|
|
274
|
+
test('import idea reference resolves to imported file when local idea shares name', async () => {
|
|
275
|
+
const subreqs = services.shared.workspace.LangiumDocumentFactory.fromString('myidea imported body', URI.parse('file:///tmp/subreqs.rq'));
|
|
276
|
+
const consumer = services.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
277
|
+
from "subreqs.rq" import myidea as myideaalias
|
|
278
|
+
myidea local body
|
|
279
|
+
`, URI.parse('file:///tmp/consumer.rq'));
|
|
280
|
+
services.shared.workspace.LangiumDocuments.addDocument(subreqs);
|
|
281
|
+
services.shared.workspace.LangiumDocuments.addDocument(consumer);
|
|
282
|
+
await services.shared.workspace.DocumentBuilder.build([subreqs, consumer], { validation: false });
|
|
283
|
+
const fromImport = consumer.parseResult.value.imports.find(isFromImport);
|
|
284
|
+
const localIdea = consumer.parseResult.value.elements.find(element => isOneLinerIdea(element) && element.name === 'myidea');
|
|
285
|
+
expect(fromImport?.specifiers[0]?.idea.ref && AstUtils.getDocument(fromImport.specifiers[0].idea.ref).uri.path).toBe('/tmp/subreqs.rq');
|
|
286
|
+
expect(fromImport?.specifiers[0]?.idea.ref?.name).toBe('myidea');
|
|
287
|
+
expect(localIdea).toBeDefined();
|
|
288
|
+
expect(fromImport?.specifiers[0]?.idea.ref).not.toBe(localIdea);
|
|
289
|
+
});
|
|
290
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".refactor_support]
|
|
291
|
+
test('rename finds all idea references including wikilinks', async () => {
|
|
292
|
+
document = await parse(`alpha {
|
|
293
|
+
see [[beta]]
|
|
294
|
+
}
|
|
295
|
+
beta {
|
|
296
|
+
see [[alpha]]
|
|
297
|
+
}`);
|
|
298
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
299
|
+
const beta = [...AstUtils.streamAst(document.parseResult.value)].find(node => isIdea(node) && node.name === 'beta');
|
|
300
|
+
expect(beta && isIdea(beta)).toBe(true);
|
|
301
|
+
if (!beta || !isIdea(beta)) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const references = services.Reqlan.references.References
|
|
305
|
+
.findReferences(beta, { includeDeclaration: true })
|
|
306
|
+
.toArray();
|
|
307
|
+
const texts = references.map(reference => document.textDocument.getText(reference.segment.range));
|
|
308
|
+
expect(texts.filter(text => text === 'beta').length).toBeGreaterThanOrEqual(2);
|
|
309
|
+
});
|
|
310
|
+
// rq:["../../../reqlan rq/language/syntax.rq".same_file_reference]
|
|
311
|
+
test('resolve forward reference to later idea in the same file', async () => {
|
|
312
|
+
document = await parse(`earlier {
|
|
313
|
+
see [later_idea] before it is declared.
|
|
314
|
+
}
|
|
315
|
+
later_idea {
|
|
316
|
+
body
|
|
317
|
+
}`);
|
|
318
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
319
|
+
const bracketRef = [...AstUtils.streamAst(document.parseResult.value)]
|
|
320
|
+
.filter(isBracketReference)[0];
|
|
321
|
+
expect(bracketRef).toBeDefined();
|
|
322
|
+
const target = bracketRef?.target;
|
|
323
|
+
expect(isLocalReference(target) && target.idea?.ref?.name).toBe('later_idea');
|
|
324
|
+
expect(isLocalReference(target) ? target.idea?.error : undefined).toBeUndefined();
|
|
325
|
+
const unresolved = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
326
|
+
&& diagnostic.message.includes('Could not resolve reference'));
|
|
327
|
+
expect(unresolved).toHaveLength(0);
|
|
328
|
+
});
|
|
329
|
+
// rq:["../../../reqlan rq/language/syntax.rq".same_file_reference]
|
|
330
|
+
test('resolve circular same-file references', async () => {
|
|
331
|
+
document = await parse(`alpha {
|
|
332
|
+
see [[beta]]
|
|
333
|
+
}
|
|
334
|
+
beta {
|
|
335
|
+
see [[alpha]]
|
|
336
|
+
}`);
|
|
337
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
338
|
+
const wikilinks = [...AstUtils.streamAst(document.parseResult.value)].filter(isWikiLink);
|
|
339
|
+
expect(wikilinks).toHaveLength(2);
|
|
340
|
+
for (const link of wikilinks) {
|
|
341
|
+
const target = link.target;
|
|
342
|
+
if (isLocalReference(target)) {
|
|
343
|
+
expect(target.idea?.error).toBeUndefined();
|
|
344
|
+
expect(target.idea?.ref?.name).toBeDefined();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const unresolved = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
348
|
+
&& diagnostic.message.includes('Could not resolve reference'));
|
|
349
|
+
expect(unresolved).toHaveLength(0);
|
|
350
|
+
});
|
|
351
|
+
// rq:["../../../reqlan rq/language/syntax.rq".reference_brackets]
|
|
352
|
+
test('resolve ideaset member forward reference', async () => {
|
|
353
|
+
document = await parse(`demo {
|
|
354
|
+
see [my_ideaset.later_member]
|
|
355
|
+
}
|
|
356
|
+
my_ideaset (
|
|
357
|
+
later_member
|
|
358
|
+
)
|
|
359
|
+
later_member {
|
|
360
|
+
body
|
|
361
|
+
}`);
|
|
362
|
+
await services.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
363
|
+
const bracketRef = [...AstUtils.streamAst(document.parseResult.value)]
|
|
364
|
+
.filter(isBracketReference)[0];
|
|
365
|
+
const target = bracketRef?.target;
|
|
366
|
+
const qualifierRef = isQualifiedReference(target) ? target.qualifier?.ref : undefined;
|
|
367
|
+
expect(qualifierRef && isIdeaSet(qualifierRef) && qualifierRef.name).toBe('my_ideaset');
|
|
368
|
+
expect(isQualifiedReference(target) && target.idea?.ref?.name).toBe('later_member');
|
|
369
|
+
const unresolved = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
370
|
+
&& diagnostic.message.includes('Could not resolve reference'));
|
|
371
|
+
expect(unresolved).toHaveLength(0);
|
|
372
|
+
});
|
|
373
|
+
// rq:["../../../reqlan rq/language/imports.rq".anonymous_imports_allowed]
|
|
374
|
+
test('resolve anonymous import in bracket reference without import statement', async () => {
|
|
375
|
+
const ontologyDir = join(repoDir, 'reqlan rq/language');
|
|
376
|
+
const consumer = services.shared.workspace.LangiumDocumentFactory.fromString(`syntax_whitespace {
|
|
377
|
+
see ["./ontology.rq".attribute]
|
|
378
|
+
}`, URI.parse(pathToFileURL(join(ontologyDir, 'consumer.rq')).href));
|
|
379
|
+
const ontology = services.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(join(ontologyDir, 'ontology.rq'), 'utf8'), URI.parse(pathToFileURL(join(ontologyDir, 'ontology.rq')).href));
|
|
380
|
+
services.shared.workspace.LangiumDocuments.addDocument(ontology);
|
|
381
|
+
services.shared.workspace.LangiumDocuments.addDocument(consumer);
|
|
382
|
+
await services.shared.workspace.DocumentBuilder.build([ontology, consumer], { validation: false });
|
|
383
|
+
const bracketRef = [...AstUtils.streamAst(consumer.parseResult.value)]
|
|
384
|
+
.filter(isBracketReference)
|
|
385
|
+
.find(ref => isQualifiedReference(ref.target));
|
|
386
|
+
expect(bracketRef && isQualifiedReference(bracketRef.target)).toBe(true);
|
|
387
|
+
if (!bracketRef || !isQualifiedReference(bracketRef.target) || !bracketRef.target.idea) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
expect(bracketRef.target.path?.ref).toBeUndefined();
|
|
391
|
+
expect(bracketRef.target.idea.ref?.name).toBe('attribute');
|
|
392
|
+
expect(bracketRef.target.idea.error).toBeUndefined();
|
|
393
|
+
expect(AstUtils.getDocument(bracketRef.target.idea.ref).uri.path).toContain('ontology.rq');
|
|
394
|
+
});
|
|
395
|
+
// rq:["../../../reqlan rq/language/imports.rq".anonymous_imports_allowed]
|
|
396
|
+
test('go to definition on anonymous import path opens source file', async () => {
|
|
397
|
+
const ontologyDir = join(repoDir, 'reqlan rq/language');
|
|
398
|
+
const consumer = services.shared.workspace.LangiumDocumentFactory.fromString(`demo {
|
|
399
|
+
see ["./ontology.rq".attribute]
|
|
400
|
+
}`, URI.parse(pathToFileURL(join(ontologyDir, 'consumer.rq')).href));
|
|
401
|
+
const ontology = services.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(join(ontologyDir, 'ontology.rq'), 'utf8'), URI.parse(pathToFileURL(join(ontologyDir, 'ontology.rq')).href));
|
|
402
|
+
services.shared.workspace.LangiumDocuments.addDocument(ontology);
|
|
403
|
+
services.shared.workspace.LangiumDocuments.addDocument(consumer);
|
|
404
|
+
await services.shared.workspace.DocumentBuilder.build([ontology, consumer], { validation: false });
|
|
405
|
+
const bracketRef = [...AstUtils.streamAst(consumer.parseResult.value)]
|
|
406
|
+
.filter(isBracketReference)
|
|
407
|
+
.find(ref => isQualifiedReference(ref.target) && ref.target.path?.$refNode);
|
|
408
|
+
expect(bracketRef).toBeDefined();
|
|
409
|
+
if (!bracketRef || !isQualifiedReference(bracketRef.target)) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const pathNode = GrammarUtils.findNodeForProperty(bracketRef.target.$cstNode, 'path');
|
|
413
|
+
expect(pathNode).toBeDefined();
|
|
414
|
+
if (!pathNode) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const offset = pathNode.offset + 2;
|
|
418
|
+
expect(offset).toBeGreaterThanOrEqual(pathNode.offset);
|
|
419
|
+
expect(offset).toBeLessThan(pathNode.end);
|
|
420
|
+
expect(bracketRef.target.path?.ref).toBeUndefined();
|
|
421
|
+
const links = await services.Reqlan.lsp.DefinitionProvider?.getDefinition(consumer, {
|
|
422
|
+
textDocument: { uri: consumer.textDocument.uri },
|
|
423
|
+
position: consumer.textDocument.positionAt(offset)
|
|
424
|
+
});
|
|
425
|
+
expect(links).toHaveLength(1);
|
|
426
|
+
expect(links?.[0].targetUri).toBe(ontology.textDocument.uri);
|
|
427
|
+
});
|
|
428
|
+
// rq:["../../../reqlan rq/language/imports.rq".anonymous_imports_allowed]
|
|
429
|
+
test('go to definition on anonymous import idea opens idea declaration', async () => {
|
|
430
|
+
const ontologyDir = join(repoDir, 'reqlan rq/language');
|
|
431
|
+
const consumer = services.shared.workspace.LangiumDocumentFactory.fromString(`demo {
|
|
432
|
+
see ["./ontology.rq".attribute]
|
|
433
|
+
}`, URI.parse(pathToFileURL(join(ontologyDir, 'consumer.rq')).href));
|
|
434
|
+
const ontology = services.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(join(ontologyDir, 'ontology.rq'), 'utf8'), URI.parse(pathToFileURL(join(ontologyDir, 'ontology.rq')).href));
|
|
435
|
+
services.shared.workspace.LangiumDocuments.addDocument(ontology);
|
|
436
|
+
services.shared.workspace.LangiumDocuments.addDocument(consumer);
|
|
437
|
+
await services.shared.workspace.DocumentBuilder.build([ontology, consumer], { validation: false });
|
|
438
|
+
const bracketRef = [...AstUtils.streamAst(consumer.parseResult.value)]
|
|
439
|
+
.filter(isBracketReference)
|
|
440
|
+
.find(ref => isQualifiedReference(ref.target) && ref.target.idea?.$refNode);
|
|
441
|
+
expect(bracketRef).toBeDefined();
|
|
442
|
+
if (!bracketRef || !isQualifiedReference(bracketRef.target) || !bracketRef.target.idea?.$refNode) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const links = await services.Reqlan.lsp.DefinitionProvider?.getDefinition(consumer, {
|
|
446
|
+
textDocument: { uri: consumer.textDocument.uri },
|
|
447
|
+
position: consumer.textDocument.positionAt(bracketRef.target.idea.$refNode.offset)
|
|
448
|
+
});
|
|
449
|
+
expect(links).toHaveLength(1);
|
|
450
|
+
expect(links?.[0].targetUri).toBe(ontology.textDocument.uri);
|
|
451
|
+
expect(ontology.textDocument.getText(links[0].targetSelectionRange)).toBe('attribute');
|
|
452
|
+
});
|
|
453
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
454
|
+
test('document links resolve test file references to the matching test', async () => {
|
|
455
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
456
|
+
const featuresPath = join(repoDir, 'reqlan rq/extension/syntax/features-syntax.rq');
|
|
457
|
+
const validatingPath = join(repoDir, 'packages/language/test/validating.test.ts');
|
|
458
|
+
const testName = 'reports duplicate when local idea shares imported idea name';
|
|
459
|
+
const testLine = findTestLineInText(readFileSync(validatingPath, 'utf8'), testName);
|
|
460
|
+
expect(testLine).toBeDefined();
|
|
461
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(featuresPath, 'utf8'), URI.parse(pathToFileURL(featuresPath).href));
|
|
462
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
463
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
464
|
+
const links = await fileServices.Reqlan.lsp.DocumentLinkProvider?.getDocumentLinks(document, {
|
|
465
|
+
textDocument: { uri: document.textDocument.uri }
|
|
466
|
+
});
|
|
467
|
+
const expectedLineFragment = `#L${testLine + 1}`;
|
|
468
|
+
const matchingLink = links?.find(link => link.target?.includes('validating.test.ts') && link.target.includes(expectedLineFragment));
|
|
469
|
+
expect(matchingLink?.target).toContain('validating.test.ts');
|
|
470
|
+
expect(matchingLink?.target).toContain(expectedLineFragment);
|
|
471
|
+
const position = {
|
|
472
|
+
line: matchingLink.range.start.line,
|
|
473
|
+
character: matchingLink.range.start.character + 5
|
|
474
|
+
};
|
|
475
|
+
const definitions = await fileServices.Reqlan.lsp.DefinitionProvider?.getDefinition(document, {
|
|
476
|
+
textDocument: { uri: document.textDocument.uri },
|
|
477
|
+
position
|
|
478
|
+
});
|
|
479
|
+
expect(definitions).toHaveLength(1);
|
|
480
|
+
expect(definitions?.[0].targetUri).toContain('validating.test.ts');
|
|
481
|
+
expect(definitions?.[0].targetSelectionRange?.start.line).toBe(testLine);
|
|
482
|
+
});
|
|
483
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
484
|
+
test('classifyReferenceUri treats existing directories as folders', async () => {
|
|
485
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
486
|
+
const sourcePath = join(repoDir, 'reqlan rq/extension/features-syntax-highlighting.rq');
|
|
487
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
488
|
+
folder_reference {
|
|
489
|
+
see ["./module"]
|
|
490
|
+
}
|
|
491
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
492
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
493
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
494
|
+
const targetUri = resolveFileUri('./module', document);
|
|
495
|
+
const resolution = classifyReferenceUri(targetUri, fileServices.shared.workspace.LangiumDocuments, fileServices.shared.workspace.FileSystemProvider);
|
|
496
|
+
expect(resolution).toBe('folder');
|
|
497
|
+
});
|
|
498
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
499
|
+
test('folder file references resolve without reading the directory as a file', async () => {
|
|
500
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
501
|
+
const sourcePath = join(repoDir, 'reqlan rq/extension/features-syntax-highlighting.rq');
|
|
502
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
503
|
+
folder_reference {
|
|
504
|
+
see ["./module"]
|
|
505
|
+
}
|
|
506
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
507
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
508
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
509
|
+
const position = { line: 1, character: 12 };
|
|
510
|
+
const definitions = await fileServices.Reqlan.lsp.DefinitionProvider?.getDefinition(document, {
|
|
511
|
+
textDocument: { uri: document.textDocument.uri },
|
|
512
|
+
position
|
|
513
|
+
});
|
|
514
|
+
expect(definitions).toBeUndefined();
|
|
515
|
+
const links = await fileServices.Reqlan.lsp.DocumentLinkProvider?.getDocumentLinks(document, {
|
|
516
|
+
textDocument: { uri: document.textDocument.uri }
|
|
517
|
+
});
|
|
518
|
+
expect(links?.some(link => link.target?.includes('reqlan.openFolderReference'))).toBe(true);
|
|
519
|
+
});
|
|
520
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
521
|
+
test('empty file references produce document links and warning diagnostics', async () => {
|
|
522
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
523
|
+
const fixturesDir = join(repoDir, 'packages/language/test/fixtures/file-ref-targets');
|
|
524
|
+
mkdirSync(fixturesDir, { recursive: true });
|
|
525
|
+
const sourcePath = join(fixturesDir, 'source.rq');
|
|
526
|
+
const emptyPath = join(fixturesDir, 'empty.ts');
|
|
527
|
+
writeFileSync(emptyPath, '');
|
|
528
|
+
try {
|
|
529
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
530
|
+
empty_reference {
|
|
531
|
+
see ["./empty.ts"]
|
|
532
|
+
}
|
|
533
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
534
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
535
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
536
|
+
const linkProvider = new ReqlanDocumentLinkProvider(fileServices.Reqlan);
|
|
537
|
+
const links = linkProvider.getDocumentLinks(document, {
|
|
538
|
+
textDocument: { uri: document.textDocument.uri }
|
|
539
|
+
});
|
|
540
|
+
expect(links.some(link => link.target?.includes('empty.ts'))).toBe(true);
|
|
541
|
+
const warnings = (document.diagnostics ?? []).filter(diagnostic => diagnostic.message === 'Referenced file is empty.');
|
|
542
|
+
expect(warnings).toHaveLength(1);
|
|
543
|
+
expect(warnings[0]?.severity).toBe(2);
|
|
544
|
+
const definitions = await fileServices.Reqlan.lsp.DefinitionProvider?.getDefinition(document, {
|
|
545
|
+
textDocument: { uri: document.textDocument.uri },
|
|
546
|
+
position: { line: 1, character: 12 }
|
|
547
|
+
});
|
|
548
|
+
expect(definitions).toHaveLength(1);
|
|
549
|
+
expect(definitions?.[0].targetUri).toContain('empty.ts');
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
unlinkSync(emptyPath);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
556
|
+
test('parse-error file references produce document links and warning diagnostics', async () => {
|
|
557
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
558
|
+
const fixturesDir = join(repoDir, 'packages/language/test/fixtures/file-ref-targets');
|
|
559
|
+
mkdirSync(fixturesDir, { recursive: true });
|
|
560
|
+
const sourcePath = join(fixturesDir, 'source.rq');
|
|
561
|
+
const brokenPath = join(fixturesDir, 'broken.rq');
|
|
562
|
+
writeFileSync(brokenPath, 'from "x" import');
|
|
563
|
+
const targetDoc = fileServices.shared.workspace.LangiumDocumentFactory.fromString('from "x" import', URI.parse(pathToFileURL(brokenPath).href));
|
|
564
|
+
try {
|
|
565
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
566
|
+
broken_reference {
|
|
567
|
+
see ["./broken.rq"]
|
|
568
|
+
}
|
|
569
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
570
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(targetDoc);
|
|
571
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
572
|
+
await fileServices.shared.workspace.DocumentBuilder.build([targetDoc, document], { validation: true });
|
|
573
|
+
expect(targetDoc.parseResult.parserErrors.length).toBeGreaterThan(0);
|
|
574
|
+
const linkProvider = new ReqlanDocumentLinkProvider(fileServices.Reqlan);
|
|
575
|
+
const links = linkProvider.getDocumentLinks(document, {
|
|
576
|
+
textDocument: { uri: document.textDocument.uri }
|
|
577
|
+
});
|
|
578
|
+
expect(links.some(link => link.target?.includes('broken.rq'))).toBe(true);
|
|
579
|
+
const warnings = (document.diagnostics ?? []).filter(diagnostic => diagnostic.message === 'Referenced file has parse errors.');
|
|
580
|
+
expect(warnings).toHaveLength(1);
|
|
581
|
+
expect(warnings[0]?.severity).toBe(2);
|
|
582
|
+
}
|
|
583
|
+
finally {
|
|
584
|
+
unlinkSync(brokenPath);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
588
|
+
test('missing file references do not produce document links', async () => {
|
|
589
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
590
|
+
const sourcePath = join(repoDir, 'reqlan rq/extension/features-syntax-highlighting.rq');
|
|
591
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
592
|
+
missing_reference {
|
|
593
|
+
see ["./does-not-exist.rq"]
|
|
594
|
+
}
|
|
595
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
596
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
597
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
598
|
+
const links = await fileServices.Reqlan.lsp.DocumentLinkProvider?.getDocumentLinks(document, {
|
|
599
|
+
textDocument: { uri: document.textDocument.uri }
|
|
600
|
+
});
|
|
601
|
+
expect(links).toHaveLength(0);
|
|
602
|
+
});
|
|
603
|
+
// rq:["../../../reqlan rq/extension/features-non-rq-code-comment/functional-code-comment-references.rq".references_in_functional_code_comments]
|
|
604
|
+
test('comment references in source files resolve to rq ideas', async () => {
|
|
605
|
+
const demoDir = join(repoDir, 'reqlan rq/extension/features-non-rq-code-comment');
|
|
606
|
+
const rqPath = join(demoDir, 'functional-code-comment-references.rq');
|
|
607
|
+
const jsPath = join(demoDir, 'features-code-comment.text.js');
|
|
608
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
609
|
+
const rqDoc = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(rqPath, 'utf8'), URI.parse(pathToFileURL(rqPath).href));
|
|
610
|
+
const jsDoc = createSourceTextDocument(pathToFileURL(jsPath).href, readFileSync(jsPath, 'utf8'));
|
|
611
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(rqDoc);
|
|
612
|
+
await fileServices.shared.workspace.DocumentBuilder.build([rqDoc], { validation: false });
|
|
613
|
+
const refs = findCommentReferencesInText(readFileSync(jsPath, 'utf8'));
|
|
614
|
+
expect(refs.length).toBeGreaterThan(0);
|
|
615
|
+
const links = findCommentDefinitionAtPosition(jsDoc, refs[0].range.start, fileServices.shared.workspace.LangiumDocuments);
|
|
616
|
+
expect(links).toHaveLength(1);
|
|
617
|
+
expect(links?.[0]?.targetUri).toBe(rqDoc.textDocument.uri);
|
|
618
|
+
});
|
|
619
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".file_references]
|
|
620
|
+
test('resolve namespace import references to files', async () => {
|
|
621
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
622
|
+
const targetPath = join(repoDir, 'packages/extension/webviews/ideas-summary/components/IndexPanel.svelte');
|
|
623
|
+
const sourcePath = join(repoDir, 'reqlan rq/extension/module/webview.rq');
|
|
624
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
625
|
+
import "${targetPath.replace(/\\/g, '/')}" as IndexPanel
|
|
626
|
+
demo {
|
|
627
|
+
see [IndexPanel]
|
|
628
|
+
}
|
|
629
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
630
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
631
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
632
|
+
const namespaceRef = [...AstUtils.streamAst(document.parseResult.value)]
|
|
633
|
+
.filter(isBracketReference)
|
|
634
|
+
.map(ref => ref.target)
|
|
635
|
+
.find((target) => (isLocalReference(target) || isQualifiedReference(target))
|
|
636
|
+
&& isNamespaceImportOnlyReference(target));
|
|
637
|
+
expect(namespaceRef).toBeDefined();
|
|
638
|
+
if (!namespaceRef) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
expect(isNamespaceImportOnlyReference(namespaceRef)).toBe(true);
|
|
642
|
+
const link = resolveNamespaceImportReferenceLink(namespaceRef, fileServices.shared.workspace.LangiumDocuments, fileServices.shared.workspace.FileSystemProvider);
|
|
643
|
+
expect(link?.targetUri).toContain('IndexPanel.svelte');
|
|
644
|
+
});
|
|
645
|
+
// rq:["../../../reqlan rq/language/imports.rq".import_namespace]
|
|
646
|
+
test('namespace import bracket reference links only the alias name', async () => {
|
|
647
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
648
|
+
const targetPath = join(repoDir, 'packages/extension/src/activity_bar_module/context-model.ts');
|
|
649
|
+
const sourcePath = join(repoDir, 'reqlan rq/extension/module/context-scope.rq');
|
|
650
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
651
|
+
import "${targetPath.replace(/\\/g, '/')}" as context_model
|
|
652
|
+
any_file_scope {
|
|
653
|
+
1. [any_file_scope] types and [context_model] builder.
|
|
654
|
+
}
|
|
655
|
+
`, URI.parse(pathToFileURL(sourcePath).href));
|
|
656
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
657
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
658
|
+
const namespaceRef = [...AstUtils.streamAst(document.parseResult.value)]
|
|
659
|
+
.filter(isBracketReference)
|
|
660
|
+
.map(ref => ref.target)
|
|
661
|
+
.find((target) => isLocalReference(target) && target.idea?.$refText === 'context_model');
|
|
662
|
+
expect(namespaceRef).toBeDefined();
|
|
663
|
+
if (!namespaceRef) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
expect(isNamespaceImportOnlyReference(namespaceRef)).toBe(true);
|
|
667
|
+
const link = resolveNamespaceImportReferenceLink(namespaceRef, fileServices.shared.workspace.LangiumDocuments, fileServices.shared.workspace.FileSystemProvider);
|
|
668
|
+
expect(link?.targetUri).toContain('context-model.ts');
|
|
669
|
+
expect(document.textDocument.getText(link.sourceRange)).toBe('context_model');
|
|
670
|
+
const documentLinks = await fileServices.Reqlan.lsp.DocumentLinkProvider?.getDocumentLinks(document, {
|
|
671
|
+
textDocument: { uri: document.textDocument.uri }
|
|
672
|
+
});
|
|
673
|
+
const contextModelLink = documentLinks?.find(entry => document.textDocument.getText(entry.range) === 'context_model');
|
|
674
|
+
expect(contextModelLink?.target).toContain('context-model.ts');
|
|
675
|
+
const definitions = await fileServices.Reqlan.lsp.DefinitionProvider?.getDefinition(document, {
|
|
676
|
+
textDocument: { uri: document.textDocument.uri },
|
|
677
|
+
position: namespaceRef.idea.$refNode.range.start
|
|
678
|
+
});
|
|
679
|
+
expect(definitions).toHaveLength(1);
|
|
680
|
+
expect(definitions?.[0].targetUri).toContain('context-model.ts');
|
|
681
|
+
expect(definitions?.[0].targetSelectionRange?.start).toEqual({ line: 0, character: 0 });
|
|
682
|
+
});
|
|
683
|
+
// rq:["../../../reqlan rq/language/imports.rq".anonymous_imports_allowed]
|
|
684
|
+
test('does not report linking error for anonymous import path only', async () => {
|
|
685
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
686
|
+
const corePath = join(repoDir, 'site/reqs/core.rq');
|
|
687
|
+
const brandPath = join(repoDir, 'reqlan rq/brand.rq');
|
|
688
|
+
const core = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(corePath, 'utf8'), URI.parse(pathToFileURL(corePath).href));
|
|
689
|
+
const brand = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(brandPath, 'utf8'), URI.parse(pathToFileURL(brandPath).href));
|
|
690
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(brand);
|
|
691
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(core);
|
|
692
|
+
await fileServices.shared.workspace.DocumentBuilder.build([brand, core], { validation: true });
|
|
693
|
+
const importPathErrors = (core.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
694
|
+
&& diagnostic.message.includes("Could not resolve reference to Import"));
|
|
695
|
+
expect(importPathErrors).toHaveLength(0);
|
|
696
|
+
});
|
|
697
|
+
// rq:["../../../reqlan rq/language/imports.rq".anonymous_imports_allowed]
|
|
698
|
+
test('does not report linking error for anonymous import path with idea', async () => {
|
|
699
|
+
const ontologyDir = join(repoDir, 'reqlan rq/language');
|
|
700
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
701
|
+
const consumer = fileServices.shared.workspace.LangiumDocumentFactory.fromString(`syntax_whitespace {
|
|
702
|
+
see ["./ontology.rq".attribute]
|
|
703
|
+
}`, URI.parse(pathToFileURL(join(ontologyDir, 'consumer.rq')).href));
|
|
704
|
+
const ontology = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(join(ontologyDir, 'ontology.rq'), 'utf8'), URI.parse(pathToFileURL(join(ontologyDir, 'ontology.rq')).href));
|
|
705
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(ontology);
|
|
706
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(consumer);
|
|
707
|
+
await fileServices.shared.workspace.DocumentBuilder.build([ontology, consumer], { validation: true });
|
|
708
|
+
const importPathErrors = (consumer.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
709
|
+
&& diagnostic.message.includes("Could not resolve reference to Import"));
|
|
710
|
+
expect(importPathErrors).toHaveLength(0);
|
|
711
|
+
});
|
|
712
|
+
// rq:["../../../reqlan rq/language/imports.rq".import_namespace]
|
|
713
|
+
test('does not report linking error for bare namespace alias references', async () => {
|
|
714
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
715
|
+
const svelteTarget = join(repoDir, 'packages/extension/webviews/activity-bar/components/NestedSection.svelte')
|
|
716
|
+
.replace(/\\/g, '/');
|
|
717
|
+
const rqTarget = join(repoDir, 'reqlan rq/brand.rq');
|
|
718
|
+
const brand = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(rqTarget, 'utf8'), URI.parse(pathToFileURL(rqTarget).href));
|
|
719
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(s `
|
|
720
|
+
import "${svelteTarget}" as NestedSection
|
|
721
|
+
import "${rqTarget.replace(/\\/g, '/')}" as brand
|
|
722
|
+
styling {
|
|
723
|
+
Nested lists use [NestedSection] per [brand].
|
|
724
|
+
}
|
|
725
|
+
`, URI.parse(pathToFileURL(join(repoDir, 'reqlan rq/extension/module/namespace-alias.rq')).href));
|
|
726
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(brand);
|
|
727
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
728
|
+
await fileServices.shared.workspace.DocumentBuilder.build([brand, document], { validation: true });
|
|
729
|
+
const unresolvedErrors = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
730
|
+
&& diagnostic.message.includes('Could not resolve reference to IdeaDeclaration'));
|
|
731
|
+
expect(unresolvedErrors).toHaveLength(0);
|
|
732
|
+
});
|
|
733
|
+
// rq:["../../../reqlan rq/extension/syntax/features-syntax.rq".duplicate_error]
|
|
734
|
+
test('does not report linking error for @tests file references with test name suffix', async () => {
|
|
735
|
+
const fileServices = createReqlanServices(NodeFileSystem);
|
|
736
|
+
const featuresPath = join(repoDir, 'reqlan rq/extension/syntax/features-syntax.rq');
|
|
737
|
+
const document = fileServices.shared.workspace.LangiumDocumentFactory.fromString(readFileSync(featuresPath, 'utf8'), URI.parse(pathToFileURL(featuresPath).href));
|
|
738
|
+
fileServices.shared.workspace.LangiumDocuments.addDocument(document);
|
|
739
|
+
await fileServices.shared.workspace.DocumentBuilder.build([document], { validation: true });
|
|
740
|
+
const importPathErrors = (document.diagnostics ?? []).filter(diagnostic => typeof diagnostic.message === 'string'
|
|
741
|
+
&& diagnostic.message.includes("Could not resolve reference to Import"));
|
|
742
|
+
expect(importPathErrors).toHaveLength(0);
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
function checkDocumentValid(document) {
|
|
746
|
+
return document.parseResult.parserErrors.length && s `
|
|
747
|
+
Parser errors:
|
|
748
|
+
${document.parseResult.parserErrors.map(e => e.message).join('\n ')}
|
|
749
|
+
`
|
|
750
|
+
|| document.parseResult.value === undefined && `ParseResult is 'undefined'.`
|
|
751
|
+
|| !isModel(document.parseResult.value) && `Root AST object is a ${document.parseResult.value.$type}, expected a 'Model'.`
|
|
752
|
+
|| undefined;
|
|
753
|
+
}
|
|
754
|
+
//# sourceMappingURL=linking.test.js.map
|