@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,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collects relative file path strings in .rq imports and embedded references, and rq: paths in comments.
|
|
3
|
+
*/
|
|
4
|
+
import type { Range } from 'vscode-languageserver';
|
|
5
|
+
import { findCommentReferencesInText } from './reqlan-comment-resolver.js';
|
|
6
|
+
import { findEmbeddedFileReferencesInText } from './reqlan-embedded-file-references.js';
|
|
7
|
+
import { parseFileReferenceString } from './reqlan-file-references.js';
|
|
8
|
+
import type { PathReference } from './file-path-rewrite.js';
|
|
9
|
+
import { parseReqlanQuotedString, REQLAN_QUOTED_STRING_CAPTURE } from './reqlan-quoted-strings.js';
|
|
10
|
+
|
|
11
|
+
const IMPORT_PATH_PATTERN = new RegExp(`(?:\\bfrom|\\bimport)\\s+(${REQLAN_QUOTED_STRING_CAPTURE})`, 'g');
|
|
12
|
+
|
|
13
|
+
export function findImportPathReferencesInText(text: string, lineOffset = 0): PathReference[] {
|
|
14
|
+
const references: PathReference[] = [];
|
|
15
|
+
const lines = text.split(/\r?\n/);
|
|
16
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
17
|
+
const line = lines[lineIndex]!;
|
|
18
|
+
for (const match of line.matchAll(IMPORT_PATH_PATTERN)) {
|
|
19
|
+
const quoted = match[1]!;
|
|
20
|
+
const path = parseReqlanQuotedString(quoted);
|
|
21
|
+
const start = match.index! + match[0].indexOf(quoted);
|
|
22
|
+
references.push({
|
|
23
|
+
path,
|
|
24
|
+
range: {
|
|
25
|
+
start: { line: lineOffset + lineIndex, character: start },
|
|
26
|
+
end: { line: lineOffset + lineIndex, character: start + quoted.length }
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return references;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function findEmbeddedPathReferencesInText(text: string, lineOffset = 0): PathReference[] {
|
|
35
|
+
const references: PathReference[] = [];
|
|
36
|
+
for (const embedded of findEmbeddedFileReferencesInText(text, lineOffset)) {
|
|
37
|
+
const parsed = parseFileReferenceString(embedded.file);
|
|
38
|
+
if (!parsed.filePath) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const quotedPattern = new RegExp(REQLAN_QUOTED_STRING_CAPTURE);
|
|
42
|
+
const embeddedText = text.slice(
|
|
43
|
+
textOffsetAtPosition(text, embedded.range.start),
|
|
44
|
+
textOffsetAtPosition(text, embedded.range.end)
|
|
45
|
+
);
|
|
46
|
+
const quotedMatch = quotedPattern.exec(embeddedText);
|
|
47
|
+
if (!quotedMatch) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const quoted = quotedMatch[0];
|
|
51
|
+
const pathIndex = quotedMatch.index ?? embeddedText.indexOf(quoted);
|
|
52
|
+
const absoluteStart = textOffsetAtPosition(text, embedded.range.start) + pathIndex;
|
|
53
|
+
const startPos = offsetToPosition(text, absoluteStart);
|
|
54
|
+
references.push({
|
|
55
|
+
path: parsed.filePath,
|
|
56
|
+
range: {
|
|
57
|
+
start: startPos,
|
|
58
|
+
end: { line: startPos.line, character: startPos.character + quoted.length }
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return references;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function findCommentPathReferencesInText(text: string, lineOffset = 0): PathReference[] {
|
|
66
|
+
const references: PathReference[] = [];
|
|
67
|
+
for (const commentRef of findCommentReferencesInText(text, lineOffset)) {
|
|
68
|
+
if (!commentRef.path) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const quotedPattern = new RegExp(REQLAN_QUOTED_STRING_CAPTURE);
|
|
72
|
+
const segmentStart = textOffsetAtPosition(text, commentRef.range.start);
|
|
73
|
+
const segmentEnd = textOffsetAtPosition(text, commentRef.range.end);
|
|
74
|
+
const segment = text.slice(segmentStart, segmentEnd);
|
|
75
|
+
const quotedMatch = quotedPattern.exec(segment);
|
|
76
|
+
if (!quotedMatch) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const quoted = quotedMatch[0];
|
|
80
|
+
const pathIndex = quotedMatch.index ?? segment.indexOf(quoted);
|
|
81
|
+
const absoluteStart = segmentStart + pathIndex;
|
|
82
|
+
const startPos = offsetToPosition(text, absoluteStart);
|
|
83
|
+
references.push({
|
|
84
|
+
path: commentRef.path,
|
|
85
|
+
range: {
|
|
86
|
+
start: startPos,
|
|
87
|
+
end: { line: startPos.line, character: startPos.character + quoted.length }
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return references;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function findPathReferencesInMovedFile(text: string, isRqFile: boolean): PathReference[] {
|
|
95
|
+
const references = isRqFile
|
|
96
|
+
? [...findImportPathReferencesInText(text), ...findEmbeddedPathReferencesInText(text)]
|
|
97
|
+
: findCommentPathReferencesInText(text);
|
|
98
|
+
return dedupePathReferences(references);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function dedupePathReferences(references: PathReference[]): PathReference[] {
|
|
102
|
+
const seen = new Set<string>();
|
|
103
|
+
const unique: PathReference[] = [];
|
|
104
|
+
for (const reference of references) {
|
|
105
|
+
const key = [
|
|
106
|
+
reference.range.start.line,
|
|
107
|
+
reference.range.start.character,
|
|
108
|
+
reference.range.end.line,
|
|
109
|
+
reference.range.end.character
|
|
110
|
+
].join(':');
|
|
111
|
+
if (seen.has(key)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
seen.add(key);
|
|
115
|
+
unique.push(reference);
|
|
116
|
+
}
|
|
117
|
+
return unique;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function textOffsetAtLine(text: string, lineIndex: number): number {
|
|
121
|
+
const lines = text.split(/\r?\n/);
|
|
122
|
+
let offset = 0;
|
|
123
|
+
for (let index = 0; index < lineIndex; index++) {
|
|
124
|
+
offset += lines[index]!.length + 1;
|
|
125
|
+
}
|
|
126
|
+
return offset;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function textOffsetAtPosition(text: string, position: Range['start']): number {
|
|
130
|
+
return textOffsetAtLine(text, position.line) + position.character;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function offsetToPosition(text: string, offset: number): Range['start'] {
|
|
134
|
+
const before = text.slice(0, offset);
|
|
135
|
+
const lines = before.split(/\r?\n/);
|
|
136
|
+
return {
|
|
137
|
+
line: lines.length - 1,
|
|
138
|
+
character: lines[lines.length - 1]!.length
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves document-relative and import-root-aliased paths (default `@/`).
|
|
3
|
+
* rq:["../../../reqlan rq/extension/configuration.rq".configuration_location]
|
|
4
|
+
* rq:["../../../reqlan rq/extension/configuration.rq".configuration_import_roots]
|
|
5
|
+
* rq:["../../../reqlan rq/language/imports.rq".configuration_import_root_alias]
|
|
6
|
+
*/
|
|
7
|
+
import type { FileSystemProvider, LangiumDocument, URI } from 'langium';
|
|
8
|
+
import { URI as UriCtor, UriUtils } from 'langium';
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_IMPORT_ROOT_ALIAS = '@';
|
|
11
|
+
export const RQCONFIG_FILENAME = '.rqconfig.json';
|
|
12
|
+
|
|
13
|
+
export interface ImportRootMapping {
|
|
14
|
+
alias: string;
|
|
15
|
+
/** Absolute URI of that alias’s import-root directory, when set. */
|
|
16
|
+
rootUri?: URI;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type RqExportScope = 'workspace' | 'currentFile';
|
|
20
|
+
export type RqExportRuntimeMode = 'interactive' | 'document' | 'print';
|
|
21
|
+
export type RqExportClusterStrategy = 'deterministic' | 'hybrid';
|
|
22
|
+
|
|
23
|
+
export interface RqHtmlExportConfig {
|
|
24
|
+
printEntryFileName?: string;
|
|
25
|
+
includeRequirementsPage?: boolean;
|
|
26
|
+
includeGraphPage?: boolean;
|
|
27
|
+
runtimeMode?: RqExportRuntimeMode;
|
|
28
|
+
clusterStrategy?: RqExportClusterStrategy;
|
|
29
|
+
includeIdeaPages?: boolean;
|
|
30
|
+
includeFilePages?: boolean;
|
|
31
|
+
includeCodeFilePages?: boolean;
|
|
32
|
+
includeClusterPages?: boolean;
|
|
33
|
+
includePrintPages?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RqExportConfig {
|
|
37
|
+
outputFolder?: string;
|
|
38
|
+
templateId?: string;
|
|
39
|
+
scope?: RqExportScope;
|
|
40
|
+
html?: RqHtmlExportConfig;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RqConfig {
|
|
44
|
+
importRoots: ImportRootMapping[];
|
|
45
|
+
export?: RqExportConfig;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PathResolveContext {
|
|
49
|
+
/** Workspace folders that contain documents (longest match wins). */
|
|
50
|
+
workspaceFolderUris?: readonly URI[];
|
|
51
|
+
/** Explicit workspace folder for tests; preferred over list lookup when set. */
|
|
52
|
+
workspaceFolderUri?: URI;
|
|
53
|
+
fileSystem?: FileSystemProvider;
|
|
54
|
+
/**
|
|
55
|
+
* Preloaded config. `undefined` loads nearest `.rqconfig.json` when fileSystem is set.
|
|
56
|
+
* `null` skips loading and uses defaults only.
|
|
57
|
+
*/
|
|
58
|
+
config?: RqConfig | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function defaultRqConfig(): RqConfig {
|
|
62
|
+
return { importRoots: [{ alias: DEFAULT_IMPORT_ROOT_ALIAS }] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function matchImportRootAlias(path: string, alias: string): string | undefined {
|
|
66
|
+
if (!alias || !path.startsWith(alias)) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
const afterAlias = path.slice(alias.length);
|
|
70
|
+
if (!afterAlias.startsWith('/')) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return afterAlias.slice(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Longest matching alias wins when several mappings could apply. */
|
|
77
|
+
export function matchImportRootMapping(
|
|
78
|
+
path: string,
|
|
79
|
+
mappings: readonly ImportRootMapping[]
|
|
80
|
+
): { mapping: ImportRootMapping; remainder: string } | undefined {
|
|
81
|
+
const ordered = [...mappings]
|
|
82
|
+
.filter(mapping => mapping.alias.length > 0)
|
|
83
|
+
.sort((left, right) => right.alias.length - left.alias.length);
|
|
84
|
+
for (const mapping of ordered) {
|
|
85
|
+
const remainder = matchImportRootAlias(path, mapping.alias);
|
|
86
|
+
if (remainder !== undefined) {
|
|
87
|
+
return { mapping, remainder };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function findWorkspaceFolderUri(
|
|
94
|
+
documentUri: URI,
|
|
95
|
+
folderUris: readonly URI[] | undefined
|
|
96
|
+
): URI | undefined {
|
|
97
|
+
if (!folderUris || folderUris.length === 0) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
const documentPath = documentUri.toString();
|
|
101
|
+
let best: URI | undefined;
|
|
102
|
+
let bestLength = -1;
|
|
103
|
+
for (const folder of folderUris) {
|
|
104
|
+
const folderPath = folder.path.endsWith('/')
|
|
105
|
+
? folder.toString()
|
|
106
|
+
: `${folder.toString()}/`;
|
|
107
|
+
const folderBase = folder.toString();
|
|
108
|
+
if (documentPath === folderBase || documentPath.startsWith(folderPath)) {
|
|
109
|
+
const length = folderBase.length;
|
|
110
|
+
if (length > bestLength) {
|
|
111
|
+
best = folder;
|
|
112
|
+
bestLength = length;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return best;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function loadApplyingRqConfig(
|
|
120
|
+
startDirUri: URI,
|
|
121
|
+
fileSystem: FileSystemProvider
|
|
122
|
+
): RqConfig | undefined {
|
|
123
|
+
let dir = startDirUri;
|
|
124
|
+
for (;;) {
|
|
125
|
+
const configUri = UriUtils.joinPath(dir, RQCONFIG_FILENAME);
|
|
126
|
+
if (fileSystem.existsSync(configUri) && !fileSystem.statSync(configUri).isDirectory) {
|
|
127
|
+
return parseRqConfig(configUri, fileSystem);
|
|
128
|
+
}
|
|
129
|
+
const parent = UriUtils.dirname(dir);
|
|
130
|
+
if (UriUtils.equals(parent, dir)) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
dir = parent;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function parseRqConfig(configUri: URI, fileSystem: FileSystemProvider): RqConfig | undefined {
|
|
138
|
+
let raw: unknown;
|
|
139
|
+
try {
|
|
140
|
+
raw = JSON.parse(fileSystem.readFileSync(configUri));
|
|
141
|
+
} catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
const record = raw as Record<string, unknown>;
|
|
148
|
+
const importRoots = parseImportRoots(record.importRoots, configUri);
|
|
149
|
+
if (record.importRoots !== undefined && importRoots === undefined) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const parsedExport = parseExportConfig(record.export, UriUtils.dirname(configUri));
|
|
153
|
+
const config: RqConfig = {
|
|
154
|
+
importRoots: importRoots ?? defaultRqConfig().importRoots
|
|
155
|
+
};
|
|
156
|
+
if (parsedExport) {
|
|
157
|
+
config.export = parsedExport;
|
|
158
|
+
}
|
|
159
|
+
return config;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parseImportRootEntry(entry: unknown, configDir: URI): ImportRootMapping | undefined {
|
|
163
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
const record = entry as Record<string, unknown>;
|
|
167
|
+
if (typeof record.alias !== 'string' || record.alias.length === 0) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
const mapping: ImportRootMapping = { alias: record.alias };
|
|
171
|
+
if (typeof record.root === 'string' && record.root.length > 0) {
|
|
172
|
+
mapping.rootUri = isAbsoluteUriOrPath(record.root)
|
|
173
|
+
? toDirectoryUri(record.root)
|
|
174
|
+
: UriUtils.resolvePath(configDir, record.root);
|
|
175
|
+
}
|
|
176
|
+
return mapping;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parseImportRoots(raw: unknown, configUri: URI): ImportRootMapping[] | undefined {
|
|
180
|
+
if (raw === undefined) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
if (!Array.isArray(raw)) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const configDir = UriUtils.dirname(configUri);
|
|
187
|
+
const importRoots: ImportRootMapping[] = [];
|
|
188
|
+
for (const entry of raw) {
|
|
189
|
+
const mapping = parseImportRootEntry(entry, configDir);
|
|
190
|
+
if (mapping) {
|
|
191
|
+
importRoots.push(mapping);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return importRoots.length > 0 ? importRoots : defaultRqConfig().importRoots;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function parseExportConfig(raw: unknown, configDir: URI): RqExportConfig | undefined {
|
|
198
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const record = raw as Record<string, unknown>;
|
|
202
|
+
const config: RqExportConfig = {};
|
|
203
|
+
|
|
204
|
+
if (typeof record.outputFolder === 'string' && record.outputFolder.trim().length > 0) {
|
|
205
|
+
config.outputFolder = isAbsoluteUriOrPath(record.outputFolder)
|
|
206
|
+
? toDirectoryUri(record.outputFolder).fsPath
|
|
207
|
+
: UriUtils.resolvePath(configDir, record.outputFolder).fsPath;
|
|
208
|
+
}
|
|
209
|
+
if (typeof record.templateId === 'string' && record.templateId.trim().length > 0) {
|
|
210
|
+
config.templateId = record.templateId.trim();
|
|
211
|
+
}
|
|
212
|
+
if (record.scope === 'workspace' || record.scope === 'currentFile') {
|
|
213
|
+
config.scope = record.scope;
|
|
214
|
+
}
|
|
215
|
+
const html = parseHtmlExportConfig(record.html);
|
|
216
|
+
if (html) {
|
|
217
|
+
config.html = html;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return Object.keys(config).length > 0 ? config : undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function parseHtmlExportConfig(raw: unknown): RqHtmlExportConfig | undefined {
|
|
224
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
const record = raw as Record<string, unknown>;
|
|
228
|
+
const config: RqHtmlExportConfig = {};
|
|
229
|
+
if (typeof record.printEntryFileName === 'string' && record.printEntryFileName.trim().length > 0) {
|
|
230
|
+
config.printEntryFileName = record.printEntryFileName.trim();
|
|
231
|
+
}
|
|
232
|
+
if (typeof record.includeRequirementsPage === 'boolean') {
|
|
233
|
+
config.includeRequirementsPage = record.includeRequirementsPage;
|
|
234
|
+
}
|
|
235
|
+
if (typeof record.includeGraphPage === 'boolean') {
|
|
236
|
+
config.includeGraphPage = record.includeGraphPage;
|
|
237
|
+
}
|
|
238
|
+
if (record.runtimeMode === 'interactive' || record.runtimeMode === 'document' || record.runtimeMode === 'print') {
|
|
239
|
+
config.runtimeMode = record.runtimeMode;
|
|
240
|
+
}
|
|
241
|
+
if (record.clusterStrategy === 'deterministic' || record.clusterStrategy === 'hybrid') {
|
|
242
|
+
config.clusterStrategy = record.clusterStrategy;
|
|
243
|
+
}
|
|
244
|
+
if (typeof record.includeIdeaPages === 'boolean') {
|
|
245
|
+
config.includeIdeaPages = record.includeIdeaPages;
|
|
246
|
+
}
|
|
247
|
+
if (typeof record.includeFilePages === 'boolean') {
|
|
248
|
+
config.includeFilePages = record.includeFilePages;
|
|
249
|
+
}
|
|
250
|
+
if (typeof record.includeCodeFilePages === 'boolean') {
|
|
251
|
+
config.includeCodeFilePages = record.includeCodeFilePages;
|
|
252
|
+
}
|
|
253
|
+
if (typeof record.includeClusterPages === 'boolean') {
|
|
254
|
+
config.includeClusterPages = record.includeClusterPages;
|
|
255
|
+
}
|
|
256
|
+
if (typeof record.includePrintPages === 'boolean') {
|
|
257
|
+
config.includePrintPages = record.includePrintPages;
|
|
258
|
+
}
|
|
259
|
+
return Object.keys(config).length > 0 ? config : undefined;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function isAbsoluteUriOrPath(value: string): boolean {
|
|
263
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value) || value.startsWith('/');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function toDirectoryUri(value: string): URI {
|
|
267
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
268
|
+
return UriCtor.parse(value);
|
|
269
|
+
}
|
|
270
|
+
return UriCtor.file(value);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function resolveRqConfig(document: LangiumDocument, context?: PathResolveContext): RqConfig {
|
|
274
|
+
if (context?.config === null) {
|
|
275
|
+
return defaultRqConfig();
|
|
276
|
+
}
|
|
277
|
+
if (context?.config) {
|
|
278
|
+
return context.config;
|
|
279
|
+
}
|
|
280
|
+
if (context?.fileSystem) {
|
|
281
|
+
const loaded = loadApplyingRqConfig(UriUtils.dirname(document.uri), context.fileSystem);
|
|
282
|
+
if (loaded) {
|
|
283
|
+
return loaded;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return defaultRqConfig();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function resolveImportRootUri(
|
|
290
|
+
document: LangiumDocument,
|
|
291
|
+
context: PathResolveContext | undefined,
|
|
292
|
+
mapping: ImportRootMapping
|
|
293
|
+
): URI | undefined {
|
|
294
|
+
if (mapping.rootUri) {
|
|
295
|
+
return mapping.rootUri;
|
|
296
|
+
}
|
|
297
|
+
if (context?.workspaceFolderUri) {
|
|
298
|
+
return context.workspaceFolderUri;
|
|
299
|
+
}
|
|
300
|
+
return findWorkspaceFolderUri(document.uri, context?.workspaceFolderUris);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Resolves a path string against the document directory, or against an import root when aliased.
|
|
305
|
+
*/
|
|
306
|
+
export function resolveDocumentPathUri(
|
|
307
|
+
path: string,
|
|
308
|
+
document: LangiumDocument,
|
|
309
|
+
context?: PathResolveContext
|
|
310
|
+
): URI {
|
|
311
|
+
const config = resolveRqConfig(document, context);
|
|
312
|
+
const matched = matchImportRootMapping(path, config.importRoots);
|
|
313
|
+
if (!matched) {
|
|
314
|
+
return UriUtils.resolvePath(UriUtils.dirname(document.uri), path);
|
|
315
|
+
}
|
|
316
|
+
const importRoot = resolveImportRootUri(document, context, matched.mapping);
|
|
317
|
+
if (!importRoot) {
|
|
318
|
+
return UriUtils.resolvePath(UriUtils.dirname(document.uri), path);
|
|
319
|
+
}
|
|
320
|
+
return UriUtils.resolvePath(importRoot, matched.remainder);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function workspaceFolderUrisFromManager(
|
|
324
|
+
folders: ReadonlyArray<{ uri: string }> | undefined
|
|
325
|
+
): URI[] {
|
|
326
|
+
if (!folders) {
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
return folders.map(folder => UriCtor.parse(folder.uri));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function pathResolveContextFromServices(services: {
|
|
333
|
+
shared: {
|
|
334
|
+
workspace: {
|
|
335
|
+
WorkspaceManager: { workspaceFolders?: ReadonlyArray<{ uri: string }> };
|
|
336
|
+
FileSystemProvider: FileSystemProvider;
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
}): PathResolveContext {
|
|
340
|
+
return {
|
|
341
|
+
fileSystem: services.shared.workspace.FileSystemProvider,
|
|
342
|
+
workspaceFolderUris: workspaceFolderUrisFromManager(
|
|
343
|
+
services.shared.workspace.WorkspaceManager.workspaceFolders
|
|
344
|
+
)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for reqlan double- and single-quoted string literals.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Capturing group for one quoted string (double or single). */
|
|
6
|
+
export const REQLAN_QUOTED_STRING_CAPTURE = '(?:"(?:\\\\.|[^"\\\\])*"|\'(?:\\\\.|[^\'\\\\])*\')';
|
|
7
|
+
|
|
8
|
+
export const REQLAN_QUOTED_STRING_PATTERN = new RegExp(REQLAN_QUOTED_STRING_CAPTURE, 'g');
|
|
9
|
+
|
|
10
|
+
export function reqlanStringDelimiter(text: string): '"' | "'" | undefined {
|
|
11
|
+
if (text.startsWith('"')) {
|
|
12
|
+
return '"';
|
|
13
|
+
}
|
|
14
|
+
if (text.startsWith("'")) {
|
|
15
|
+
return "'";
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function unquoteReqlanString(text: string): string {
|
|
21
|
+
const delimiter = reqlanStringDelimiter(text);
|
|
22
|
+
if (!delimiter || !text.endsWith(delimiter)) {
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
return text.slice(1, -1).replace(/\\(.)/g, '$1');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function parseReqlanQuotedString(quoted: string): string {
|
|
29
|
+
return unquoteReqlanString(quoted);
|
|
30
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locates file path references at a cursor position and resolves them to files, folders, or missing paths.
|
|
3
|
+
*/
|
|
4
|
+
import type { CstNode, FileSystemProvider, LangiumDocument, LangiumDocuments } from 'langium';
|
|
5
|
+
import { CstUtils, GrammarUtils } from 'langium';
|
|
6
|
+
import type { Position, LocationLink } from 'vscode-languageserver';
|
|
7
|
+
import {
|
|
8
|
+
findCommentReferencePartAt,
|
|
9
|
+
resolveCommentDefinitionLinks,
|
|
10
|
+
resolveFileUri
|
|
11
|
+
} from './reqlan-comment-resolver.js';
|
|
12
|
+
import { findEmbeddedFileReferenceAt } from './reqlan-embedded-file-references.js';
|
|
13
|
+
import {
|
|
14
|
+
classifyReferenceUri,
|
|
15
|
+
listFolderFileNames,
|
|
16
|
+
resolveEmbeddedFileReferenceLink,
|
|
17
|
+
resolveFileReferenceLink,
|
|
18
|
+
type ReferenceResolution,
|
|
19
|
+
type ResolvedFileLink
|
|
20
|
+
} from './reqlan-file-link-resolver.js';
|
|
21
|
+
import {
|
|
22
|
+
isFileReference,
|
|
23
|
+
isFileSymbolReference,
|
|
24
|
+
type FileReference,
|
|
25
|
+
type FileSymbolReference
|
|
26
|
+
} from './generated/ast.js';
|
|
27
|
+
import type { PathResolveContext } from './reqlan-path-resolve.js';
|
|
28
|
+
|
|
29
|
+
export const REQLAN_OPEN_FOLDER_COMMAND = 'reqlan.openFolderReference';
|
|
30
|
+
export const REQLAN_FILE_REFERENCE_AT_REQUEST = 'reqlan/fileReferenceAt';
|
|
31
|
+
export const REQLAN_COMMENT_DEFINITION_REQUEST = 'reqlan/commentDefinition';
|
|
32
|
+
|
|
33
|
+
export function folderReferenceCommandTarget(folderUri: string): string {
|
|
34
|
+
return `command:${REQLAN_OPEN_FOLDER_COMMAND}?${encodeURIComponent(JSON.stringify([folderUri]))}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function findFileReferenceAtPosition(
|
|
38
|
+
document: LangiumDocument,
|
|
39
|
+
position: Position,
|
|
40
|
+
documents: LangiumDocuments,
|
|
41
|
+
fileSystem: FileSystemProvider,
|
|
42
|
+
context?: PathResolveContext
|
|
43
|
+
): ResolvedFileLink | undefined {
|
|
44
|
+
const pathContext = { ...context, fileSystem: context?.fileSystem ?? fileSystem };
|
|
45
|
+
const commentPart = findCommentReferencePartAt(document, position);
|
|
46
|
+
if (commentPart?.property === 'path' && commentPart.reference.path) {
|
|
47
|
+
return resolvePathReference(
|
|
48
|
+
document,
|
|
49
|
+
commentPart.reference.path,
|
|
50
|
+
commentPart.reference.range,
|
|
51
|
+
documents,
|
|
52
|
+
fileSystem,
|
|
53
|
+
pathContext
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const embeddedReference = findEmbeddedFileReferenceAt(document, position);
|
|
57
|
+
if (embeddedReference) {
|
|
58
|
+
return resolveEmbeddedFileReferenceLink(embeddedReference, document, documents, fileSystem, pathContext);
|
|
59
|
+
}
|
|
60
|
+
const fileReference = findFileReferenceNodeAtPosition(document, position);
|
|
61
|
+
if (fileReference) {
|
|
62
|
+
return resolveFileReferenceLink(fileReference.node, documents, fileSystem, pathContext);
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function resolvePathReference(
|
|
68
|
+
document: LangiumDocument,
|
|
69
|
+
path: string,
|
|
70
|
+
sourceRange: ResolvedFileLink['sourceRange'],
|
|
71
|
+
documents: LangiumDocuments,
|
|
72
|
+
fileSystem: FileSystemProvider,
|
|
73
|
+
context?: PathResolveContext
|
|
74
|
+
): ResolvedFileLink | undefined {
|
|
75
|
+
const targetUri = resolveFileUri(path, document, context);
|
|
76
|
+
const resolution = classifyReferenceUri(targetUri, documents, fileSystem);
|
|
77
|
+
if (resolution === 'missing') {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
if (resolution === 'folder') {
|
|
81
|
+
return {
|
|
82
|
+
sourceRange,
|
|
83
|
+
targetUri: targetUri.toString(),
|
|
84
|
+
resolution,
|
|
85
|
+
folderFiles: listFolderFileNames(fileSystem, targetUri)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const targetDocument = documents.getDocument(targetUri);
|
|
89
|
+
const target = targetDocument?.parseResult.value.$cstNode;
|
|
90
|
+
if (!targetDocument || !target) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
sourceRange,
|
|
95
|
+
targetUri: targetDocument.textDocument.uri,
|
|
96
|
+
targetRange: target.range,
|
|
97
|
+
resolution: 'file'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function findFileReferenceNodeAtPosition(
|
|
102
|
+
document: LangiumDocument,
|
|
103
|
+
position: Position
|
|
104
|
+
): { node: FileReference | FileSymbolReference; pathNode: CstNode } | undefined {
|
|
105
|
+
const root = document.parseResult.value.$cstNode;
|
|
106
|
+
if (!root) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const offset = document.textDocument.offsetAt(position);
|
|
110
|
+
let current: CstNode | undefined = CstUtils.findLeafNodeAtOffset(root, offset);
|
|
111
|
+
while (current) {
|
|
112
|
+
const assignment = GrammarUtils.findAssignment(current);
|
|
113
|
+
const container = current.astNode;
|
|
114
|
+
if ((isFileReference(container) || isFileSymbolReference(container)) && assignment?.feature === 'file') {
|
|
115
|
+
const pathNode = GrammarUtils.findNodeForProperty(container.$cstNode, 'file');
|
|
116
|
+
if (pathNode) {
|
|
117
|
+
return { node: container, pathNode };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
current = current.container;
|
|
121
|
+
}
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface FileReferenceAtRequestResult {
|
|
126
|
+
sourceRange: ResolvedFileLink['sourceRange'];
|
|
127
|
+
targetUri: string;
|
|
128
|
+
resolution: ReferenceResolution;
|
|
129
|
+
folderFiles?: string[];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function findCommentDefinitionAtPosition(
|
|
133
|
+
document: LangiumDocument,
|
|
134
|
+
position: Position,
|
|
135
|
+
documents: LangiumDocuments,
|
|
136
|
+
context?: PathResolveContext
|
|
137
|
+
): LocationLink[] | undefined {
|
|
138
|
+
const commentPart = findCommentReferencePartAt(document, position);
|
|
139
|
+
if (commentPart?.property !== 'idea') {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
return resolveCommentDefinitionLinks(commentPart.reference, document, documents, context);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function fileReferenceAtRequestResult(
|
|
146
|
+
link: ResolvedFileLink
|
|
147
|
+
): FileReferenceAtRequestResult {
|
|
148
|
+
return {
|
|
149
|
+
sourceRange: link.sourceRange,
|
|
150
|
+
targetUri: link.targetUri,
|
|
151
|
+
resolution: link.resolution ?? 'file',
|
|
152
|
+
folderFiles: link.folderFiles
|
|
153
|
+
};
|
|
154
|
+
}
|