@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,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact name index pushed from the extension host for unresolved-reference quick fixes.
|
|
3
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_error]
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type NameCatalogKind = 'idea' | 'oneliner' | 'ideaset' | 'file';
|
|
7
|
+
|
|
8
|
+
export interface NameCatalogEntry {
|
|
9
|
+
name: string;
|
|
10
|
+
kind: NameCatalogKind;
|
|
11
|
+
fileUri: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface NameCatalog {
|
|
15
|
+
entries: NameCatalogEntry[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class NameCatalogStore {
|
|
19
|
+
private catalog: NameCatalog = { entries: [] };
|
|
20
|
+
|
|
21
|
+
get(): NameCatalog {
|
|
22
|
+
return this.catalog;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
update(catalog: NameCatalog): void {
|
|
26
|
+
this.catalog = {
|
|
27
|
+
entries: catalog.entries.map(entry => ({ ...entry }))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
reset(): void {
|
|
32
|
+
this.catalog = { entries: [] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
findExact(name: string): NameCatalogEntry[] {
|
|
36
|
+
return this.catalog.entries.filter(entry => entry.name === name);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
findMatching(query: string, limit = 25): NameCatalogEntry[] {
|
|
40
|
+
const needle = query.trim().toLowerCase();
|
|
41
|
+
if (!needle) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const scored = this.catalog.entries
|
|
45
|
+
.map(entry => ({ entry, score: scoreNameMatch(entry.name, needle) }))
|
|
46
|
+
.filter(item => item.score > 0)
|
|
47
|
+
.sort((left, right) => right.score - left.score || left.entry.name.localeCompare(right.entry.name));
|
|
48
|
+
const seen = new Set<string>();
|
|
49
|
+
const results: NameCatalogEntry[] = [];
|
|
50
|
+
for (const item of scored) {
|
|
51
|
+
const key = `${item.entry.kind}:${item.entry.fileUri}:${item.entry.name}`;
|
|
52
|
+
if (seen.has(key)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
seen.add(key);
|
|
56
|
+
results.push(item.entry);
|
|
57
|
+
if (results.length >= limit) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return results;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const REQLAN_NAME_CATALOG_NOTIFICATION = 'reqlan/nameCatalog';
|
|
66
|
+
|
|
67
|
+
export const sharedNameCatalog = new NameCatalogStore();
|
|
68
|
+
|
|
69
|
+
function scoreNameMatch(name: string, needle: string): number {
|
|
70
|
+
const hay = name.toLowerCase();
|
|
71
|
+
if (hay === needle) {
|
|
72
|
+
return 100;
|
|
73
|
+
}
|
|
74
|
+
if (hay.startsWith(needle)) {
|
|
75
|
+
return 80;
|
|
76
|
+
}
|
|
77
|
+
if (hay.includes(needle)) {
|
|
78
|
+
return 50;
|
|
79
|
+
}
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves declaration names for ideas, ideasets, and import aliases in the requirement graph.
|
|
3
|
+
* Interacts with rename, scope export, and cross-reference linking.
|
|
4
|
+
*/
|
|
5
|
+
import type { AstNode, CstNode } from 'langium';
|
|
6
|
+
import { DefaultNameProvider, GrammarUtils } from 'langium';
|
|
7
|
+
import {
|
|
8
|
+
isFromImport,
|
|
9
|
+
isFromImportSpecifier,
|
|
10
|
+
isIdea,
|
|
11
|
+
isIdeaSet,
|
|
12
|
+
isNamespaceImport,
|
|
13
|
+
isQualifiedImport,
|
|
14
|
+
isOneLinerIdea,
|
|
15
|
+
type Import
|
|
16
|
+
} from './generated/ast.js';
|
|
17
|
+
|
|
18
|
+
export class ReqlanNameProvider extends DefaultNameProvider {
|
|
19
|
+
|
|
20
|
+
override getName(node: AstNode): string | undefined {
|
|
21
|
+
if (isImportWithAlias(node)) {
|
|
22
|
+
return node.alias;
|
|
23
|
+
}
|
|
24
|
+
if (isImportWithPath(node)) {
|
|
25
|
+
return node.path;
|
|
26
|
+
}
|
|
27
|
+
if (isIdea(node) || isOneLinerIdea(node) || isIdeaSet(node)) {
|
|
28
|
+
return node.name;
|
|
29
|
+
}
|
|
30
|
+
return super.getName(node);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override getNameNode(node: AstNode): CstNode | undefined {
|
|
34
|
+
if (isImportWithAlias(node)) {
|
|
35
|
+
return GrammarUtils.findNodeForProperty(node.$cstNode, 'alias');
|
|
36
|
+
}
|
|
37
|
+
if (isImportWithPath(node)) {
|
|
38
|
+
return GrammarUtils.findNodeForProperty(node.$cstNode, 'path');
|
|
39
|
+
}
|
|
40
|
+
if (isIdea(node) || isOneLinerIdea(node) || isIdeaSet(node)) {
|
|
41
|
+
return GrammarUtils.findNodeForProperty(node.$cstNode, 'name');
|
|
42
|
+
}
|
|
43
|
+
return super.getNameNode(node);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isImportWithAlias(node: AstNode): node is Import & { alias: string } {
|
|
48
|
+
return (isFromImportSpecifier(node) || isNamespaceImport(node) || isQualifiedImport(node)) && node.alias !== undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isImportWithPath(node: AstNode): node is Import & { path: string } {
|
|
52
|
+
if (isFromImport(node)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return (isNamespaceImport(node) || isQualifiedImport(node)) && node.alias === undefined;
|
|
56
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves bracket references to namespace-imported files (ideasets or external files).
|
|
3
|
+
*/
|
|
4
|
+
import type { CstNode, FileSystemProvider, LangiumDocuments } from 'langium';
|
|
5
|
+
import { AstUtils, GrammarUtils } from 'langium';
|
|
6
|
+
import { isIdea, isModel, isOneLinerIdea, type Import, type LocalReference, type QualifiedReference } from './generated/ast.js';
|
|
7
|
+
import { findNamespaceImportByAlias } from './reqlan-import-bindings.js';
|
|
8
|
+
import { bindingNameSourceRange, resolveImportedFileLink, type ResolvedFileLink } from './reqlan-file-link-resolver.js';
|
|
9
|
+
import type { PathResolveContext } from './reqlan-path-resolve.js';
|
|
10
|
+
|
|
11
|
+
export function namespaceImportBindingName(reference: LocalReference): string | undefined {
|
|
12
|
+
return reference.idea?.$refText;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isNamespaceImportOnlyReference(reference: LocalReference | QualifiedReference): boolean {
|
|
16
|
+
if (reference.$type === 'QualifiedReference') {
|
|
17
|
+
return reference.qualifier !== undefined
|
|
18
|
+
&& reference.path === undefined
|
|
19
|
+
&& reference.idea === undefined
|
|
20
|
+
&& reference.ideaset === undefined;
|
|
21
|
+
}
|
|
22
|
+
const bindingName = namespaceImportBindingName(reference);
|
|
23
|
+
if (!bindingName) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const document = AstUtils.getDocument(reference);
|
|
27
|
+
const model = document.parseResult.value;
|
|
28
|
+
if (!isModel(model)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (reference.idea?.ref) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (model.elements.some(element =>
|
|
35
|
+
(isIdea(element) || isOneLinerIdea(element)) && element.name === bindingName
|
|
36
|
+
)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return findNamespaceImportByAlias(model.imports, bindingName) !== undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveNamespaceImportReferenceLink(
|
|
43
|
+
reference: LocalReference | QualifiedReference,
|
|
44
|
+
documents: LangiumDocuments,
|
|
45
|
+
fileSystem: FileSystemProvider,
|
|
46
|
+
context?: PathResolveContext
|
|
47
|
+
): ResolvedFileLink | undefined {
|
|
48
|
+
const document = AstUtils.getDocument(reference);
|
|
49
|
+
const model = document.parseResult.value;
|
|
50
|
+
if (!isModel(model)) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const bindingName = reference.$type === 'QualifiedReference'
|
|
54
|
+
? reference.qualifier?.$refText
|
|
55
|
+
: namespaceImportBindingName(reference);
|
|
56
|
+
if (!bindingName) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const importDecl = reference.$type === 'QualifiedReference'
|
|
60
|
+
? reference.qualifier?.ref
|
|
61
|
+
: findNamespaceImportByAlias(model.imports, bindingName);
|
|
62
|
+
if (!importDecl || !isNamespaceImportBinding(importDecl, bindingName)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const pathNode = referencePathNode(reference, importDecl);
|
|
66
|
+
if (!pathNode) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return resolveImportedFileLink(
|
|
70
|
+
document,
|
|
71
|
+
documents,
|
|
72
|
+
fileSystem,
|
|
73
|
+
importDecl.path,
|
|
74
|
+
bindingNameSourceRange(pathNode, bindingName),
|
|
75
|
+
context
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isNamespaceImportBinding(importDecl: Import, bindingName: string): boolean {
|
|
80
|
+
return findNamespaceImportByAlias([importDecl], bindingName) !== undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function referencePathNode(reference: LocalReference | QualifiedReference, importDecl: Import): CstNode | undefined {
|
|
84
|
+
if (reference.$type === 'QualifiedReference') {
|
|
85
|
+
return reference.qualifier?.$refNode
|
|
86
|
+
?? GrammarUtils.findNodeForProperty(reference.$cstNode, 'qualifier');
|
|
87
|
+
}
|
|
88
|
+
if (reference.idea?.$refNode) {
|
|
89
|
+
return reference.idea.$refNode;
|
|
90
|
+
}
|
|
91
|
+
return GrammarUtils.findNodeForProperty(reference.$cstNode, 'idea');
|
|
92
|
+
}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared import / anonymous file-path completion candidates, folder browsing, and proximity ranking.
|
|
3
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_code_completion]
|
|
4
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_code_completion_explicit_extension]
|
|
5
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_code_completion_path_segments]
|
|
6
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_code_completion_substring_match]
|
|
7
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".import_code_completion_ranking]
|
|
8
|
+
* rq:["../../../reqlan rq/extension/language-support/features-imports.rq".anonymous_reference_code_completion]
|
|
9
|
+
*/
|
|
10
|
+
import type { FileSystemProvider, LangiumDocument, LangiumDocuments, URI } from 'langium';
|
|
11
|
+
import { UriUtils } from 'langium';
|
|
12
|
+
import { relativeRqImportPath } from './reqlan-import-edits.js';
|
|
13
|
+
import {
|
|
14
|
+
resolveDocumentPathUri,
|
|
15
|
+
resolveImportRootUri,
|
|
16
|
+
resolveRqConfig,
|
|
17
|
+
type PathResolveContext
|
|
18
|
+
} from './reqlan-path-resolve.js';
|
|
19
|
+
|
|
20
|
+
export const UNREACHABLE_PATH_DISTANCE = 9999;
|
|
21
|
+
|
|
22
|
+
export interface PathCompletionCandidate {
|
|
23
|
+
path: string;
|
|
24
|
+
targetUri: URI;
|
|
25
|
+
isDirectory: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface CollectPathCompletionOptions {
|
|
29
|
+
/** When set (e.g. `.rq`), only files with that extension are included. Directories always included. */
|
|
30
|
+
extensionFilter?: string;
|
|
31
|
+
/** Unquoted typed query; folder browsing when it ends with `/`, otherwise substring/subsequence search. */
|
|
32
|
+
prefix?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Directory hop count between two directory URIs (each `..` or named segment counts). */
|
|
36
|
+
export function directoryHopDistance(fromDir: URI, toDir: URI): number {
|
|
37
|
+
const relative = UriUtils.relative(fromDir.toString(), toDir.toString());
|
|
38
|
+
if (!relative || relative === '.') {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
return relative.split('/').filter(segment => segment.length > 0 && segment !== '.').length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Proximity of a path candidate from the completing document's directory. */
|
|
45
|
+
export function pathCandidateHopDistance(document: LangiumDocument, candidate: PathCompletionCandidate): number {
|
|
46
|
+
const fromDir = UriUtils.dirname(document.uri);
|
|
47
|
+
const toDir = candidate.isDirectory ? candidate.targetUri : UriUtils.dirname(candidate.targetUri);
|
|
48
|
+
return directoryHopDistance(fromDir, toDir);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Rank by proximity, then directories before files, then path alphabetically.
|
|
53
|
+
* Puts `../path/` before `../path/file.rq` when hop distance ties.
|
|
54
|
+
*/
|
|
55
|
+
export function pathCompletionSortText(document: LangiumDocument, candidate: PathCompletionCandidate): string {
|
|
56
|
+
const hops = pathCandidateHopDistance(document, candidate);
|
|
57
|
+
const directoryRank = candidate.isDirectory ? '0' : '1';
|
|
58
|
+
return `${String(hops).padStart(4, '0')}_${directoryRank}_${candidate.path}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Equalize client fuzzy-match scores so sortText (proximity) wins.
|
|
63
|
+
* VS Code/Cursor re-ranks by filterText/label match first; sortText is only a tie-breaker.
|
|
64
|
+
* The server already filters via pathMatchesQuery and returns isIncomplete lists.
|
|
65
|
+
*/
|
|
66
|
+
export function pathCompletionFilterText(prefix: string, candidate: PathCompletionCandidate): string {
|
|
67
|
+
return prefix.length > 0 ? prefix : candidate.path;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function comparePathCompletionCandidates(
|
|
71
|
+
document: LangiumDocument,
|
|
72
|
+
left: PathCompletionCandidate,
|
|
73
|
+
right: PathCompletionCandidate
|
|
74
|
+
): number {
|
|
75
|
+
return pathCompletionSortText(document, left).localeCompare(pathCompletionSortText(document, right));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function relativePathKeepExtension(fromDir: string, targetUri: URI): string {
|
|
79
|
+
let relativePath = UriUtils.relative(fromDir, targetUri.toString());
|
|
80
|
+
if (!relativePath.startsWith('./') && !relativePath.startsWith('../')) {
|
|
81
|
+
relativePath = `./${relativePath}`;
|
|
82
|
+
}
|
|
83
|
+
return relativePath;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function aliasedPathKeepExtension(
|
|
87
|
+
rootString: string,
|
|
88
|
+
aliasPrefix: string,
|
|
89
|
+
targetUri: URI
|
|
90
|
+
): string | undefined {
|
|
91
|
+
const relativePath = UriUtils.relative(rootString, targetUri.toString());
|
|
92
|
+
if (!relativePath || relativePath.startsWith('..')) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const cleaned = relativePath.replace(/^\.\//, '');
|
|
96
|
+
return `${aliasPrefix}${cleaned}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Match a typed query against a candidate path using substring search (not only starts-with).
|
|
101
|
+
* Also matches when query characters appear in order after stripping `/` and `.` path markers
|
|
102
|
+
* (so `hellopath` matches `../../hello/inbetween/path/file.rq`).
|
|
103
|
+
*/
|
|
104
|
+
export function pathMatchesQuery(path: string, query: string): boolean {
|
|
105
|
+
if (!query) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
const pathLower = path.toLowerCase();
|
|
109
|
+
const queryLower = query.toLowerCase();
|
|
110
|
+
if (pathLower.includes(queryLower)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
const strippedPath = stripPathSeparators(pathLower);
|
|
114
|
+
const strippedQuery = stripPathSeparators(queryLower);
|
|
115
|
+
if (!strippedQuery) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (strippedPath.includes(strippedQuery)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return isCharacterSubsequence(strippedPath, strippedQuery);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function stripPathSeparators(value: string): string {
|
|
125
|
+
return value.replace(/\.?\.\//g, '').replace(/\.\//g, '').replace(/\//g, '');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isCharacterSubsequence(haystack: string, needle: string): boolean {
|
|
129
|
+
let index = 0;
|
|
130
|
+
for (const char of haystack) {
|
|
131
|
+
if (char === needle[index]) {
|
|
132
|
+
index += 1;
|
|
133
|
+
if (index >= needle.length) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function collectPathCompletionCandidates(
|
|
142
|
+
document: LangiumDocument,
|
|
143
|
+
documents: LangiumDocuments,
|
|
144
|
+
fileSystem: FileSystemProvider,
|
|
145
|
+
pathContext: PathResolveContext,
|
|
146
|
+
options: CollectPathCompletionOptions = {}
|
|
147
|
+
): PathCompletionCandidate[] {
|
|
148
|
+
const byPath = new Map<string, PathCompletionCandidate>();
|
|
149
|
+
const add = (candidate: PathCompletionCandidate) => {
|
|
150
|
+
byPath.set(candidate.path, candidate);
|
|
151
|
+
};
|
|
152
|
+
const addFile = (candidate: PathCompletionCandidate) => {
|
|
153
|
+
add(candidate);
|
|
154
|
+
for (const directory of intermediateDirectoryCandidates(candidate)) {
|
|
155
|
+
add(directory);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
for (const candidate of collectRelativePathCandidates(document, documents, fileSystem, options.extensionFilter)) {
|
|
160
|
+
if (candidate.isDirectory) {
|
|
161
|
+
add(candidate);
|
|
162
|
+
} else {
|
|
163
|
+
addFile(candidate);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
for (const candidate of collectAliasedPathCandidates(document, documents, fileSystem, pathContext, options.extensionFilter)) {
|
|
167
|
+
if (candidate.isDirectory) {
|
|
168
|
+
add(candidate);
|
|
169
|
+
} else {
|
|
170
|
+
addFile(candidate);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const query = options.prefix ?? '';
|
|
174
|
+
if (query.endsWith('/')) {
|
|
175
|
+
for (const candidate of collectFolderSegmentCandidates(
|
|
176
|
+
document,
|
|
177
|
+
query,
|
|
178
|
+
fileSystem,
|
|
179
|
+
pathContext,
|
|
180
|
+
options.extensionFilter
|
|
181
|
+
)) {
|
|
182
|
+
add(candidate);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let candidates = [...byPath.values()];
|
|
187
|
+
if (query.length > 0) {
|
|
188
|
+
candidates = candidates.filter(candidate => pathMatchesQuery(candidate.path, query));
|
|
189
|
+
}
|
|
190
|
+
return candidates;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Directory prefixes for a file path, e.g. `../path/file.rq` → `../path/`. */
|
|
194
|
+
export function intermediateDirectoryPaths(filePath: string): string[] {
|
|
195
|
+
const dirs: string[] = [];
|
|
196
|
+
let index = 0;
|
|
197
|
+
while (index < filePath.length) {
|
|
198
|
+
const slash = filePath.indexOf('/', index);
|
|
199
|
+
if (slash < 0 || slash === filePath.length - 1) {
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
const prefix = filePath.slice(0, slash + 1);
|
|
203
|
+
// Skip `./`, runs of `../`, and bare alias roots like `@/`.
|
|
204
|
+
const skip = prefix === '@/'
|
|
205
|
+
|| /^\.\/$/.test(prefix)
|
|
206
|
+
|| /^(\.\.\/)+$/.test(prefix);
|
|
207
|
+
if (!skip) {
|
|
208
|
+
dirs.push(prefix);
|
|
209
|
+
}
|
|
210
|
+
index = slash + 1;
|
|
211
|
+
}
|
|
212
|
+
return dirs;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function intermediateDirectoryCandidates(fileCandidate: PathCompletionCandidate): PathCompletionCandidate[] {
|
|
216
|
+
const paths = intermediateDirectoryPaths(fileCandidate.path);
|
|
217
|
+
if (paths.length === 0) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
const result: PathCompletionCandidate[] = [];
|
|
221
|
+
let dirUri = UriUtils.dirname(fileCandidate.targetUri);
|
|
222
|
+
for (let i = paths.length - 1; i >= 0; i--) {
|
|
223
|
+
result.push({
|
|
224
|
+
path: paths[i]!,
|
|
225
|
+
targetUri: dirUri,
|
|
226
|
+
isDirectory: true
|
|
227
|
+
});
|
|
228
|
+
dirUri = UriUtils.dirname(dirUri);
|
|
229
|
+
}
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function collectRelativePathCandidates(
|
|
234
|
+
document: LangiumDocument,
|
|
235
|
+
documents: LangiumDocuments,
|
|
236
|
+
fileSystem: FileSystemProvider,
|
|
237
|
+
extensionFilter?: string
|
|
238
|
+
): PathCompletionCandidate[] {
|
|
239
|
+
const sourceDir = UriUtils.dirname(document.uri);
|
|
240
|
+
const dirname = sourceDir.toString();
|
|
241
|
+
const candidates: PathCompletionCandidate[] = [];
|
|
242
|
+
|
|
243
|
+
for (const doc of documents.all.toArray()) {
|
|
244
|
+
if (UriUtils.equals(doc.uri, document.uri)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (extensionFilter && !doc.uri.path.endsWith(extensionFilter)) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
candidates.push({
|
|
251
|
+
path: relativeRqImportPath(document.uri, doc.uri),
|
|
252
|
+
targetUri: doc.uri,
|
|
253
|
+
isDirectory: false
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (fileSystem.existsSync(sourceDir)) {
|
|
258
|
+
collectDirectoryTree(sourceDir, dirname, undefined, undefined, candidates, extensionFilter, fileSystem);
|
|
259
|
+
}
|
|
260
|
+
return candidates;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function collectAliasedPathCandidates(
|
|
264
|
+
document: LangiumDocument,
|
|
265
|
+
documents: LangiumDocuments,
|
|
266
|
+
fileSystem: FileSystemProvider,
|
|
267
|
+
pathContext: PathResolveContext,
|
|
268
|
+
extensionFilter?: string
|
|
269
|
+
): PathCompletionCandidate[] {
|
|
270
|
+
const config = resolveRqConfig(document, pathContext);
|
|
271
|
+
const candidates: PathCompletionCandidate[] = [];
|
|
272
|
+
for (const mapping of config.importRoots) {
|
|
273
|
+
const importRoot = resolveImportRootUri(document, pathContext, mapping);
|
|
274
|
+
if (!importRoot) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const aliasPrefix = `${mapping.alias}/`;
|
|
278
|
+
const rootString = importRoot.toString();
|
|
279
|
+
for (const doc of documents.all.toArray()) {
|
|
280
|
+
if (UriUtils.equals(doc.uri, document.uri)) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (extensionFilter && !doc.uri.path.endsWith(extensionFilter)) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const aliased = aliasedPathKeepExtension(rootString, aliasPrefix, doc.uri);
|
|
287
|
+
if (aliased) {
|
|
288
|
+
candidates.push({
|
|
289
|
+
path: aliased,
|
|
290
|
+
targetUri: doc.uri,
|
|
291
|
+
isDirectory: false
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (fileSystem.existsSync(importRoot)) {
|
|
296
|
+
collectDirectoryTree(
|
|
297
|
+
importRoot,
|
|
298
|
+
rootString,
|
|
299
|
+
aliasPrefix,
|
|
300
|
+
rootString,
|
|
301
|
+
candidates,
|
|
302
|
+
extensionFilter,
|
|
303
|
+
fileSystem
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return candidates;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function collectDirectoryTree(
|
|
311
|
+
directory: URI,
|
|
312
|
+
relativeFrom: string,
|
|
313
|
+
aliasPrefix: string | undefined,
|
|
314
|
+
aliasRoot: string | undefined,
|
|
315
|
+
candidates: PathCompletionCandidate[],
|
|
316
|
+
extensionFilter: string | undefined,
|
|
317
|
+
fileSystem: FileSystemProvider,
|
|
318
|
+
depth = 0
|
|
319
|
+
): void {
|
|
320
|
+
if (depth > 32) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
for (const entry of fileSystem.readDirectorySync(directory)) {
|
|
324
|
+
const name = entry.uri.path.split('/').pop() ?? '';
|
|
325
|
+
if (!name || name.startsWith('.')) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (entry.isDirectory) {
|
|
329
|
+
const path = aliasPrefix && aliasRoot
|
|
330
|
+
? (() => {
|
|
331
|
+
const aliased = aliasedPathKeepExtension(aliasRoot, aliasPrefix, entry.uri);
|
|
332
|
+
return aliased ? `${aliased}/` : undefined;
|
|
333
|
+
})()
|
|
334
|
+
: `${relativePathKeepExtension(relativeFrom, entry.uri)}/`;
|
|
335
|
+
if (path) {
|
|
336
|
+
candidates.push({ path, targetUri: entry.uri, isDirectory: true });
|
|
337
|
+
}
|
|
338
|
+
collectDirectoryTree(
|
|
339
|
+
entry.uri,
|
|
340
|
+
relativeFrom,
|
|
341
|
+
aliasPrefix,
|
|
342
|
+
aliasRoot,
|
|
343
|
+
candidates,
|
|
344
|
+
extensionFilter,
|
|
345
|
+
fileSystem,
|
|
346
|
+
depth + 1
|
|
347
|
+
);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (extensionFilter && !name.endsWith(extensionFilter)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (aliasPrefix && aliasRoot) {
|
|
354
|
+
const aliased = aliasedPathKeepExtension(aliasRoot, aliasPrefix, entry.uri);
|
|
355
|
+
if (aliased) {
|
|
356
|
+
candidates.push({ path: aliased, targetUri: entry.uri, isDirectory: false });
|
|
357
|
+
}
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
candidates.push({
|
|
361
|
+
path: relativePathKeepExtension(relativeFrom, entry.uri),
|
|
362
|
+
targetUri: entry.uri,
|
|
363
|
+
isDirectory: false
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function collectFolderSegmentCandidates(
|
|
369
|
+
document: LangiumDocument,
|
|
370
|
+
prefix: string,
|
|
371
|
+
fileSystem: FileSystemProvider,
|
|
372
|
+
pathContext: PathResolveContext,
|
|
373
|
+
extensionFilter?: string
|
|
374
|
+
): PathCompletionCandidate[] {
|
|
375
|
+
const dirUri = resolveFolderPrefixUri(prefix, document, pathContext, fileSystem);
|
|
376
|
+
if (!dirUri) {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
const candidates: PathCompletionCandidate[] = [];
|
|
380
|
+
for (const entry of fileSystem.readDirectorySync(dirUri)) {
|
|
381
|
+
const name = entry.uri.path.split('/').pop() ?? '';
|
|
382
|
+
if (!name || name.startsWith('.')) {
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (entry.isDirectory) {
|
|
386
|
+
candidates.push({
|
|
387
|
+
path: `${prefix}${name}/`,
|
|
388
|
+
targetUri: entry.uri,
|
|
389
|
+
isDirectory: true
|
|
390
|
+
});
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (extensionFilter && !name.endsWith(extensionFilter)) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
candidates.push({
|
|
397
|
+
path: `${prefix}${name}`,
|
|
398
|
+
targetUri: entry.uri,
|
|
399
|
+
isDirectory: false
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return candidates;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function resolveFolderPrefixUri(
|
|
406
|
+
prefix: string,
|
|
407
|
+
document: LangiumDocument,
|
|
408
|
+
pathContext: PathResolveContext,
|
|
409
|
+
fileSystem: FileSystemProvider
|
|
410
|
+
): URI | undefined {
|
|
411
|
+
const dirUri = resolveDocumentPathUri(prefix, document, pathContext);
|
|
412
|
+
if (!fileSystem.existsSync(dirUri)) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
const stat = fileSystem.statSync(dirUri);
|
|
416
|
+
if (!stat.isDirectory) {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
return dirUri;
|
|
420
|
+
}
|