@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,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves file references and import paths to target URIs and editor ranges.
|
|
3
|
+
*/
|
|
4
|
+
import type { CstNode, FileSystemProvider, LangiumDocument, LangiumDocuments, URI } from 'langium';
|
|
5
|
+
import { AstUtils, CstUtils, GrammarUtils } from 'langium';
|
|
6
|
+
import type { Range } from 'vscode-languageserver';
|
|
7
|
+
import { resolveFileUri } from './reqlan-comment-resolver.js';
|
|
8
|
+
import type { EmbeddedFileReference } from './reqlan-embedded-file-references.js';
|
|
9
|
+
import { findEmbeddedFileReferencesInText } from './reqlan-embedded-file-references.js';
|
|
10
|
+
import { findImportedDocument, resolveExistingImportUri } from './reqlan-imports.js';
|
|
11
|
+
import type { PathResolveContext } from './reqlan-path-resolve.js';
|
|
12
|
+
import { qualifiedReferenceImportPath } from './reqlan-references.js';
|
|
13
|
+
import {
|
|
14
|
+
findTestLineInText,
|
|
15
|
+
parseFileReferenceString,
|
|
16
|
+
type ParsedFileReference
|
|
17
|
+
} from './reqlan-file-references.js';
|
|
18
|
+
import {
|
|
19
|
+
isFileReference,
|
|
20
|
+
isFileSymbolReference,
|
|
21
|
+
isImport,
|
|
22
|
+
isLocalReference,
|
|
23
|
+
isMarkdownLink,
|
|
24
|
+
isQualifiedReference,
|
|
25
|
+
type FileReference,
|
|
26
|
+
type FileSymbolReference,
|
|
27
|
+
type Import,
|
|
28
|
+
type MarkdownLink,
|
|
29
|
+
type QualifiedReference
|
|
30
|
+
} from './generated/ast.js';
|
|
31
|
+
import { parseMarkdownLink } from './reqlan-references.js';
|
|
32
|
+
import { reqlanStringDelimiter } from './reqlan-quoted-strings.js';
|
|
33
|
+
import {
|
|
34
|
+
isNamespaceImportOnlyReference,
|
|
35
|
+
resolveNamespaceImportReferenceLink
|
|
36
|
+
} from './reqlan-namespace-import-links.js';
|
|
37
|
+
|
|
38
|
+
export type ReferenceResolution = 'file' | 'folder' | 'missing';
|
|
39
|
+
|
|
40
|
+
export type FileLinkTargetIssue = 'empty' | 'parse-error';
|
|
41
|
+
|
|
42
|
+
export interface ResolvedFileLink {
|
|
43
|
+
sourceRange: Range;
|
|
44
|
+
targetUri: string;
|
|
45
|
+
targetRange?: Range;
|
|
46
|
+
resolution?: ReferenceResolution;
|
|
47
|
+
folderFiles?: string[];
|
|
48
|
+
targetIssue?: FileLinkTargetIssue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function withFileSystem(
|
|
52
|
+
fileSystem: FileSystemProvider,
|
|
53
|
+
context?: PathResolveContext
|
|
54
|
+
): PathResolveContext {
|
|
55
|
+
return { ...context, fileSystem: context?.fileSystem ?? fileSystem };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function classifyReferenceUri(
|
|
59
|
+
targetUri: URI,
|
|
60
|
+
documents: LangiumDocuments,
|
|
61
|
+
fileSystem: FileSystemProvider
|
|
62
|
+
): ReferenceResolution {
|
|
63
|
+
if (findTargetDocument(targetUri, documents)) {
|
|
64
|
+
return 'file';
|
|
65
|
+
}
|
|
66
|
+
if (!fileSystem.existsSync(targetUri)) {
|
|
67
|
+
return 'missing';
|
|
68
|
+
}
|
|
69
|
+
return fileSystem.statSync(targetUri).isDirectory ? 'folder' : 'file';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function listFolderFileNames(fileSystem: FileSystemProvider, folderUri: URI): string[] {
|
|
73
|
+
return fileSystem.readDirectorySync(folderUri)
|
|
74
|
+
.filter(entry => entry.isFile)
|
|
75
|
+
.map(entry => {
|
|
76
|
+
const segments = entry.uri.path.split('/');
|
|
77
|
+
return segments[segments.length - 1] ?? '';
|
|
78
|
+
})
|
|
79
|
+
.filter(name => name.length > 0)
|
|
80
|
+
.sort((left, right) => left.localeCompare(right));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function resolveFileReferenceLink(
|
|
84
|
+
reference: FileReference | FileSymbolReference,
|
|
85
|
+
documents: LangiumDocuments,
|
|
86
|
+
fileSystem: FileSystemProvider,
|
|
87
|
+
context?: PathResolveContext
|
|
88
|
+
): ResolvedFileLink | undefined {
|
|
89
|
+
const document = AstUtils.getDocument(reference);
|
|
90
|
+
const parsed = parseFileReferenceString(reference.file);
|
|
91
|
+
const pathNode = GrammarUtils.findNodeForProperty(reference.$cstNode, 'file');
|
|
92
|
+
if (!pathNode) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
return resolveParsedFileLink(document, documents, fileSystem, parsed, pathNode, context);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function resolveImportPathLink(
|
|
99
|
+
importDecl: Import,
|
|
100
|
+
documents: LangiumDocuments,
|
|
101
|
+
context?: PathResolveContext
|
|
102
|
+
): ResolvedFileLink | undefined {
|
|
103
|
+
const document = AstUtils.getDocument(importDecl);
|
|
104
|
+
const pathNode = GrammarUtils.findNodeForProperty(importDecl.$cstNode, 'path');
|
|
105
|
+
if (!pathNode) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const imported = findImportedDocument(importDecl.path, document, documents, context);
|
|
109
|
+
const target = imported?.parseResult.value.$cstNode;
|
|
110
|
+
if (!imported || !target) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
sourceRange: pathNode.range,
|
|
115
|
+
targetUri: imported.textDocument.uri,
|
|
116
|
+
targetRange: target.range
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function resolveQualifiedReferencePathLink(
|
|
121
|
+
reference: QualifiedReference,
|
|
122
|
+
documents: LangiumDocuments,
|
|
123
|
+
context?: PathResolveContext
|
|
124
|
+
): ResolvedFileLink | undefined {
|
|
125
|
+
const pathNode = reference.path?.$refNode;
|
|
126
|
+
if (!pathNode) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const document = AstUtils.getDocument(reference);
|
|
130
|
+
const path = qualifiedReferenceImportPath(reference);
|
|
131
|
+
if (!path) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const imported = findImportedDocument(path, document, documents, context);
|
|
135
|
+
const target = imported?.parseResult.value.$cstNode;
|
|
136
|
+
if (!imported || !target) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
sourceRange: pathNode.range,
|
|
141
|
+
targetUri: imported.textDocument.uri,
|
|
142
|
+
targetRange: target.range
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function resolveMarkdownLinkTargetLink(
|
|
147
|
+
link: MarkdownLink,
|
|
148
|
+
document: LangiumDocument,
|
|
149
|
+
documents: LangiumDocuments,
|
|
150
|
+
fileSystem: FileSystemProvider,
|
|
151
|
+
context?: PathResolveContext
|
|
152
|
+
): ResolvedFileLink | undefined {
|
|
153
|
+
const parsedLink = parseMarkdownLink(link.raw);
|
|
154
|
+
if (!parsedLink) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const pathNode = GrammarUtils.findNodeForProperty(link.$cstNode, 'raw');
|
|
158
|
+
if (!pathNode) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
const targetStart = link.raw.indexOf('](') + 2;
|
|
162
|
+
const targetLength = parsedLink.target.length;
|
|
163
|
+
const sourceRange = {
|
|
164
|
+
start: {
|
|
165
|
+
line: pathNode.range.start.line,
|
|
166
|
+
character: pathNode.range.start.character + targetStart
|
|
167
|
+
},
|
|
168
|
+
end: {
|
|
169
|
+
line: pathNode.range.start.line,
|
|
170
|
+
character: pathNode.range.start.character + targetStart + targetLength
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const parsed = parseFileReferenceString(parsedLink.target);
|
|
174
|
+
const targetUri = resolveFileUri(parsed.filePath, document, withFileSystem(fileSystem, context));
|
|
175
|
+
return resolvePathStringLink(document, documents, fileSystem, parsed, targetUri, sourceRange);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function resolveEmbeddedFileReferenceLink(
|
|
179
|
+
reference: EmbeddedFileReference,
|
|
180
|
+
document: LangiumDocument,
|
|
181
|
+
documents: LangiumDocuments,
|
|
182
|
+
fileSystem: FileSystemProvider,
|
|
183
|
+
context?: PathResolveContext
|
|
184
|
+
): ResolvedFileLink | undefined {
|
|
185
|
+
const parsed = parseFileReferenceString(reference.file);
|
|
186
|
+
const targetUri = resolveFileUri(parsed.filePath, document, withFileSystem(fileSystem, context));
|
|
187
|
+
return resolvePathStringLink(
|
|
188
|
+
document,
|
|
189
|
+
documents,
|
|
190
|
+
fileSystem,
|
|
191
|
+
parsed,
|
|
192
|
+
targetUri,
|
|
193
|
+
reference.range
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function resolveParsedFileLink(
|
|
198
|
+
document: LangiumDocument,
|
|
199
|
+
documents: LangiumDocuments,
|
|
200
|
+
fileSystem: FileSystemProvider,
|
|
201
|
+
parsed: ParsedFileReference,
|
|
202
|
+
pathNode: CstNode,
|
|
203
|
+
context?: PathResolveContext
|
|
204
|
+
): ResolvedFileLink | undefined {
|
|
205
|
+
const targetUri = resolveFileUri(parsed.filePath, document, withFileSystem(fileSystem, context));
|
|
206
|
+
const sourceRange = parsed.testName
|
|
207
|
+
? stringContentRange(pathNode)
|
|
208
|
+
: filePathRangeInStringNode(pathNode, parsed);
|
|
209
|
+
return resolvePathStringLink(document, documents, fileSystem, parsed, targetUri, sourceRange);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function resolvePathStringLink(
|
|
213
|
+
_document: LangiumDocument,
|
|
214
|
+
documents: LangiumDocuments,
|
|
215
|
+
fileSystem: FileSystemProvider,
|
|
216
|
+
parsed: ParsedFileReference,
|
|
217
|
+
targetUri: URI,
|
|
218
|
+
sourceRange: Range
|
|
219
|
+
): ResolvedFileLink | undefined {
|
|
220
|
+
const resolution = classifyReferenceUri(targetUri, documents, fileSystem);
|
|
221
|
+
if (resolution === 'missing') {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
if (resolution === 'folder') {
|
|
225
|
+
return {
|
|
226
|
+
sourceRange,
|
|
227
|
+
targetUri: targetUri.toString(),
|
|
228
|
+
resolution,
|
|
229
|
+
folderFiles: listFolderFileNames(fileSystem, targetUri)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
const text = readTargetText(targetUri, documents, fileSystem);
|
|
233
|
+
if (text === undefined) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
const targetIssue = detectFileLinkTargetIssue(text, findTargetDocument(targetUri, documents));
|
|
237
|
+
let targetRange: Range | undefined;
|
|
238
|
+
if (parsed.testName) {
|
|
239
|
+
const line = findTestLineInText(text, parsed.testName);
|
|
240
|
+
if (line !== undefined) {
|
|
241
|
+
targetRange = lineRangeFromText(text, line);
|
|
242
|
+
}
|
|
243
|
+
} else if (parsed.lineStart !== undefined) {
|
|
244
|
+
targetRange = lineRangeFromText(text, parsed.lineStart - 1);
|
|
245
|
+
} else {
|
|
246
|
+
targetRange = fileStartRange();
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
sourceRange,
|
|
250
|
+
targetUri: targetUri.toString(),
|
|
251
|
+
targetRange,
|
|
252
|
+
resolution: 'file',
|
|
253
|
+
targetIssue
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function detectFileLinkTargetIssue(
|
|
258
|
+
text: string,
|
|
259
|
+
targetDocument: LangiumDocument | undefined
|
|
260
|
+
): FileLinkTargetIssue | undefined {
|
|
261
|
+
if (text.length === 0) {
|
|
262
|
+
return 'empty';
|
|
263
|
+
}
|
|
264
|
+
if (targetDocument && targetDocument.parseResult.parserErrors.length > 0) {
|
|
265
|
+
return 'parse-error';
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function fileLinkTargetIssueMessage(issue: FileLinkTargetIssue): string {
|
|
271
|
+
switch (issue) {
|
|
272
|
+
case 'empty':
|
|
273
|
+
return 'Referenced file is empty.';
|
|
274
|
+
case 'parse-error':
|
|
275
|
+
return 'Referenced file has parse errors.';
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function findTargetDocument(targetUri: URI, documents: LangiumDocuments): LangiumDocument | undefined {
|
|
280
|
+
const direct = documents.getDocument(targetUri);
|
|
281
|
+
if (direct) {
|
|
282
|
+
return direct;
|
|
283
|
+
}
|
|
284
|
+
const normalizedTarget = normalizeUriPath(targetUri);
|
|
285
|
+
for (const document of documents.all) {
|
|
286
|
+
if (normalizeUriPath(document.uri) === normalizedTarget) {
|
|
287
|
+
return document;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function normalizeUriPath(uri: URI): string {
|
|
294
|
+
return decodeURIComponent(uri.path).replace(/\\/g, '/');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function readTargetText(targetUri: URI, documents: LangiumDocuments, fileSystem: FileSystemProvider): string | undefined {
|
|
298
|
+
const targetDocument = findTargetDocument(targetUri, documents);
|
|
299
|
+
if (targetDocument) {
|
|
300
|
+
return targetDocument.textDocument.getText();
|
|
301
|
+
}
|
|
302
|
+
if (!fileSystem.existsSync(targetUri)) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
if (fileSystem.statSync(targetUri).isDirectory) {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
return fileSystem.readFileSync(targetUri);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function bindingNameSourceRange(bindingNode: CstNode, bindingName: string): Range {
|
|
312
|
+
const line = bindingNode.range.start.line;
|
|
313
|
+
const startCharacter = bindingNode.range.start.character;
|
|
314
|
+
return {
|
|
315
|
+
start: { line, character: startCharacter },
|
|
316
|
+
end: { line, character: startCharacter + bindingName.length }
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function resolveImportedFileLink(
|
|
321
|
+
document: LangiumDocument,
|
|
322
|
+
documents: LangiumDocuments,
|
|
323
|
+
fileSystem: FileSystemProvider,
|
|
324
|
+
filePath: string,
|
|
325
|
+
sourceRange: Range,
|
|
326
|
+
context?: PathResolveContext
|
|
327
|
+
): ResolvedFileLink | undefined {
|
|
328
|
+
const targetUri = resolveExistingImportUri(filePath, document, documents, fileSystem, context);
|
|
329
|
+
return resolvePathStringLink(document, documents, fileSystem, { filePath }, targetUri, sourceRange);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function filePathRangeInStringNode(pathNode: CstNode, parsed: ParsedFileReference): Range {
|
|
333
|
+
const text = pathNode.text;
|
|
334
|
+
const contentStart = pathNode.range.start.character + (reqlanStringDelimiter(text) ? 1 : 0);
|
|
335
|
+
return {
|
|
336
|
+
start: { line: pathNode.range.start.line, character: contentStart },
|
|
337
|
+
end: { line: pathNode.range.start.line, character: contentStart + parsed.filePath.length }
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function stringContentRange(pathNode: CstNode): Range {
|
|
342
|
+
const text = pathNode.text;
|
|
343
|
+
const quoteOffset = reqlanStringDelimiter(text) ? 1 : 0;
|
|
344
|
+
const trailingQuote = quoteOffset;
|
|
345
|
+
return {
|
|
346
|
+
start: { line: pathNode.range.start.line, character: pathNode.range.start.character + quoteOffset },
|
|
347
|
+
end: { line: pathNode.range.end.line, character: pathNode.range.end.character - trailingQuote }
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function lineRangeFromText(text: string, line: number): Range {
|
|
352
|
+
const lineText = text.split(/\r?\n/)[line] ?? '';
|
|
353
|
+
return {
|
|
354
|
+
start: { line, character: 0 },
|
|
355
|
+
end: { line, character: lineText.length }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function fileStartRange(): Range {
|
|
360
|
+
return {
|
|
361
|
+
start: { line: 0, character: 0 },
|
|
362
|
+
end: { line: 0, character: 0 }
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function collectFileLinks(
|
|
367
|
+
document: LangiumDocument,
|
|
368
|
+
documents: LangiumDocuments,
|
|
369
|
+
fileSystem: FileSystemProvider,
|
|
370
|
+
context?: PathResolveContext
|
|
371
|
+
): ResolvedFileLink[] {
|
|
372
|
+
const pathContext = withFileSystem(fileSystem, context);
|
|
373
|
+
const links: ResolvedFileLink[] = [];
|
|
374
|
+
const linkedRanges: string[] = [];
|
|
375
|
+
for (const node of AstUtils.streamAst(document.parseResult.value)) {
|
|
376
|
+
if (isFileReference(node) || isFileSymbolReference(node)) {
|
|
377
|
+
const link = resolveFileReferenceLink(node, documents, fileSystem, pathContext);
|
|
378
|
+
if (link) {
|
|
379
|
+
links.push(link);
|
|
380
|
+
linkedRanges.push(rangeKey(link.sourceRange));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if ((isLocalReference(node) || isQualifiedReference(node)) && isNamespaceImportOnlyReference(node)) {
|
|
384
|
+
const link = resolveNamespaceImportReferenceLink(node, documents, fileSystem, pathContext);
|
|
385
|
+
if (link) {
|
|
386
|
+
links.push(link);
|
|
387
|
+
linkedRanges.push(rangeKey(link.sourceRange));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (isImport(node)) {
|
|
391
|
+
const link = resolveImportPathLink(node, documents, pathContext);
|
|
392
|
+
if (link) {
|
|
393
|
+
links.push(link);
|
|
394
|
+
linkedRanges.push(rangeKey(link.sourceRange));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (isQualifiedReference(node) && node.path && !node.path.ref) {
|
|
398
|
+
const link = resolveQualifiedReferencePathLink(node, documents, pathContext);
|
|
399
|
+
if (link) {
|
|
400
|
+
links.push(link);
|
|
401
|
+
linkedRanges.push(rangeKey(link.sourceRange));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (isMarkdownLink(node)) {
|
|
405
|
+
const link = resolveMarkdownLinkTargetLink(node, document, documents, fileSystem, pathContext);
|
|
406
|
+
if (link) {
|
|
407
|
+
links.push(link);
|
|
408
|
+
linkedRanges.push(rangeKey(link.sourceRange));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
for (const reference of findEmbeddedFileReferencesInText(document.textDocument.getText())) {
|
|
413
|
+
const key = rangeKey(reference.range);
|
|
414
|
+
if (linkedRanges.includes(key) || links.some(link => rangesOverlap(link.sourceRange, reference.range))) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const link = resolveEmbeddedFileReferenceLink(reference, document, documents, fileSystem, pathContext);
|
|
418
|
+
if (link) {
|
|
419
|
+
links.push(link);
|
|
420
|
+
linkedRanges.push(key);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return links;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function rangeKey(range: Range): string {
|
|
427
|
+
return `${range.start.line}:${range.start.character}-${range.end.line}:${range.end.character}`;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function rangesOverlap(left: Range, right: Range): boolean {
|
|
431
|
+
if (left.start.line !== right.start.line || right.start.line !== right.end.line || left.start.line !== left.end.line) {
|
|
432
|
+
return left.start.line === right.start.line;
|
|
433
|
+
}
|
|
434
|
+
return left.start.character <= right.end.character && right.start.character <= left.end.character;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function resolvedFileLinkTargetUri(link: ResolvedFileLink): string | undefined {
|
|
438
|
+
if (link.resolution === 'folder' || link.resolution === 'missing') {
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
if (link.targetRange) {
|
|
442
|
+
return `${link.targetUri}#L${link.targetRange.start.line + 1}`;
|
|
443
|
+
}
|
|
444
|
+
return link.targetUri;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
export function resolvedFileLinkToGoToTarget(
|
|
448
|
+
link: ResolvedFileLink,
|
|
449
|
+
sourceNode: CstNode,
|
|
450
|
+
targetDocument: LangiumDocument
|
|
451
|
+
): { source: CstNode; target: CstNode; targetDocument: LangiumDocument } | undefined {
|
|
452
|
+
const root = targetDocument.parseResult.value.$cstNode;
|
|
453
|
+
if (!root) {
|
|
454
|
+
return undefined;
|
|
455
|
+
}
|
|
456
|
+
const target = link.targetRange
|
|
457
|
+
? ({
|
|
458
|
+
range: link.targetRange,
|
|
459
|
+
astNode: root.astNode
|
|
460
|
+
} as CstNode)
|
|
461
|
+
: root;
|
|
462
|
+
return {
|
|
463
|
+
source: CstUtils.getDatatypeNode(sourceNode) ?? sourceNode,
|
|
464
|
+
target,
|
|
465
|
+
targetDocument
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function resolvedFileLinkToGoToTargetFromFilesystem(
|
|
470
|
+
link: ResolvedFileLink,
|
|
471
|
+
sourceNode: CstNode
|
|
472
|
+
): { source: CstNode; target: CstNode; targetDocument: LangiumDocument } | undefined {
|
|
473
|
+
if (!link.targetRange) {
|
|
474
|
+
return undefined;
|
|
475
|
+
}
|
|
476
|
+
const target = { range: link.targetRange } as CstNode;
|
|
477
|
+
const targetDocument = {
|
|
478
|
+
textDocument: { uri: link.targetUri }
|
|
479
|
+
} as LangiumDocument;
|
|
480
|
+
return {
|
|
481
|
+
source: CstUtils.getDatatypeNode(sourceNode) ?? sourceNode,
|
|
482
|
+
target,
|
|
483
|
+
targetDocument
|
|
484
|
+
};
|
|
485
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses file path strings from bracket references, including L# line suffixes and :test name suffixes.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ParsedFileReference {
|
|
6
|
+
filePath: string;
|
|
7
|
+
lineStart?: number;
|
|
8
|
+
lineEnd?: number;
|
|
9
|
+
testName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const LINE_SUFFIX_PATTERN = /^(.*?)L#(\d+)(?:-(\d+))?$/;
|
|
13
|
+
|
|
14
|
+
export function parseFileReferenceString(file: string): ParsedFileReference {
|
|
15
|
+
const lineMatch = LINE_SUFFIX_PATTERN.exec(file);
|
|
16
|
+
const lineParsed = lineMatch
|
|
17
|
+
? {
|
|
18
|
+
filePath: lineMatch[1],
|
|
19
|
+
lineStart: Number.parseInt(lineMatch[2], 10),
|
|
20
|
+
lineEnd: lineMatch[3] ? Number.parseInt(lineMatch[3], 10) : Number.parseInt(lineMatch[2], 10)
|
|
21
|
+
}
|
|
22
|
+
: { filePath: file };
|
|
23
|
+
const testParsed = parseTestNameSuffix(lineParsed.filePath);
|
|
24
|
+
return {
|
|
25
|
+
filePath: testParsed.filePath,
|
|
26
|
+
lineStart: lineParsed.lineStart,
|
|
27
|
+
lineEnd: lineParsed.lineEnd,
|
|
28
|
+
testName: testParsed.testName
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseTestNameSuffix(file: string): { filePath: string; testName?: string } {
|
|
33
|
+
let colonIndex = -1;
|
|
34
|
+
for (let index = file.length - 1; index >= 0; index--) {
|
|
35
|
+
if (file[index] !== ':') {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (index > 0 && file[index - 1] === '/' && index > 1 && file[index - 2] === '/') {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
colonIndex = index;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
if (colonIndex < 0) {
|
|
45
|
+
return { filePath: file };
|
|
46
|
+
}
|
|
47
|
+
const testName = file.slice(colonIndex + 1);
|
|
48
|
+
if (!testName) {
|
|
49
|
+
return { filePath: file };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
filePath: file.slice(0, colonIndex),
|
|
53
|
+
testName
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const NON_RQ_FILE_EXTENSION = /\.[^./\\]+$/;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* True when a bracketed quoted path is an arbitrary file reference, not an inline .rq import path.
|
|
61
|
+
*/
|
|
62
|
+
export function isOpaqueFileReferencePath(path: string): boolean {
|
|
63
|
+
const parsed = parseFileReferenceString(path);
|
|
64
|
+
if (parsed.testName !== undefined || parsed.lineStart !== undefined) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
const filePath = parsed.filePath;
|
|
68
|
+
if (/\.rq$/i.test(filePath)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return NON_RQ_FILE_EXTENSION.test(filePath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function findTestLineInText(text: string, testName: string): number | undefined {
|
|
75
|
+
const escaped = testName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
76
|
+
const pattern = new RegExp(`\\b(?:test|it)\\(\\s*['\`]${escaped}['\`]`);
|
|
77
|
+
const lines = text.split(/\r?\n/);
|
|
78
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
79
|
+
if (pattern.test(lines[lineIndex])) {
|
|
80
|
+
return lineIndex;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grammar configuration for rename and comment handling in requirement documents.
|
|
3
|
+
*/
|
|
4
|
+
import type { GrammarConfig, LangiumCoreServices } from 'langium';
|
|
5
|
+
import { createGrammarConfig } from 'langium';
|
|
6
|
+
|
|
7
|
+
const ReqlanNameRegexp = /"[^"]*"|'[^']*'|\w[\w_]*/;
|
|
8
|
+
|
|
9
|
+
export function createReqlanGrammarConfig(services: LangiumCoreServices): GrammarConfig {
|
|
10
|
+
return {
|
|
11
|
+
...createGrammarConfig(services),
|
|
12
|
+
nameRegexp: ReqlanNameRegexp
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate a bracket/wiki idea reference under the cursor for search code actions.
|
|
3
|
+
* rq:["../../../reqlan rq/extension/features-commands.rq".search_code_actions]
|
|
4
|
+
*/
|
|
5
|
+
import type { CstNode, LangiumDocument } from 'langium';
|
|
6
|
+
import { CstUtils } from 'langium';
|
|
7
|
+
import type { Position, Range } from 'vscode-languageserver';
|
|
8
|
+
import {
|
|
9
|
+
isBracketReference,
|
|
10
|
+
isLocalReference,
|
|
11
|
+
isQualifiedReference,
|
|
12
|
+
isWikiLink,
|
|
13
|
+
type BracketReference,
|
|
14
|
+
type WikiLink
|
|
15
|
+
} from './generated/ast.js';
|
|
16
|
+
import { referenceIdea } from './reqlan-references.js';
|
|
17
|
+
|
|
18
|
+
export interface IdeaReferenceSite {
|
|
19
|
+
/** Current idea name text inside the reference (may be empty or partial). */
|
|
20
|
+
refText: string;
|
|
21
|
+
/** Range of text to replace with the chosen idea name (inside brackets). */
|
|
22
|
+
range: Range;
|
|
23
|
+
kind: 'bracket' | 'wikilink';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function findIdeaReferenceAtPosition(
|
|
27
|
+
document: LangiumDocument,
|
|
28
|
+
position: Position
|
|
29
|
+
): IdeaReferenceSite | undefined {
|
|
30
|
+
const root = document.parseResult.value.$cstNode;
|
|
31
|
+
if (!root) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const offset = document.textDocument.offsetAt(position);
|
|
35
|
+
let current: CstNode | undefined = CstUtils.findLeafNodeAtOffset(root, offset);
|
|
36
|
+
while (current) {
|
|
37
|
+
const node = current.astNode;
|
|
38
|
+
if (isBracketReference(node) || isWikiLink(node)) {
|
|
39
|
+
return siteFromReferenceContainer(node);
|
|
40
|
+
}
|
|
41
|
+
current = current.container;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function siteFromReferenceContainer(node: BracketReference | WikiLink): IdeaReferenceSite | undefined {
|
|
47
|
+
const kind = isWikiLink(node) ? 'wikilink' : 'bracket';
|
|
48
|
+
const target = node.target;
|
|
49
|
+
if (!target) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isLocalReference(target) || isQualifiedReference(target)) {
|
|
54
|
+
const idea = referenceIdea(target);
|
|
55
|
+
const ideaNode = idea?.$refNode;
|
|
56
|
+
if (ideaNode) {
|
|
57
|
+
return {
|
|
58
|
+
refText: idea.$refText,
|
|
59
|
+
range: ideaNode.range,
|
|
60
|
+
kind
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const targetNode = target.$cstNode;
|
|
66
|
+
if (!targetNode) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
refText: targetNode.text,
|
|
71
|
+
range: targetNode.range,
|
|
72
|
+
kind
|
|
73
|
+
};
|
|
74
|
+
}
|