@greenpandastudios/aug-cli 0.19.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/LICENSE +21 -0
- package/README.md +13 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/bin/aug.mjs +3 -0
- package/docs/api/crypto.md +351 -0
- package/docs/api/io.md +207 -0
- package/docs/api/json.md +21 -0
- package/docs/api/memory.md +109 -0
- package/docs/api/time.md +54 -0
- package/docs/api/web.md +184 -0
- package/docs/assets/benchmarks/execution.svg +2978 -0
- package/docs/assets/benchmarks/http.svg +1674 -0
- package/docs/assets/benchmarks/improvements.svg +2315 -0
- package/docs/assets/benchmarks/memory.svg +1891 -0
- package/docs/benchmark-baseline.json +1087 -0
- package/docs/benchmark-results.json +91123 -0
- package/docs/benchmarks.json +38 -0
- package/docs/compatibility.md +42 -0
- package/docs/concurrency-implementation.md +18 -0
- package/docs/diagnostics.md +97 -0
- package/docs/docker.md +35 -0
- package/docs/example-projects.json +26 -0
- package/docs/examples/approved-design/counters.md +253 -0
- package/docs/examples/approved-design/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/approved-design/domain/app.md +165 -0
- package/docs/examples/approved-design/domain/export.md +83 -0
- package/docs/examples/approved-design/domain/models.md +80 -0
- package/docs/examples/approved-design/domain/numbers.md +248 -0
- package/docs/examples/approved-design/index.md +39 -0
- package/docs/examples/approved-design/main-yaml.md +24 -0
- package/docs/examples/approved-design/main.md +215 -0
- package/docs/examples/benchmark/index.md +33 -0
- package/docs/examples/benchmark/main-yaml.md +17 -0
- package/docs/examples/benchmark/main.md +117 -0
- package/docs/examples/cli-args/index.md +32 -0
- package/docs/examples/cli-args/main.md +107 -0
- package/docs/examples/collections/index.md +32 -0
- package/docs/examples/collections/main.md +118 -0
- package/docs/examples/collections-benchmark/index.md +34 -0
- package/docs/examples/collections-benchmark/main.md +115 -0
- package/docs/examples/cpu-benchmark/index.md +34 -0
- package/docs/examples/cpu-benchmark/main.md +90 -0
- package/docs/examples/developer-workflow/calculator.md +357 -0
- package/docs/examples/developer-workflow/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/developer-workflow/index.md +37 -0
- package/docs/examples/developer-workflow/logging/console.md +124 -0
- package/docs/examples/developer-workflow/logging/export.md +70 -0
- package/docs/examples/developer-workflow/logging/logger.md +111 -0
- package/docs/examples/developer-workflow/main.md +172 -0
- package/docs/examples/drop/index.md +33 -0
- package/docs/examples/drop/main.md +85 -0
- package/docs/examples/drop/resource.md +95 -0
- package/docs/examples/errors/errors.md +85 -0
- package/docs/examples/errors/index.md +33 -0
- package/docs/examples/errors/main.md +92 -0
- package/docs/examples/ffi/index.md +33 -0
- package/docs/examples/ffi/main.md +75 -0
- package/docs/examples/ffi/native.md +93 -0
- package/docs/examples/generic-di/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/generic-di/index.md +33 -0
- package/docs/examples/generic-di/main.md +111 -0
- package/docs/examples/generic-di/types.md +180 -0
- package/docs/examples/generics/index.md +33 -0
- package/docs/examples/generics/main.md +120 -0
- package/docs/examples/generics/types.md +189 -0
- package/docs/examples/hello/app/export.md +67 -0
- package/docs/examples/hello/app/greeter.md +190 -0
- package/docs/examples/hello/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/hello/index.md +37 -0
- package/docs/examples/hello/logging/console.md +121 -0
- package/docs/examples/hello/logging/export.md +71 -0
- package/docs/examples/hello/logging/logger.md +120 -0
- package/docs/examples/hello/main.md +115 -0
- package/docs/examples/http-benchmark/index.md +35 -0
- package/docs/examples/http-benchmark/main.md +79 -0
- package/docs/examples/http-benchmark/routes.md +88 -0
- package/docs/examples/index.md +60 -0
- package/docs/examples/interceptors/app.md +250 -0
- package/docs/examples/interceptors/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/interceptors/index.md +35 -0
- package/docs/examples/interceptors/interceptors.md +284 -0
- package/docs/examples/interceptors/logging.md +155 -0
- package/docs/examples/interceptors/main.md +162 -0
- package/docs/examples/json-benchmark/data.md +71 -0
- package/docs/examples/json-benchmark/dependencies/august/0.19.0/json/contracts.md +103 -0
- package/docs/examples/json-benchmark/index.md +35 -0
- package/docs/examples/json-benchmark/main.md +131 -0
- package/docs/examples/new-syntax/console.md +118 -0
- package/docs/examples/new-syntax/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/new-syntax/greeter.md +145 -0
- package/docs/examples/new-syntax/index.md +36 -0
- package/docs/examples/new-syntax/logger.md +111 -0
- package/docs/examples/new-syntax/main.md +135 -0
- package/docs/examples/new-syntax/math.md +79 -0
- package/docs/examples/oidc-login/client/contracts.md +151 -0
- package/docs/examples/oidc-login/client/endpoints.md +263 -0
- package/docs/examples/oidc-login/client/export.md +107 -0
- package/docs/examples/oidc-login/client/login.md +515 -0
- package/docs/examples/oidc-login/client/logout.md +249 -0
- package/docs/examples/oidc-login/client/protocol.md +595 -0
- package/docs/examples/oidc-login/client/session.md +297 -0
- package/docs/examples/oidc-login/client/views.md +144 -0
- package/docs/examples/oidc-login/common/export.md +115 -0
- package/docs/examples/oidc-login/common/headers.md +162 -0
- package/docs/examples/oidc-login/common/keys.md +336 -0
- package/docs/examples/oidc-login/common/settings.md +116 -0
- package/docs/examples/oidc-login/common/views.md +103 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/contracts.md +925 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/crypto/jose.md +434 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/json/contracts.md +103 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/memory/store.md +374 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/time/contracts.md +150 -0
- package/docs/examples/oidc-login/dependencies/august/0.19.0/web/contracts.md +532 -0
- package/docs/examples/oidc-login/index.md +57 -0
- package/docs/examples/oidc-login/main-yaml.md +28 -0
- package/docs/examples/oidc-login/main.md +283 -0
- package/docs/examples/oidc-login/provider/authorization.md +457 -0
- package/docs/examples/oidc-login/provider/contracts.md +267 -0
- package/docs/examples/oidc-login/provider/credentials.md +148 -0
- package/docs/examples/oidc-login/provider/discovery.md +215 -0
- package/docs/examples/oidc-login/provider/export.md +131 -0
- package/docs/examples/oidc-login/provider/token.md +364 -0
- package/docs/examples/oidc-login/provider/userinfo.md +228 -0
- package/docs/examples/oidc-login/provider/views.md +137 -0
- package/docs/examples/ownership/counter.md +140 -0
- package/docs/examples/ownership/index.md +33 -0
- package/docs/examples/ownership/main.md +90 -0
- package/docs/examples/ownership-transfer/dependencies/august/0.19.0/io/contracts.md +395 -0
- package/docs/examples/ownership-transfer/index.md +33 -0
- package/docs/examples/ownership-transfer/main.md +125 -0
- package/docs/examples/ownership-transfer/resource.md +150 -0
- package/docs/examples/packages-app/dependencies/packages/@example/aug-math/0.1.0/arithmetic.md +117 -0
- package/docs/examples/packages-app/index.md +34 -0
- package/docs/examples/packages-app/main-yaml.md +18 -0
- package/docs/examples/packages-app/main.md +80 -0
- package/docs/examples/packages-math/aug-package-json.md +24 -0
- package/docs/examples/packages-math/index.md +36 -0
- package/docs/examples/packages-math/package-json.md +25 -0
- package/docs/examples/packages-math/src/arithmetic.md +121 -0
- package/docs/examples/packages-math/src/export.md +63 -0
- package/docs/examples/startup-benchmark/index.md +34 -0
- package/docs/examples/startup-benchmark/main.md +68 -0
- package/docs/examples/visibility/counter.md +142 -0
- package/docs/examples/visibility/index.md +33 -0
- package/docs/examples/visibility/main.md +96 -0
- package/docs/examples.json +47 -0
- package/docs/getting-started.md +5 -0
- package/docs/grammar.md +136 -0
- package/docs/implementation-map.md +106 -0
- package/docs/index.md +46 -0
- package/docs/language-conformance.md +30 -0
- package/docs/language-constructs.md +1461 -0
- package/docs/language-design-audit.md +147 -0
- package/docs/maintaining-docs.md +43 -0
- package/docs/packages.md +184 -0
- package/docs/performance.md +535 -0
- package/docs/production-readiness.md +39 -0
- package/docs/reference.md +361 -0
- package/docs/release-review.md +23 -0
- package/docs/releasing.md +79 -0
- package/docs/roadmap.md +13 -0
- package/docs/specifications.md +86 -0
- package/docs/testing.md +120 -0
- package/docs/tooling.md +197 -0
- package/docs/web-implementation-plan.md +39 -0
- package/docs/web-library-gaps.md +27 -0
- package/docs/web.md +197 -0
- package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/approved-design/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/approved-design/.aug-spec/manifest.json +14 -0
- package/examples/approved-design/counters.aug +22 -0
- package/examples/approved-design/counters.aug.md +177 -0
- package/examples/approved-design/domain/app.aug +12 -0
- package/examples/approved-design/domain/app.aug.md +97 -0
- package/examples/approved-design/domain/export.aug +5 -0
- package/examples/approved-design/domain/export.aug.md +25 -0
- package/examples/approved-design/domain/models.aug +2 -0
- package/examples/approved-design/domain/models.aug.md +30 -0
- package/examples/approved-design/domain/numbers.aug +29 -0
- package/examples/approved-design/domain/numbers.aug.md +141 -0
- package/examples/approved-design/main.aug +26 -0
- package/examples/approved-design/main.aug.md +108 -0
- package/examples/approved-design/main.yaml +8 -0
- package/examples/benchmark/.aug-spec/manifest.json +7 -0
- package/examples/benchmark/main.aug +17 -0
- package/examples/benchmark/main.aug.md +43 -0
- package/examples/benchmark/main.yaml +1 -0
- package/examples/cli-args/.aug-spec/manifest.json +7 -0
- package/examples/cli-args/main.aug +15 -0
- package/examples/cli-args/main.aug.md +38 -0
- package/examples/collections/.aug-spec/manifest.json +7 -0
- package/examples/collections/main.aug +18 -0
- package/examples/collections/main.aug.md +43 -0
- package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/developer-workflow/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/developer-workflow/.aug-spec/manifest.json +13 -0
- package/examples/developer-workflow/calculator.aug +55 -0
- package/examples/developer-workflow/calculator.aug.md +228 -0
- package/examples/developer-workflow/logging/console.aug +8 -0
- package/examples/developer-workflow/logging/console.aug.md +67 -0
- package/examples/developer-workflow/logging/export.aug +2 -0
- package/examples/developer-workflow/logging/export.aug.md +19 -0
- package/examples/developer-workflow/logging/logger.aug +6 -0
- package/examples/developer-workflow/logging/logger.aug.md +57 -0
- package/examples/developer-workflow/main.aug +25 -0
- package/examples/developer-workflow/main.aug.md +79 -0
- package/examples/drop/.aug-spec/manifest.json +8 -0
- package/examples/drop/main.aug +3 -0
- package/examples/drop/main.aug.md +35 -0
- package/examples/drop/resource.aug +8 -0
- package/examples/drop/resource.aug.md +44 -0
- package/examples/errors/.aug-spec/manifest.json +8 -0
- package/examples/errors/errors.aug +6 -0
- package/examples/errors/errors.aug.md +33 -0
- package/examples/errors/main.aug +7 -0
- package/examples/errors/main.aug.md +36 -0
- package/examples/ffi/.aug-spec/manifest.json +8 -0
- package/examples/ffi/main.aug +2 -0
- package/examples/ffi/main.aug.md +27 -0
- package/examples/ffi/native.aug +6 -0
- package/examples/ffi/native.aug.md +43 -0
- package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/generic-di/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/generic-di/.aug-spec/manifest.json +10 -0
- package/examples/generic-di/main.aug +9 -0
- package/examples/generic-di/main.aug.md +49 -0
- package/examples/generic-di/types.aug +17 -0
- package/examples/generic-di/types.aug.md +124 -0
- package/examples/generics/.aug-spec/manifest.json +8 -0
- package/examples/generics/main.aug +9 -0
- package/examples/generics/main.aug.md +58 -0
- package/examples/generics/types.aug +19 -0
- package/examples/generics/types.aug.md +132 -0
- package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/hello/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/hello/.aug-spec/manifest.json +14 -0
- package/examples/hello/app/export.aug +1 -0
- package/examples/hello/app/export.aug.md +17 -0
- package/examples/hello/app/greeter.aug +22 -0
- package/examples/hello/app/greeter.aug.md +109 -0
- package/examples/hello/logging/console.aug +7 -0
- package/examples/hello/logging/console.aug.md +65 -0
- package/examples/hello/logging/export.aug +2 -0
- package/examples/hello/logging/export.aug.md +19 -0
- package/examples/hello/logging/logger.aug +9 -0
- package/examples/hello/logging/logger.aug.md +59 -0
- package/examples/hello/main.aug +9 -0
- package/examples/hello/main.aug.md +49 -0
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/interceptors/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/interceptors/.aug-spec/manifest.json +12 -0
- package/examples/interceptors/app.aug +28 -0
- package/examples/interceptors/app.aug.md +162 -0
- package/examples/interceptors/interceptors.aug +40 -0
- package/examples/interceptors/interceptors.aug.md +180 -0
- package/examples/interceptors/logging.aug +12 -0
- package/examples/interceptors/logging.aug.md +96 -0
- package/examples/interceptors/main.aug +22 -0
- package/examples/interceptors/main.aug.md +76 -0
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/new-syntax/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/new-syntax/.aug-spec/manifest.json +13 -0
- package/examples/new-syntax/console.aug +7 -0
- package/examples/new-syntax/console.aug.md +63 -0
- package/examples/new-syntax/greeter.aug +10 -0
- package/examples/new-syntax/greeter.aug.md +89 -0
- package/examples/new-syntax/logger.aug +6 -0
- package/examples/new-syntax/logger.aug.md +57 -0
- package/examples/new-syntax/main.aug +12 -0
- package/examples/new-syntax/main.aug.md +64 -0
- package/examples/new-syntax/math.aug +3 -0
- package/examples/new-syntax/math.aug.md +29 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug +73 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/contracts.aug.md +791 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug +68 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/crypto/jose.aug.md +266 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug +6 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/json/contracts.aug.md +55 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug +45 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/memory/store.aug.md +250 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug +11 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/time/contracts.aug.md +96 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug +58 -0
- package/examples/oidc-login/.aug-spec/august/0.19.0/web/contracts.aug.md +420 -0
- package/examples/oidc-login/.aug-spec/manifest.json +40 -0
- package/examples/oidc-login/README.md +29 -0
- package/examples/oidc-login/client/contracts.aug +7 -0
- package/examples/oidc-login/client/contracts.aug.md +80 -0
- package/examples/oidc-login/client/endpoints.aug +21 -0
- package/examples/oidc-login/client/endpoints.aug.md +161 -0
- package/examples/oidc-login/client/export.aug +7 -0
- package/examples/oidc-login/client/export.aug.md +29 -0
- package/examples/oidc-login/client/login.aug +58 -0
- package/examples/oidc-login/client/login.aug.md +331 -0
- package/examples/oidc-login/client/logout.aug +18 -0
- package/examples/oidc-login/client/logout.aug.md +150 -0
- package/examples/oidc-login/client/protocol.aug +106 -0
- package/examples/oidc-login/client/protocol.aug.md +326 -0
- package/examples/oidc-login/client/session.aug +34 -0
- package/examples/oidc-login/client/session.aug.md +155 -0
- package/examples/oidc-login/client/views.aug +21 -0
- package/examples/oidc-login/client/views.aug.md +68 -0
- package/examples/oidc-login/common/export.aug +9 -0
- package/examples/oidc-login/common/export.aug.md +33 -0
- package/examples/oidc-login/common/headers.aug +11 -0
- package/examples/oidc-login/common/headers.aug.md +79 -0
- package/examples/oidc-login/common/keys.aug +37 -0
- package/examples/oidc-login/common/keys.aug.md +207 -0
- package/examples/oidc-login/common/settings.aug +4 -0
- package/examples/oidc-login/common/settings.aug.md +47 -0
- package/examples/oidc-login/common/views.aug +16 -0
- package/examples/oidc-login/common/views.aug.md +34 -0
- package/examples/oidc-login/main.aug +39 -0
- package/examples/oidc-login/main.aug.md +166 -0
- package/examples/oidc-login/main.yaml +12 -0
- package/examples/oidc-login/provider/authorization.aug +59 -0
- package/examples/oidc-login/provider/authorization.aug.md +264 -0
- package/examples/oidc-login/provider/contracts.aug +15 -0
- package/examples/oidc-login/provider/contracts.aug.md +193 -0
- package/examples/oidc-login/provider/credentials.aug +10 -0
- package/examples/oidc-login/provider/credentials.aug.md +64 -0
- package/examples/oidc-login/provider/discovery.aug +13 -0
- package/examples/oidc-login/provider/discovery.aug.md +133 -0
- package/examples/oidc-login/provider/export.aug +13 -0
- package/examples/oidc-login/provider/export.aug.md +41 -0
- package/examples/oidc-login/provider/token.aug +37 -0
- package/examples/oidc-login/provider/token.aug.md +223 -0
- package/examples/oidc-login/provider/userinfo.aug +26 -0
- package/examples/oidc-login/provider/userinfo.aug.md +104 -0
- package/examples/oidc-login/provider/views.aug +18 -0
- package/examples/oidc-login/provider/views.aug.md +63 -0
- package/examples/ownership/.aug-spec/manifest.json +8 -0
- package/examples/ownership/counter.aug +14 -0
- package/examples/ownership/counter.aug.md +85 -0
- package/examples/ownership/main.aug +4 -0
- package/examples/ownership/main.aug.md +38 -0
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug +36 -0
- package/examples/ownership-transfer/.aug-spec/august/0.19.0/io/contracts.aug.md +316 -0
- package/examples/ownership-transfer/.aug-spec/manifest.json +10 -0
- package/examples/ownership-transfer/main.aug +9 -0
- package/examples/ownership-transfer/main.aug.md +63 -0
- package/examples/ownership-transfer/resource.aug +16 -0
- package/examples/ownership-transfer/resource.aug.md +89 -0
- package/examples/packages/README.md +15 -0
- package/examples/packages/app/.aug-spec/manifest.json +9 -0
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug +9 -0
- package/examples/packages/app/.aug-spec/packages/@example/aug-math/0.1.0/arithmetic.aug.md +63 -0
- package/examples/packages/app/main.aug +2 -0
- package/examples/packages/app/main.aug.md +33 -0
- package/examples/packages/app/main.yaml +2 -0
- package/examples/packages/math/.aug-spec/manifest.json +8 -0
- package/examples/packages/math/README.md +3 -0
- package/examples/packages/math/aug-package.json +8 -0
- package/examples/packages/math/package.json +9 -0
- package/examples/packages/math/src/arithmetic.aug +8 -0
- package/examples/packages/math/src/arithmetic.aug.md +63 -0
- package/examples/packages/math/src/export.aug +1 -0
- package/examples/packages/math/src/export.aug.md +17 -0
- package/examples/visibility/.aug-spec/manifest.json +8 -0
- package/examples/visibility/counter.aug +14 -0
- package/examples/visibility/counter.aug.md +87 -0
- package/examples/visibility/main.aug +7 -0
- package/examples/visibility/main.aug.md +39 -0
- package/package.json +50 -0
- package/runtime/aug_crypto.c +121 -0
- package/runtime/aug_html.c +93 -0
- package/runtime/aug_http.c +829 -0
- package/runtime/aug_json.c +165 -0
- package/runtime/aug_runtime.c +792 -0
- package/runtime/aug_runtime.h +245 -0
- package/runtime/aug_tasks.c +172 -0
- package/runtime/aug_time.c +5 -0
- package/runtime/aug_values.c +80 -0
- package/scripts/bootstrap-native.mjs +121 -0
- package/scripts/native-dependencies.lock.json +65 -0
- package/scripts/native-home.mjs +11 -0
- package/src/actions.js +109 -0
- package/src/ast.js +19 -0
- package/src/builtins.js +93 -0
- package/src/checker.js +2896 -0
- package/src/cli.js +344 -0
- package/src/codegen.js +1159 -0
- package/src/config.js +149 -0
- package/src/continuation.js +110 -0
- package/src/di.js +24 -0
- package/src/documentation.js +27 -0
- package/src/editor.js +782 -0
- package/src/effects.js +29 -0
- package/src/fixes.js +222 -0
- package/src/formatter.js +296 -0
- package/src/freshness.js +154 -0
- package/src/help.js +212 -0
- package/src/html.js +27 -0
- package/src/http-policies.js +90 -0
- package/src/inference.js +16 -0
- package/src/interceptors.js +70 -0
- package/src/javadoc.js +67 -0
- package/src/lexer.js +199 -0
- package/src/libraries.js +31 -0
- package/src/lsp.js +180 -0
- package/src/native.js +144 -0
- package/src/navigation.js +162 -0
- package/src/openapi.js +220 -0
- package/src/ownership.js +232 -0
- package/src/package-manager.js +299 -0
- package/src/parser.js +1155 -0
- package/src/policies.js +130 -0
- package/src/project-init.js +19 -0
- package/src/project.js +292 -0
- package/src/schemas.js +62 -0
- package/src/semantic.js +255 -0
- package/src/spec.js +738 -0
- package/src/testing.js +186 -0
- package/src/types.js +10 -0
package/src/editor.js
ADDED
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
2
|
+
import { fieldsOf, typeName } from "./ast.js";
|
|
3
|
+
import { tyName } from "./checker.js";
|
|
4
|
+
import { lex } from "./lexer.js";
|
|
5
|
+
import { isPrivateName } from "./project.js";
|
|
6
|
+
import { javadocBefore } from "./javadoc.js";
|
|
7
|
+
import { languageHelp } from "./help.js";
|
|
8
|
+
import { parse } from "./parser.js";
|
|
9
|
+
import { builtinFunctions, collectionOperations, operationType } from "./builtins.js";
|
|
10
|
+
import { callableDocumentation } from "./documentation.js";
|
|
11
|
+
import { interceptorBehavior } from "./interceptors.js";
|
|
12
|
+
import { httpPolicyNames, httpPolicyOptions, httpPolicyOptionHelp } from "./http-policies.js";
|
|
13
|
+
import { builtinTypes } from "./builtins.js";
|
|
14
|
+
import { libraryChild, libraryRelative } from "./libraries.js";
|
|
15
|
+
const builtins = [
|
|
16
|
+
...builtinFunctions.map(operation => {
|
|
17
|
+
const parameters = operation.parameters.map(param => `${param.label}=${param.type}`);
|
|
18
|
+
const signature = `${operation.name}(${parameters.join(', ')})`;
|
|
19
|
+
return { label: operation.name, kind: 'function', signature, parameters,
|
|
20
|
+
detail: `${signature} returns ${operation.returns}` + (operation.errors?.length ? ` unless ${operation.errors.join(' and ')}` : ''),
|
|
21
|
+
documentation: operation.documentation };
|
|
22
|
+
}),
|
|
23
|
+
...Object.entries(languageHelp).filter(([, help]) => help.category === 'type')
|
|
24
|
+
.map(([label, help]) => ({ label, kind: 'type', detail: help.detail, documentation: help.documentation })),
|
|
25
|
+
];
|
|
26
|
+
const keywords = [
|
|
27
|
+
{ label: 'initialize block', kind: 'snippet', detail: 'Constructor initialization inside a class or record', documentation: languageHelp.initialize.documentation,
|
|
28
|
+
insertText: 'initialize:\n $0' },
|
|
29
|
+
...Object.entries(languageHelp).filter(([label, help]) => help.category === 'keyword' &&
|
|
30
|
+
!['class', 'function', 'throws', 'bind'].includes(label))
|
|
31
|
+
.map(([label, help]) => ({ label, kind: 'keyword', detail: help.detail,
|
|
32
|
+
documentation: help.documentation })),
|
|
33
|
+
{ label: 'try/catch', kind: 'snippet', detail: 'Checked error handler',
|
|
34
|
+
documentation: languageHelp.try.documentation,
|
|
35
|
+
insertText: 'try {\n $1\n} catch ${2:Error} ${3:error} {\n $0\n}' },
|
|
36
|
+
{ label: 'borrow block', kind: 'snippet', detail: 'Mutable borrow',
|
|
37
|
+
documentation: languageHelp.borrow.documentation,
|
|
38
|
+
insertText: 'borrow ${1:value} {\n $0\n}' },
|
|
39
|
+
{ label: 'interceptor declaration', kind: 'snippet', detail: 'Generic interceptor wrapping any result type',
|
|
40
|
+
documentation: languageHelp.interceptor.documentation,
|
|
41
|
+
insertText: 'interceptor ${1:Audit}<T>() {\n around() returns T {\n T result to next()\n $0\n return result\n }\n}' },
|
|
42
|
+
{ label: 'test suite', kind: 'snippet', detail: 'Same-file class tests with fresh setup per case',
|
|
43
|
+
documentation: languageHelp.test.documentation,
|
|
44
|
+
insertText: 'test ${1:Worker} ${2:worker} {\n when ${3:ready} {\n ${2:worker} = ${1:Worker}()\n it ${4:works} {\n assert(${5:true})\n }\n }\n}' },
|
|
45
|
+
{ label: 'test group', kind: 'snippet', detail: 'Group setup and cases',
|
|
46
|
+
documentation: languageHelp.when.documentation,
|
|
47
|
+
insertText: 'when ${1:group} {\n $0\n it ${2:works} {\n assert(${3:true})\n }\n}' },
|
|
48
|
+
{ label: 'test case', kind: 'snippet', detail: 'Named test case', documentation: languageHelp.it.documentation,
|
|
49
|
+
insertText: 'it ${1:works} {\n assert(${2:true})\n}' },
|
|
50
|
+
{ label: 'endpoint declaration', kind: 'snippet', detail: 'Named HTTP endpoint with typed input', documentation: languageHelp.endpoint.documentation,
|
|
51
|
+
insertText: 'endpoint ${1:GET} "${2:/users/{id}}" as ${3:getUser}(int id from path) returns ${4:User}:\n $0' },
|
|
52
|
+
{ label: 'endpoint test', kind: 'snippet', detail: 'Exercise the HTTP pipeline beside its declaration', documentation: languageHelp.test.documentation,
|
|
53
|
+
insertText: 'test endpoint ${1:getUser} client:\n when requests:\n it succeeds:\n response = client.request(method="GET", path="${2:/users/1}")\n assert(condition=response.status == 200)\n' },
|
|
54
|
+
{ label: 'task scope', kind: 'snippet', detail: 'Joined child computations', documentation: languageHelp.scope.documentation,
|
|
55
|
+
insertText: 'scope:\n task = start ${1:load}()\n wait for task as result\n $0' },
|
|
56
|
+
];
|
|
57
|
+
function parameterText(param) {
|
|
58
|
+
return `${param.injected ? 'resolve ' : param.ownership === 'managed' ? '' : `${param.ownership} `}` +
|
|
59
|
+
`${typeName(param.type)} ${param.label ?? param.name}${param.label && param.label !== param.name ? ` to ${param.name}` : ''}` +
|
|
60
|
+
(param.source ? ` from ${param.source.kind}${param.source.name ? ' ' + JSON.stringify(param.source.name) : ''}` : '');
|
|
61
|
+
}
|
|
62
|
+
function signature(method, callSite = false, additionalErrors = []) {
|
|
63
|
+
const generic = method.typeParams.length ? `<${method.typeParams.join(', ')}>` : '';
|
|
64
|
+
const params = callSite ? method.params.filter(param => !param.injected)
|
|
65
|
+
.map(param => `${param.label ?? param.name}=${typeName(param.type)}`) : method.params.map(parameterText);
|
|
66
|
+
const errors = [...new Set([...method.throws.map(typeName), ...additionalErrors])];
|
|
67
|
+
return (method.endpoint ? `endpoint ${method.endpoint.method} ${JSON.stringify(method.endpoint.path)} as ` : '') + `${method.name}${generic}(${params.join(', ')}) ${method.endpoint?.streams ? 'streams' : 'returns'} ` +
|
|
68
|
+
`${method.returnOwnership === 'own' ? 'own ' : ''}${typeName(method.returns)}` +
|
|
69
|
+
(method.changes?.length ? ` changes ${method.changes.join(' and ')}` : '') +
|
|
70
|
+
(method.uses?.length ? ` uses ${method.uses.map(use => `${use.source}.${use.operation}`).join(' and ')}` : '') +
|
|
71
|
+
(errors.length ? ` unless ${errors.join(', ')}` : '') +
|
|
72
|
+
(method.endpoint && method.endpoint.status !== 200 ? ` with status ${method.endpoint.status}` : '');
|
|
73
|
+
}
|
|
74
|
+
function declarationDocumentation(checked, node) {
|
|
75
|
+
const first = 'annotations' in node ? node.annotations?.[0]?.span.start : undefined;
|
|
76
|
+
return documentation(checked, node.span.file, first ?? node.span.start) ??
|
|
77
|
+
documentation(checked, node.span.file, node.span.start);
|
|
78
|
+
}
|
|
79
|
+
function interceptorDescription(checked, node) {
|
|
80
|
+
const layers = checked.interceptorPlans.get(node) ?? [];
|
|
81
|
+
const policies = node.kind === 'function' ? checked.httpPolicies.get(node) ?? [] : [];
|
|
82
|
+
const policyText = policies.length ? '**HTTP policies before decoding**\n\n' + policies.map((policy, index) => `${index + 1}. \`${policy.name}\` ${JSON.stringify(policy.options)}; dependencies: ${policy.dependencies.map(index => node.kind === 'function' ? node.params[index].name : '').join(', ') || 'none'}.`).join('\n') : '';
|
|
83
|
+
const layerText = layers.length ? `Interceptor chain: ${layers.map(layer => `\`${layer.annotation.name}\``).join(' → ')}. ` +
|
|
84
|
+
'Calls enter in that order; results unwind in reverse.\n\n' + layers.map((layer, index) => {
|
|
85
|
+
const fields = layer.definition.node.kind === 'interceptor' ? layer.definition.node.fields : [];
|
|
86
|
+
const dependencies = [...new Set([...fields, ...layer.around.params].filter(param => param.injected).map(param => typeName(param.type)))];
|
|
87
|
+
const effects = checked.effectContracts.get(layer.around);
|
|
88
|
+
const uses = [...(effects?.uses.values() ?? [])].map(use => `${use.source}.${use.operation}`);
|
|
89
|
+
const behavior = interceptorBehavior(layer.around.body ?? []);
|
|
90
|
+
return `Layer ${index + 1} ${layer.definition.name}: dependencies ${dependencies.join(', ') || 'none'}; ` +
|
|
91
|
+
`changes ${effects?.changes.join(', ') || 'nothing'}; uses ${uses.join(', ') || 'nothing'}; ` +
|
|
92
|
+
`errors ${layer.errors.map(tyName).join(', ') || 'none'}. ${behavior.mayShortCircuit ? 'May short circuit; inspect its definition.' : 'Delegates directly.'}`;
|
|
93
|
+
}).join('\n\n') : '';
|
|
94
|
+
return [policyText, layerText].filter(Boolean).join('\n\n');
|
|
95
|
+
}
|
|
96
|
+
function documentation(checked, file, offset) {
|
|
97
|
+
return javadocBefore(checked.project.files.get(file)?.source ?? '', offset);
|
|
98
|
+
}
|
|
99
|
+
function interfaceMethodDocumentation(checked, file, interfaces, name, visited = new Set()) {
|
|
100
|
+
for (const reference of interfaces) {
|
|
101
|
+
const def = checked.project.scopes.get(file)?.get(reference.name);
|
|
102
|
+
if (!def || def.node.kind !== 'interface' || visited.has(def.id))
|
|
103
|
+
continue;
|
|
104
|
+
visited.add(def.id);
|
|
105
|
+
const method = def.node.methods.find(entry => entry.name === name);
|
|
106
|
+
const doc = method && declarationDocumentation(checked, method);
|
|
107
|
+
if (doc)
|
|
108
|
+
return doc;
|
|
109
|
+
const inherited = interfaceMethodDocumentation(checked, def.file, def.node.extends, name, visited);
|
|
110
|
+
if (inherited)
|
|
111
|
+
return inherited;
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
function methodDocumentation(checked, method, owner) {
|
|
116
|
+
return callableDocumentation(checked.project, method, owner);
|
|
117
|
+
}
|
|
118
|
+
function methodItem(checked, method, kind, owner) {
|
|
119
|
+
const errors = (checked.interceptorPlans.get(method) ?? []).flatMap(layer => layer.errors.map(tyName));
|
|
120
|
+
const label = signature(method, false, errors);
|
|
121
|
+
const injected = method.params.filter(param => param.injected);
|
|
122
|
+
const doc = methodDocumentation(checked, method, owner);
|
|
123
|
+
const injectionHelp = injected.length ? `Injected from bindings: ${injected.map(parameterText).join(', ')}.` : '';
|
|
124
|
+
const contract = checked.effectContracts.get(method);
|
|
125
|
+
const effects = contract ? `${contract.inferred ? 'Inferred capabilities; effective' : 'Effective'} contract: changes ${contract.changes.join(', ') || 'nothing'}; capabilities ` +
|
|
126
|
+
`${[...contract.uses.values()].map(effect => `${effect.source}.${effect.operation}`).join(', ') || 'none'}.` : '';
|
|
127
|
+
return { label: method.name, kind, detail: label, signature: signature(method, true, errors),
|
|
128
|
+
documentation: [doc?.markdown, isPrivateName(method.name) ? 'Private to its declaring type.' : '',
|
|
129
|
+
injectionHelp, effects, interceptorDescription(checked, method), 'Call arguments require labels; their order does not matter.']
|
|
130
|
+
.filter(Boolean).join('\n\n'),
|
|
131
|
+
parameters: method.params.filter(param => !param.injected)
|
|
132
|
+
.map(param => `${param.label ?? param.name}=${typeName(param.type)}`),
|
|
133
|
+
parameterDocumentation: method.params.filter(param => !param.injected)
|
|
134
|
+
.map(param => doc?.parameters.get(param.label ?? param.name)) };
|
|
135
|
+
}
|
|
136
|
+
function definitionItem(checked, def) {
|
|
137
|
+
const node = def.node;
|
|
138
|
+
if (node.kind === 'function')
|
|
139
|
+
return methodItem(checked, node, 'function');
|
|
140
|
+
if (node.kind === 'interface')
|
|
141
|
+
return { label: def.name, kind: 'interface',
|
|
142
|
+
detail: `interface ${def.name}${node.typeParams.length ? `<${node.typeParams.join(', ')}>` : ''}`,
|
|
143
|
+
documentation: documentation(checked, def.file, node.span.start)?.markdown };
|
|
144
|
+
if (node.kind === 'interceptor') {
|
|
145
|
+
const around = node.methods.find(method => method.name === 'around');
|
|
146
|
+
const params = around?.params.filter(param => !param.injected) ?? [];
|
|
147
|
+
const doc = around && methodDocumentation(checked, around, def);
|
|
148
|
+
return { label: def.name, kind: 'interceptor',
|
|
149
|
+
detail: `interceptor ${node.name}${node.typeParams.length ? `<${node.typeParams.join(', ')}>` : ''}` +
|
|
150
|
+
`(${node.fields.map(parameterText).join(', ')})` + (around ? `\n${signature(around)}` : ''),
|
|
151
|
+
signature: `${node.name}(${params.map(param => `${param.name}=targetLabel`).join(', ')})`,
|
|
152
|
+
parameters: params.map(param => `${param.name}=targetLabel`),
|
|
153
|
+
parameterDocumentation: params.map(param => [doc?.parameters.get(param.name),
|
|
154
|
+
`Map a target parameter of type ${typeName(param.type)} to ${param.name}. Same-name mappings are automatic.`]
|
|
155
|
+
.filter(Boolean).join('\n\n')),
|
|
156
|
+
documentation: [declarationDocumentation(checked, node)?.markdown, doc?.markdown,
|
|
157
|
+
'Apply with `[Name]` or `[Name(interceptorLabel=targetLabel)]`. A fresh instance is created for each invocation. ' +
|
|
158
|
+
'Generic types are inferred from the target. Constructor parameters come from DI. ' +
|
|
159
|
+
'The around body can call next at most once per execution path.'].filter(Boolean).join('\n\n') };
|
|
160
|
+
}
|
|
161
|
+
if (node.kind === 'composition')
|
|
162
|
+
return { label: def.name, kind: 'function', detail: `composition ${def.name}`,
|
|
163
|
+
documentation: node.bindings.map(binding => `implement ${binding.key} with ${typeName(binding.target)}`).join('\n') };
|
|
164
|
+
const params = node.fields.map(parameterText);
|
|
165
|
+
const label = `${def.name}${node.typeParams.length ? `<${node.typeParams.join(', ')}>` : ''}(${params.join(', ')})`;
|
|
166
|
+
const doc = declarationDocumentation(checked, node);
|
|
167
|
+
const explicit = node.fields.filter(field => !field.injected);
|
|
168
|
+
const callSignature = `${def.name}${node.typeParams.length ? `<${node.typeParams.join(', ')}>` : ''}` +
|
|
169
|
+
`(${explicit.map(field => `${field.label ?? field.name}=${typeName(field.type)}`).join(', ')})`;
|
|
170
|
+
const errors = [...new Set((checked.interceptorPlans.get(node) ?? []).flatMap(layer => layer.errors.map(tyName)))];
|
|
171
|
+
return { label: def.name, kind: 'class', detail: `${node.record ? 'record ' : ''}${label}` +
|
|
172
|
+
(node.implements.length ? ` implements ${node.implements.map(typeName).join(' and ')}` : '') +
|
|
173
|
+
(errors.length ? ` unless ${errors.join(', ')}` : ''),
|
|
174
|
+
signature: callSignature + (errors.length ? ` unless ${errors.join(', ')}` : ''), parameters: explicit.map(field => `${field.label ?? field.name}=${typeName(field.type)}`),
|
|
175
|
+
documentation: [doc?.markdown,
|
|
176
|
+
node.fields.some(field => field.injected) ? `Injected from bindings: ${node.fields.filter(field => field.injected).map(parameterText).join(', ')}.` : '',
|
|
177
|
+
interceptorDescription(checked, node),
|
|
178
|
+
'Constructor arguments require labels; their order does not matter.'].filter(Boolean).join('\n\n'),
|
|
179
|
+
parameterDocumentation: explicit.map(field => doc?.parameters.get(field.label ?? field.name)) };
|
|
180
|
+
}
|
|
181
|
+
function typeFromRef(checked, file, ref) {
|
|
182
|
+
const def = checked.project.scopes.get(file)?.get(ref.name);
|
|
183
|
+
return { id: def?.id ?? `builtin:${ref.name}`, name: ref.name,
|
|
184
|
+
kind: def?.node.kind === 'class' || def?.node.kind === 'interface' || def?.node.kind === 'interceptor' ? def.node.kind : 'builtin',
|
|
185
|
+
args: ref.args.map(arg => typeFromRef(checked, file, arg)), nullable: ref.nullable, def };
|
|
186
|
+
}
|
|
187
|
+
function collectLocals(checked, file, offset) {
|
|
188
|
+
const scope = [...checked.scopes.values()].filter(scope => scope.span.file === file.path &&
|
|
189
|
+
scope.span.start <= offset && offset <= scope.span.end).sort((left, right) => left.span.end - left.span.start - (right.span.end - right.span.start) || right.span.start - left.span.start)[0];
|
|
190
|
+
return new Map(scope?.locals.map(local => [local.name, { type: local.type, typeText: tyName(local.type),
|
|
191
|
+
kind: local.kind, documentation: [local.documentation, local.moved ? 'This owned value has moved.' : '',
|
|
192
|
+
local.type.readonly ? 'Read-only access.' : ''].filter(Boolean).join('\n\n') }]) ?? []);
|
|
193
|
+
}
|
|
194
|
+
function memberItems(checked, file, receiver, insideClass, seen = new Set()) {
|
|
195
|
+
if (!receiver)
|
|
196
|
+
return [];
|
|
197
|
+
if (collectionOperations[receiver.name] && receiver.id.startsWith('builtin:'))
|
|
198
|
+
return collectionOperations[receiver.name].map(operation => {
|
|
199
|
+
const parameters = operation.parameters.map(param => `${param.label}=${tyName(operationType(param.type, receiver))}`);
|
|
200
|
+
const signature = `${operation.name}(${parameters.join(', ')})`;
|
|
201
|
+
return { label: operation.name, kind: 'method', parameters, signature,
|
|
202
|
+
detail: `${signature} returns ${operation.returns === 'position' ? 'the selected tuple position' : tyName(operationType(operation.returns, receiver))}` +
|
|
203
|
+
(operation.changes ? ' changes self; requires borrow' : '') + (operation.errors?.length ? ` unless ${operation.errors.join(' and ')}` : ''),
|
|
204
|
+
documentation: operation.documentation };
|
|
205
|
+
});
|
|
206
|
+
const def = receiver.def;
|
|
207
|
+
if (!def || seen.has(def.id))
|
|
208
|
+
return [];
|
|
209
|
+
seen.add(def.id);
|
|
210
|
+
const node = def.node;
|
|
211
|
+
if (node.kind === 'class' || node.kind === 'interceptor') {
|
|
212
|
+
const methods = [...node.methods.filter(method => insideClass || !isPrivateName(method.name))
|
|
213
|
+
.map(method => methodItem(checked, method, 'method', def))];
|
|
214
|
+
for (const entry of checked.defaults.get(def.id)?.values() ?? [])
|
|
215
|
+
if ((insideClass || !isPrivateName(entry.method.name)) &&
|
|
216
|
+
!methods.some(item => item.label === entry.method.name))
|
|
217
|
+
methods.push(methodItem(checked, entry.method, 'method'));
|
|
218
|
+
for (const field of fieldsOf(node))
|
|
219
|
+
if (insideClass || !isPrivateName(field.name))
|
|
220
|
+
methods.push({ label: field.name,
|
|
221
|
+
kind: 'property', detail: `${typeName(field.type)} ${field.name}` +
|
|
222
|
+
(isPrivateName(field.name) ? ' (private)' : ''),
|
|
223
|
+
documentation: declarationDocumentation(checked, node)?.parameters.get(field.name) });
|
|
224
|
+
return methods;
|
|
225
|
+
}
|
|
226
|
+
if (node.kind === 'interface') {
|
|
227
|
+
const methods = node.methods.filter(method => insideClass || !isPrivateName(method.name))
|
|
228
|
+
.map(method => methodItem(checked, method, 'method', def));
|
|
229
|
+
for (const parent of node.extends) {
|
|
230
|
+
const type = typeFromRef(checked, def.file, parent);
|
|
231
|
+
for (const item of memberItems(checked, file, type, false, seen))
|
|
232
|
+
if (!methods.some(existing => existing.label === item.label))
|
|
233
|
+
methods.push(item);
|
|
234
|
+
}
|
|
235
|
+
return methods;
|
|
236
|
+
}
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
function unique(items) {
|
|
240
|
+
return [...new Map(items.map(item => [item.label, item])).values()];
|
|
241
|
+
}
|
|
242
|
+
function nextItem(checked, file, offset) {
|
|
243
|
+
const owner = file.items.find((item) => item.kind === 'interceptor' &&
|
|
244
|
+
item.span.start <= offset && offset <= item.span.end);
|
|
245
|
+
const around = owner?.methods.find(method => method.name === 'around' &&
|
|
246
|
+
method.span.start <= offset && offset <= method.span.end);
|
|
247
|
+
if (!around)
|
|
248
|
+
return undefined;
|
|
249
|
+
const params = around.params.filter(param => !param.injected);
|
|
250
|
+
const doc = methodDocumentation(checked, around);
|
|
251
|
+
return { label: 'next', kind: 'function',
|
|
252
|
+
detail: `next(${params.map(param => `${param.name}=${typeName(param.type)}`).join(', ')}) returns ` +
|
|
253
|
+
`${around.returnOwnership === 'own' ? 'own ' : ''}${typeName(around.returns)} (overrides optional)`,
|
|
254
|
+
signature: `next(${params.map(param => `${param.name}=${typeName(param.type)}`).join(', ')}) returns ${typeName(around.returns)}`,
|
|
255
|
+
parameters: params.map(param => `${param.name}=${typeName(param.type)}`),
|
|
256
|
+
parameterDocumentation: params.map(param => [doc?.parameters.get(param.name),
|
|
257
|
+
'Optional override of the mapped target argument. Omit to forward the original value.'].filter(Boolean).join('\n\n')),
|
|
258
|
+
documentation: languageHelp.next.documentation };
|
|
259
|
+
}
|
|
260
|
+
function annotationTarget(file, open) {
|
|
261
|
+
for (const node of file.items) {
|
|
262
|
+
const candidates = node.kind === 'class' || node.kind === 'function' ? [node] : [];
|
|
263
|
+
if ('methods' in node)
|
|
264
|
+
candidates.push(...node.methods);
|
|
265
|
+
const target = candidates.find(candidate => candidate.annotations?.some(tag => tag.span.start === open));
|
|
266
|
+
if (target)
|
|
267
|
+
return target;
|
|
268
|
+
}
|
|
269
|
+
// An unfinished mapping can prevent the annotation from parsing. The target
|
|
270
|
+
// header that follows it still provides useful labels while the user edits.
|
|
271
|
+
const close = file.source.indexOf(']', open);
|
|
272
|
+
if (close < 0)
|
|
273
|
+
return undefined;
|
|
274
|
+
return parse(file.path.replace(/main\.aug$/, 'editor.aug'), file.source.slice(close + 1)).file.items
|
|
275
|
+
.find((node) => node.kind === 'function' || node.kind === 'class');
|
|
276
|
+
}
|
|
277
|
+
export function importItems(checked, file) {
|
|
278
|
+
const project = checked.project;
|
|
279
|
+
const items = [];
|
|
280
|
+
const currentFolder = dirname(file.path);
|
|
281
|
+
const owner = file.package ? project.packages.scopes.get(file.package) : undefined;
|
|
282
|
+
const aliases = owner ? new Map(Object.entries(owner.dependencies).map(([alias, path]) => [alias, project.packages.scopes.get(path)])) : project.packages.roots;
|
|
283
|
+
for (const sibling of project.files.values()) {
|
|
284
|
+
if (dirname(sibling.path) !== currentFolder || sibling.path === file.path ||
|
|
285
|
+
sibling.path.endsWith(`${sep}export.aug`) ||
|
|
286
|
+
isPrivateName(sibling.path.slice(currentFolder.length + 1, -4)))
|
|
287
|
+
continue;
|
|
288
|
+
const from = sibling.path.slice(currentFolder.length + 1, -4);
|
|
289
|
+
for (const item of sibling.items) {
|
|
290
|
+
if (item.kind !== 'class' && item.kind !== 'interface' && item.kind !== 'function' && item.kind !== 'interceptor')
|
|
291
|
+
continue;
|
|
292
|
+
if (isPrivateName(item.name))
|
|
293
|
+
continue;
|
|
294
|
+
items.push({ label: item.name, kind: 'snippet',
|
|
295
|
+
detail: `import ${item.name} from ${from}`, insertText: `${item.name} from ${from};`,
|
|
296
|
+
documentation: item.kind === 'interface' ? documentation(checked, sibling.path, item.span.start)?.markdown :
|
|
297
|
+
declarationDocumentation(checked, item)?.markdown });
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
for (const exportFile of project.files.values()) {
|
|
301
|
+
if (!exportFile.path.endsWith(`${sep}export.aug`))
|
|
302
|
+
continue;
|
|
303
|
+
if (owner && !exportFile.package && !exportFile.builtin)
|
|
304
|
+
continue;
|
|
305
|
+
const folder = dirname(exportFile.path);
|
|
306
|
+
if (folder === currentFolder)
|
|
307
|
+
continue;
|
|
308
|
+
const standard = exportFile.builtin && project.stdlibRoot;
|
|
309
|
+
const scope = exportFile.package ? project.packages.scopes.get(exportFile.package) : undefined;
|
|
310
|
+
const root = scope?.sourceRoot ?? project.sourceRoot;
|
|
311
|
+
const prefixes = standard ? [['august']] : scope && scope !== owner ?
|
|
312
|
+
[...aliases].filter(([, target]) => target === scope).map(([alias]) => [alias]) : [[]];
|
|
313
|
+
if (!prefixes.length)
|
|
314
|
+
continue;
|
|
315
|
+
const relativeFolder = standard ? libraryRelative(project.libraries, folder) : relative(root, folder);
|
|
316
|
+
if (relativeFolder.startsWith('..'))
|
|
317
|
+
continue;
|
|
318
|
+
const segments = relativeFolder.split(sep).filter(Boolean);
|
|
319
|
+
if (segments.some(isPrivateName))
|
|
320
|
+
continue;
|
|
321
|
+
let exposed = true;
|
|
322
|
+
for (let index = scope && scope !== owner ? 0 : 1; index < segments.length; index++) {
|
|
323
|
+
let parentFolder = standard || root;
|
|
324
|
+
for (const segment of segments.slice(0, index))
|
|
325
|
+
parentFolder = standard ?
|
|
326
|
+
libraryChild(project.libraries, parentFolder, segment) : join(parentFolder, segment);
|
|
327
|
+
const parent = join(parentFolder, 'export.aug');
|
|
328
|
+
if (!project.files.get(parent)?.items.some(item => item.kind === 'export' &&
|
|
329
|
+
item.folder && item.name === segments[index])) {
|
|
330
|
+
exposed = false;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (!exposed)
|
|
335
|
+
continue;
|
|
336
|
+
for (const prefix of prefixes)
|
|
337
|
+
for (const item of exportFile.items) {
|
|
338
|
+
const from = [...prefix, ...segments].join('.');
|
|
339
|
+
if (!from)
|
|
340
|
+
continue;
|
|
341
|
+
if (item.kind !== 'export' || item.folder)
|
|
342
|
+
continue;
|
|
343
|
+
if (isPrivateName(item.name) || (item.from && isPrivateName(item.from)))
|
|
344
|
+
continue;
|
|
345
|
+
const exported = project.scopes.get(join(folder, `${item.from}.aug`))?.get(item.name);
|
|
346
|
+
items.push({ label: item.name, kind: 'snippet',
|
|
347
|
+
detail: `import ${item.name} from ${from}`, insertText: `${item.name} from ${from};`,
|
|
348
|
+
documentation: exported && definitionItem(checked, exported).documentation });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return items;
|
|
352
|
+
}
|
|
353
|
+
export function completions(checked, fileName, offset) {
|
|
354
|
+
const file = checked.project.files.get(resolve(fileName));
|
|
355
|
+
if (!file)
|
|
356
|
+
return [];
|
|
357
|
+
const prefix = file.source.slice(0, offset);
|
|
358
|
+
const line = prefix.slice(prefix.lastIndexOf('\n') + 1);
|
|
359
|
+
const imports = importItems(checked, file);
|
|
360
|
+
if (/^\s*import\s+[A-Za-z_0-9]*$/.test(line))
|
|
361
|
+
return imports;
|
|
362
|
+
const joined = /^\s*import\s+(.+?)\s+and\s+[A-Za-z_0-9]*$/.exec(line);
|
|
363
|
+
if (joined) {
|
|
364
|
+
const used = new Set(joined[1].split(/\s+and\s+/));
|
|
365
|
+
return unique(imports.filter(item => !used.has(item.label)).map(item => ({ ...item, kind: 'snippet', insertText: item.label })));
|
|
366
|
+
}
|
|
367
|
+
const from = /^\s*import\s+(.+?)\s+from\s+[A-Za-z_0-9.]*$/.exec(line);
|
|
368
|
+
if (from)
|
|
369
|
+
return unique(imports.filter(item => from[1] === 'everything' || item.label === from[1].split(/\s+and\s+/)[0]).map(item => {
|
|
370
|
+
const path = item.detail.slice(item.detail.lastIndexOf(' from ') + 6);
|
|
371
|
+
return { label: path, kind: 'snippet', detail: item.detail,
|
|
372
|
+
insertText: `${path};`, documentation: item.documentation };
|
|
373
|
+
}));
|
|
374
|
+
const annotation = /(?<=^|\n)[ \t]*\[\s*([A-Za-z_][A-Za-z0-9_]*|)(?:<[^\[\]()]*>)?(?:\(([^\n()]*))?$/.exec(prefix);
|
|
375
|
+
if (annotation) {
|
|
376
|
+
const definitions = [...(checked.project.scopes.get(file.path)?.values() ?? [])]
|
|
377
|
+
.filter(def => def.node.kind === 'interceptor');
|
|
378
|
+
if (annotation[2] === undefined)
|
|
379
|
+
return [...definitions.map(def => definitionItem(checked, def)), ...httpPolicyNames.map(label => ({ label, kind: 'interceptor', detail: languageHelp[label]?.detail ?? label, documentation: languageHelp[label]?.documentation }))];
|
|
380
|
+
const def = definitions.find(def => def.name === annotation[1]);
|
|
381
|
+
const around = def?.node.kind === 'interceptor' ? def.node.methods.find(method => method.name === 'around') : undefined;
|
|
382
|
+
const part = annotation[2].split(',').at(-1) ?? '';
|
|
383
|
+
if (httpPolicyNames.includes(annotation[1])) {
|
|
384
|
+
const specs = httpPolicyOptions[annotation[1]];
|
|
385
|
+
const selected = /^\s*(\w+)\s*(?:=|to\b)\s*(.*)$/.exec(part);
|
|
386
|
+
if (selected) {
|
|
387
|
+
if (!specs[selected[1]]?.startsWith('dependency:'))
|
|
388
|
+
return [];
|
|
389
|
+
const target = annotationTarget(file, prefix.indexOf('[', annotation.index));
|
|
390
|
+
const required = specs[selected[1]].slice(11).split('.')[0];
|
|
391
|
+
return (target?.kind === 'function' ? target.params : []).filter(param => param.injected && param.type.name === required)
|
|
392
|
+
.map(param => ({ label: param.name, kind: 'parameter', detail: parameterText(param), documentation: httpPolicyOptionHelp[selected[1]] }));
|
|
393
|
+
}
|
|
394
|
+
const used = new Set([...annotation[2].matchAll(/\b(\w+)\s*(?:=|to\b)/g)].map(match => match[1]));
|
|
395
|
+
return Object.entries(specs).filter(([name]) => !used.has(name)).map(([name, type]) => ({
|
|
396
|
+
label: name, kind: 'snippet', detail: `${annotation[1]}.${name}: ${type}`, insertText: `${name}=`, documentation: httpPolicyOptionHelp[name],
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
399
|
+
if (/(?:=|\bto\b)\s*[A-Za-z_0-9]*$/.test(part)) {
|
|
400
|
+
const target = annotationTarget(file, prefix.indexOf('[', annotation.index));
|
|
401
|
+
const params = target?.kind === 'class' ? target.fields : target?.params ?? [];
|
|
402
|
+
return params.map(param => ({ label: param.name, kind: 'parameter', detail: parameterText(param),
|
|
403
|
+
documentation: `Supply target argument ${param.name} to the selected interceptor parameter.` }));
|
|
404
|
+
}
|
|
405
|
+
const used = new Set([...annotation[2].matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|to\b)/g)].map(match => match[1]));
|
|
406
|
+
const doc = around && methodDocumentation(checked, around, def);
|
|
407
|
+
return (around?.params ?? []).filter(param => !param.injected && !used.has(param.name)).map(param => ({
|
|
408
|
+
label: param.name, kind: 'snippet', detail: `map ${parameterText(param)} to a target label`,
|
|
409
|
+
insertText: `${param.name}=`, documentation: doc?.parameters.get(param.name)
|
|
410
|
+
}));
|
|
411
|
+
}
|
|
412
|
+
const locals = collectLocals(checked, file, offset);
|
|
413
|
+
const next = nextItem(checked, file, offset);
|
|
414
|
+
const call = /(?:^|[^A-Za-z0-9_])([A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?)(?:<[^()]*>)?\(([^()]*)$/.exec(prefix);
|
|
415
|
+
if (call && /^\s*[A-Za-z_0-9]*$/.test(call[2].split(',').at(-1) ?? '')) {
|
|
416
|
+
const parts = call[1].split('.');
|
|
417
|
+
const entry = parts.length === 2 ? memberItems(checked, file.path, locals.get(parts[0])?.type, false).find(item => item.label === parts[1]) :
|
|
418
|
+
[...(checked.project.scopes.get(file.path)?.values() ?? [])]
|
|
419
|
+
.map(def => definitionItem(checked, def)).find(item => item.label === parts[0]) ??
|
|
420
|
+
(next?.label === parts[0] ? next : builtins.find(item => item.label === parts[0]));
|
|
421
|
+
if (entry?.parameters?.length) {
|
|
422
|
+
const used = new Set([...call[2].matchAll(/\b([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|to\b)/g)]
|
|
423
|
+
.map(match => match[1]));
|
|
424
|
+
const labels = entry.parameters.map((parameter, index) => ({ parameter, index,
|
|
425
|
+
name: parameter.split('=')[0] })).filter(item => !used.has(item.name));
|
|
426
|
+
if (labels.length)
|
|
427
|
+
return labels.map(({ parameter, index, name }) => ({
|
|
428
|
+
label: name, kind: 'snippet', detail: `argument ${parameter}`,
|
|
429
|
+
insertText: `${name}=`, documentation: entry.parameterDocumentation?.[index]
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const member = /\b([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*[A-Za-z_0-9]*(?:<[^()]*>)?$/.exec(prefix);
|
|
434
|
+
if (member) {
|
|
435
|
+
const local = locals.get(member[1]);
|
|
436
|
+
const receiver = local?.type;
|
|
437
|
+
const owner = file.items.find(item => (item.kind === 'class' || item.kind === 'interface' || item.kind === 'interceptor') &&
|
|
438
|
+
item.span.start <= offset && offset <= item.span.end);
|
|
439
|
+
const insideClass = !!owner && receiver?.def?.node === owner;
|
|
440
|
+
return unique(memberItems(checked, file.path, receiver, insideClass));
|
|
441
|
+
}
|
|
442
|
+
if (/\bresolve\s+[A-Za-z_0-9]*$/.test(prefix))
|
|
443
|
+
return checked.bindings
|
|
444
|
+
.filter(binding => file.path === checked.project.main?.path || !isPrivateName(binding.declaration.key))
|
|
445
|
+
.map(binding => ({
|
|
446
|
+
label: binding.key, kind: 'variable', detail: `binding ${binding.key}: ${tyName(binding.exposedType)}`,
|
|
447
|
+
documentation: binding.exposedType.def && definitionItem(checked, binding.exposedType.def).documentation
|
|
448
|
+
}));
|
|
449
|
+
const definitions = [...(checked.project.scopes.get(file.path)?.values() ?? [])]
|
|
450
|
+
.map(def => definitionItem(checked, def));
|
|
451
|
+
const localItems = [...locals].map(([label, local]) => ({
|
|
452
|
+
label, kind: local.kind, detail: `${local.typeText} ${label}`,
|
|
453
|
+
documentation: local.documentation
|
|
454
|
+
}));
|
|
455
|
+
const inTest = file.items.some(item => item.kind === 'test' && item.span.start <= offset && offset <= item.span.end);
|
|
456
|
+
const all = unique([...definitions, ...localItems, ...(next ? [next] : []),
|
|
457
|
+
...builtins.filter(item => item.label !== 'assert' || inTest), ...keywords]);
|
|
458
|
+
if (/\bresolve\s+[A-Za-z_][A-Za-z_0-9]*(?:<[^>]*>)?\s+to\s+[A-Za-z_0-9]*$/.test(prefix))
|
|
459
|
+
return localItems;
|
|
460
|
+
if (/\b(?:implement|bind)\s+[A-Za-z_][A-Za-z_0-9]*(?:<[^>]*>)?\s+(?:with|to)\s+[A-Za-z_0-9]*$/.test(prefix))
|
|
461
|
+
return all.filter(item => item.kind === 'class');
|
|
462
|
+
if (/\b(?:implements|extends)\s+[A-Za-z_0-9]*$/.test(prefix))
|
|
463
|
+
return all.filter(item => item.kind === 'interface');
|
|
464
|
+
if (/\b(?:returns|unless|catch)\s+[A-Za-z_0-9]*$/.test(prefix))
|
|
465
|
+
return all.filter(item => ['class', 'interface', 'type'].includes(item.kind));
|
|
466
|
+
return all;
|
|
467
|
+
}
|
|
468
|
+
/** Resolve exactly the token under the cursor, including syntax outside completion contexts. */
|
|
469
|
+
export function hoverInfo(checked, fileName, offset) {
|
|
470
|
+
const file = checked.project.files.get(resolve(fileName));
|
|
471
|
+
if (!file || offset < 0 || offset > file.source.length)
|
|
472
|
+
return undefined;
|
|
473
|
+
const open = file.source.lastIndexOf('/**', offset);
|
|
474
|
+
const close = open < 0 ? -1 : file.source.indexOf('*/', open + 3);
|
|
475
|
+
if (open >= 0 && (close < 0 || offset < close + 2)) {
|
|
476
|
+
const tags = /@[A-Za-z]+/g;
|
|
477
|
+
for (const match of file.source.slice(open, close < 0 ? undefined : close + 2).matchAll(tags)) {
|
|
478
|
+
const start = open + (match.index ?? 0);
|
|
479
|
+
const help = languageHelp[match[0]];
|
|
480
|
+
if (help && start <= offset && offset < start + match[0].length)
|
|
481
|
+
return { label: match[0], kind: 'keyword', detail: help.detail,
|
|
482
|
+
documentation: help.documentation, start, end: start + match[0].length };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
const token = lex(file.path, file.source).tokens.find(entry => entry.kind !== 'eof' && entry.span.start <= offset && offset < entry.span.end);
|
|
486
|
+
if (!token)
|
|
487
|
+
return undefined;
|
|
488
|
+
const { start, end } = token.span;
|
|
489
|
+
if (['import', 'everything', 'and', 'from'].includes(token.kind)) {
|
|
490
|
+
const declaration = file.items.find(item => item.kind === 'import' && item.span.start <= start && end <= item.span.end);
|
|
491
|
+
if (declaration?.kind === 'import') {
|
|
492
|
+
const imports = checked.project.imports.get(declaration) ?? [];
|
|
493
|
+
const help = languageHelp[token.value];
|
|
494
|
+
return { label: token.value, kind: 'keyword', detail: help.detail,
|
|
495
|
+
documentation: [help.documentation, imports.length ? '**Resolved imports**\n' +
|
|
496
|
+
imports.map(def => `- \`${def.name}\` from \`${relative(checked.project.root, def.file)}\``).join('\n') : 'No declarations resolved yet.'].join('\n\n'),
|
|
497
|
+
start, end };
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (['[', ']', '{', '}', '(', ')', ':', ','].includes(token.kind)) {
|
|
501
|
+
let literal;
|
|
502
|
+
const find = (value) => {
|
|
503
|
+
if (!value || typeof value !== 'object')
|
|
504
|
+
return;
|
|
505
|
+
if (Array.isArray(value)) {
|
|
506
|
+
value.forEach(find);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const expr = value;
|
|
510
|
+
if (expr.kind === 'collection' && expr.span.start <= start && end <= expr.span.end &&
|
|
511
|
+
(!literal || expr.span.end - expr.span.start < literal.span.end - literal.span.start))
|
|
512
|
+
literal = expr;
|
|
513
|
+
for (const [key, child] of Object.entries(value))
|
|
514
|
+
if (key !== 'span')
|
|
515
|
+
find(child);
|
|
516
|
+
};
|
|
517
|
+
find(file.items);
|
|
518
|
+
if (literal) {
|
|
519
|
+
const type = checked.expressionTypes.get(literal);
|
|
520
|
+
if (type)
|
|
521
|
+
return { label: token.value, kind: 'type', detail: `${tyName(type)} literal`,
|
|
522
|
+
documentation: languageHelp[type.name]?.documentation, start, end };
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (token.value === 'next') {
|
|
526
|
+
const next = nextItem(checked, file, start);
|
|
527
|
+
if (next)
|
|
528
|
+
return { ...next, start, end };
|
|
529
|
+
}
|
|
530
|
+
for (const node of file.items) {
|
|
531
|
+
const candidates = node.kind === 'class' || node.kind === 'function' ? [node] : [];
|
|
532
|
+
if ('methods' in node)
|
|
533
|
+
candidates.push(...node.methods);
|
|
534
|
+
for (const candidate of candidates)
|
|
535
|
+
for (const tag of candidate.annotations ?? []) {
|
|
536
|
+
if (start < tag.span.start || end > tag.span.end)
|
|
537
|
+
continue;
|
|
538
|
+
const def = checked.project.scopes.get(file.path)?.get(tag.name);
|
|
539
|
+
const around = def?.node.kind === 'interceptor' ? def.node.methods.find(method => method.name === 'around') : undefined;
|
|
540
|
+
const params = candidate.kind === 'class' ? candidate.fields : candidate.params;
|
|
541
|
+
const mapping = tag.mappings.find(entry => entry.span.start <= start && end <= entry.span.end);
|
|
542
|
+
if (httpPolicyNames.includes(tag.name)) {
|
|
543
|
+
if (start === tag.nameSpan.start)
|
|
544
|
+
return { label: tag.name, kind: 'interceptor', detail: languageHelp[tag.name].detail, documentation: languageHelp[tag.name].documentation, start, end };
|
|
545
|
+
if (mapping && start < mapping.sourceSpan.start)
|
|
546
|
+
return { label: mapping.name, kind: 'property', detail: `${tag.name}.${mapping.name}`, documentation: httpPolicyOptionHelp[mapping.name], start, end };
|
|
547
|
+
}
|
|
548
|
+
if (mapping) {
|
|
549
|
+
const source = mapping.sourceSpan.start <= start;
|
|
550
|
+
const param = source ? params.find(param => param.name === mapping.source) :
|
|
551
|
+
around?.params.find(param => param.name === mapping.name);
|
|
552
|
+
if (param)
|
|
553
|
+
return { label: token.value, kind: 'parameter',
|
|
554
|
+
detail: `${parameterText(param)} — ${mapping.source} → ${tag.name}.${mapping.name}`,
|
|
555
|
+
documentation: source ? declarationDocumentation(checked, candidate)?.parameters.get(param.name) :
|
|
556
|
+
around && methodDocumentation(checked, around, def)?.parameters.get(param.name), start, end };
|
|
557
|
+
}
|
|
558
|
+
if (def && start === tag.nameSpan.start) {
|
|
559
|
+
const item = definitionItem(checked, def);
|
|
560
|
+
const layer = checked.interceptorPlans.get(candidate)?.find(layer => layer.annotation === tag);
|
|
561
|
+
return { ...item, documentation: [item.documentation,
|
|
562
|
+
layer?.types.size ? `Inferred types: ${[...layer.types].map(([name, type]) => `\`${name} = ${tyName(type)}\``).join(', ')}.` : '',
|
|
563
|
+
`Wraps ${candidate.kind === 'class' ? 'construction of' : 'calls to'} \`${candidate.name}\`.`]
|
|
564
|
+
.filter(Boolean).join('\n\n'), start, end };
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (token.kind === 'number' || token.kind === 'string') {
|
|
569
|
+
const type = token.kind === 'string' ? 'string' : token.value.includes('.') ? 'float' : 'int';
|
|
570
|
+
return { label: token.value, kind: 'type', detail: `${type} literal`,
|
|
571
|
+
documentation: languageHelp[type].documentation, start, end };
|
|
572
|
+
}
|
|
573
|
+
const entryOwner = token.value === 'around' ? file.items.find((item) => item.kind === 'interceptor' && item.span.start <= start && end <= item.span.end) : undefined;
|
|
574
|
+
const around = entryOwner?.methods.find(method => method.name === 'around' && method.span.start === start);
|
|
575
|
+
if (around) {
|
|
576
|
+
const item = methodItem(checked, around, 'method');
|
|
577
|
+
return { ...item, documentation: [item.documentation, languageHelp.around.documentation].filter(Boolean).join('\n\n'), start, end };
|
|
578
|
+
}
|
|
579
|
+
const help = languageHelp[token.value];
|
|
580
|
+
if (help)
|
|
581
|
+
return { label: token.value,
|
|
582
|
+
kind: help.category === 'type' ? 'type' : help.category === 'function' ? 'function' : 'keyword',
|
|
583
|
+
detail: help.detail, documentation: help.documentation, start, end };
|
|
584
|
+
if (token.kind !== 'identifier')
|
|
585
|
+
return undefined;
|
|
586
|
+
const local = collectLocals(checked, file, start).get(token.value);
|
|
587
|
+
let item = local ? { label: token.value, kind: local.kind,
|
|
588
|
+
detail: `${local.typeText} ${token.value}` + (local.kind === 'property' && isPrivateName(token.value) ? ' (private)' : ''),
|
|
589
|
+
documentation: local.documentation } : undefined;
|
|
590
|
+
item ??= completions(checked, file.path, end).find(entry => entry.label === token.value);
|
|
591
|
+
if (!item) {
|
|
592
|
+
const def = checked.project.scopes.get(file.path)?.get(token.value);
|
|
593
|
+
if (def)
|
|
594
|
+
item = definitionItem(checked, def);
|
|
595
|
+
}
|
|
596
|
+
if (!item) {
|
|
597
|
+
const owner = file.items.find(entry => (entry.kind === 'class' || entry.kind === 'interface' || entry.kind === 'interceptor') &&
|
|
598
|
+
entry.span.start <= start && end <= entry.span.end);
|
|
599
|
+
if (owner?.kind === 'class' || owner?.kind === 'interface' || owner?.kind === 'interceptor') {
|
|
600
|
+
const method = owner.methods.find(entry => entry.name === token.value &&
|
|
601
|
+
entry.span.start === start);
|
|
602
|
+
if (method) {
|
|
603
|
+
const def = [...checked.project.definitions.values()].find(entry => entry.node === owner);
|
|
604
|
+
item = methodItem(checked, method, 'method', def);
|
|
605
|
+
}
|
|
606
|
+
if (!item && (owner.kind === 'class' || owner.kind === 'interceptor')) {
|
|
607
|
+
const field = fieldsOf(owner).find(entry => entry.name === token.value &&
|
|
608
|
+
entry.span.start <= start && end <= entry.span.end);
|
|
609
|
+
if (field)
|
|
610
|
+
item = { label: field.name, kind: 'property',
|
|
611
|
+
detail: `${typeName(field.type)} ${field.name}` +
|
|
612
|
+
(isPrivateName(field.name) ? ' (private)' : ''),
|
|
613
|
+
documentation: declarationDocumentation(checked, owner)?.parameters.get(field.name) };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (!item) {
|
|
618
|
+
const exported = file.items.find(entry => entry.kind === 'export' && !entry.folder &&
|
|
619
|
+
entry.name === token.value && entry.span.start <= start && end <= entry.span.end);
|
|
620
|
+
if (exported?.kind === 'export' && exported.from) {
|
|
621
|
+
const def = checked.project.scopes.get(join(dirname(file.path), `${exported.from}.aug`))
|
|
622
|
+
?.get(exported.name);
|
|
623
|
+
if (def)
|
|
624
|
+
item = definitionItem(checked, def);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (!item) {
|
|
628
|
+
const binding = checked.bindings.find(entry => entry.key === token.value);
|
|
629
|
+
if (binding)
|
|
630
|
+
item = { label: token.value, kind: 'variable',
|
|
631
|
+
detail: `binding ${binding.key}: ${tyName(binding.exposedType)}`,
|
|
632
|
+
documentation: definitionItem(checked, binding.target).documentation };
|
|
633
|
+
}
|
|
634
|
+
if (!item && token.value === 'self') {
|
|
635
|
+
const owner = file.items.find(entry => (entry.kind === 'class' || entry.kind === 'interface' || entry.kind === 'interceptor') &&
|
|
636
|
+
entry.span.start <= start && end <= entry.span.end);
|
|
637
|
+
if (owner?.kind === 'class' || owner?.kind === 'interface' || owner?.kind === 'interceptor')
|
|
638
|
+
item = { label: 'self', kind: 'parameter', detail: `self: ${owner.name}`,
|
|
639
|
+
documentation: 'The current instance. Class header fields are available inside its methods.' };
|
|
640
|
+
}
|
|
641
|
+
if (!item) {
|
|
642
|
+
const owner = file.items.find(entry => (entry.kind === 'class' || entry.kind === 'interface' ||
|
|
643
|
+
entry.kind === 'function' || entry.kind === 'interceptor') && entry.span.start <= start && end <= entry.span.end);
|
|
644
|
+
const typeParams = owner?.kind === 'class' || owner?.kind === 'interface' ||
|
|
645
|
+
owner?.kind === 'function' || owner?.kind === 'interceptor' ? owner.typeParams : [];
|
|
646
|
+
const method = owner && 'methods' in owner ? owner.methods.find(entry => entry.span.start <= start && end <= entry.span.end) : undefined;
|
|
647
|
+
if ([...typeParams, ...(method?.typeParams ?? [])].includes(token.value))
|
|
648
|
+
item = { label: token.value, kind: 'type', detail: `type parameter ${token.value}`,
|
|
649
|
+
documentation: 'A generic type placeholder supplied by the caller. It is checked before C generation.' };
|
|
650
|
+
}
|
|
651
|
+
return item ? { ...item, start, end } : undefined;
|
|
652
|
+
}
|
|
653
|
+
export function semanticTokens(checked, fileName) {
|
|
654
|
+
const file = checked.project.files.get(resolve(fileName));
|
|
655
|
+
if (!file)
|
|
656
|
+
return [];
|
|
657
|
+
const tokens = lex(file.path, file.source).tokens;
|
|
658
|
+
const scope = checked.project.scopes.get(file.path);
|
|
659
|
+
const parameters = new Set();
|
|
660
|
+
const fields = new Set();
|
|
661
|
+
const variables = new Set();
|
|
662
|
+
const typeParameters = new Set();
|
|
663
|
+
const declarationNames = new Map();
|
|
664
|
+
function markFunction(method, type) {
|
|
665
|
+
const first = tokens.findIndex(token => token.span.start >= method.span.start && token.span.end <= method.span.end && token.value === method.name && token.kind === 'identifier');
|
|
666
|
+
const name = tokens[first];
|
|
667
|
+
if (name?.kind === 'identifier')
|
|
668
|
+
declarationNames.set(name.span.start, type);
|
|
669
|
+
}
|
|
670
|
+
function visitStatements(statements) {
|
|
671
|
+
for (const stmt of statements) {
|
|
672
|
+
if (stmt.kind === 'assign' && stmt.target.kind === 'name')
|
|
673
|
+
variables.add(stmt.target.name);
|
|
674
|
+
if (stmt.kind === 'destructure' || stmt.kind === 'for')
|
|
675
|
+
stmt.names.forEach(name => variables.add(name));
|
|
676
|
+
if (stmt.kind === 'if') {
|
|
677
|
+
visitStatements(stmt.then);
|
|
678
|
+
visitStatements(stmt.otherwise);
|
|
679
|
+
}
|
|
680
|
+
else if (stmt.kind === 'while' || stmt.kind === 'for' || stmt.kind === 'scope' || stmt.kind === 'unsafe' || stmt.kind === 'borrow' || stmt.kind === 'lock') {
|
|
681
|
+
if (stmt.kind === 'lock')
|
|
682
|
+
variables.add(stmt.name);
|
|
683
|
+
visitStatements(stmt.body);
|
|
684
|
+
}
|
|
685
|
+
else if (stmt.kind === 'freeze')
|
|
686
|
+
variables.add(stmt.name);
|
|
687
|
+
else if (stmt.kind === 'try') {
|
|
688
|
+
visitStatements(stmt.body);
|
|
689
|
+
for (const clause of stmt.catches) {
|
|
690
|
+
parameters.add(clause.name);
|
|
691
|
+
visitStatements(clause.body);
|
|
692
|
+
}
|
|
693
|
+
visitStatements(stmt.always ?? []);
|
|
694
|
+
}
|
|
695
|
+
else if (stmt.kind === 'match')
|
|
696
|
+
for (const clause of stmt.cases) {
|
|
697
|
+
if (clause.name)
|
|
698
|
+
variables.add(clause.name);
|
|
699
|
+
visitStatements(clause.body);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
for (const item of file.items) {
|
|
704
|
+
if (item.kind === 'class' || item.kind === 'interface' || item.kind === 'interceptor') {
|
|
705
|
+
if (item.kind === 'class')
|
|
706
|
+
declarationNames.set(item.span.start, 'class');
|
|
707
|
+
if (item.kind === 'interceptor')
|
|
708
|
+
declarationNames.set(item.nameSpan.start, 'decorator');
|
|
709
|
+
item.typeParams.forEach(name => typeParameters.add(name));
|
|
710
|
+
if (item.kind === 'class' || item.kind === 'interceptor')
|
|
711
|
+
fieldsOf(item).forEach(field => fields.add(field.name));
|
|
712
|
+
if (item.kind === 'class')
|
|
713
|
+
visitStatements(item.constructorBody ?? []);
|
|
714
|
+
for (const method of item.methods) {
|
|
715
|
+
markFunction(method, 'method');
|
|
716
|
+
method.typeParams.forEach(name => typeParameters.add(name));
|
|
717
|
+
method.params.forEach(param => parameters.add(param.name));
|
|
718
|
+
visitStatements(method.body ?? []);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
else if (item.kind === 'function') {
|
|
722
|
+
markFunction(item, 'function');
|
|
723
|
+
item.typeParams.forEach(name => typeParameters.add(name));
|
|
724
|
+
item.params.forEach(param => parameters.add(param.name));
|
|
725
|
+
visitStatements(item.body ?? []);
|
|
726
|
+
}
|
|
727
|
+
else if (item.kind === 'test') {
|
|
728
|
+
variables.add(item.name);
|
|
729
|
+
for (const group of item.groups) {
|
|
730
|
+
visitStatements(group.setup.filter((entry) => entry.kind !== 'bind' && entry.kind !== 'include'));
|
|
731
|
+
group.cases.forEach(test => visitStatements(test.body));
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else if (['expr', 'assign', 'destructure', 'return', 'throw', 'if', 'while', 'for', 'scope', 'match', 'try', 'unsafe', 'borrow', 'lock', 'freeze', 'yield'].includes(item.kind))
|
|
735
|
+
visitStatements([item]);
|
|
736
|
+
}
|
|
737
|
+
const result = [];
|
|
738
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
739
|
+
const token = tokens[i];
|
|
740
|
+
if (token.kind !== 'identifier')
|
|
741
|
+
continue;
|
|
742
|
+
const previous = tokens[i - 1]?.kind;
|
|
743
|
+
const next = tokens[i + 1]?.kind;
|
|
744
|
+
let type;
|
|
745
|
+
if (previous === '.')
|
|
746
|
+
type = next === '(' || next === '<' ? 'method' : 'property';
|
|
747
|
+
else if (previous === 'interface' || previous === 'capability')
|
|
748
|
+
type = 'interface';
|
|
749
|
+
else if (previous === 'record')
|
|
750
|
+
type = 'class';
|
|
751
|
+
else if (declarationNames.has(token.span.start))
|
|
752
|
+
type = declarationNames.get(token.span.start);
|
|
753
|
+
else if (typeParameters.has(token.value))
|
|
754
|
+
type = 'typeParameter';
|
|
755
|
+
else if (parameters.has(token.value))
|
|
756
|
+
type = 'parameter';
|
|
757
|
+
else if (fields.has(token.value))
|
|
758
|
+
type = 'property';
|
|
759
|
+
else if (variables.has(token.value))
|
|
760
|
+
type = 'variable';
|
|
761
|
+
else if (builtinFunctions.some(operation => operation.name === token.value) && next === '(' || token.value === 'next')
|
|
762
|
+
type = 'function';
|
|
763
|
+
else if (httpPolicyNames.includes(token.value))
|
|
764
|
+
type = 'decorator';
|
|
765
|
+
else if (token.value in builtinTypes && !['int', 'c_int', 'float', 'bool', 'string', 'void', 'Error', 'Data'].includes(token.value))
|
|
766
|
+
type = 'class';
|
|
767
|
+
else if (token.value === 'Error')
|
|
768
|
+
type = 'interface';
|
|
769
|
+
else if (['int', 'c_int', 'float', 'bool', 'string', 'void'].includes(token.value))
|
|
770
|
+
type = 'type';
|
|
771
|
+
else {
|
|
772
|
+
const def = scope?.get(token.value);
|
|
773
|
+
if (def)
|
|
774
|
+
type = def.node.kind === 'interceptor' ? 'decorator' : def.node.kind === 'composition' ? 'function' : def.node.kind;
|
|
775
|
+
}
|
|
776
|
+
if (type)
|
|
777
|
+
result.push({ line: token.span.line - 1, start: token.span.column - 1,
|
|
778
|
+
length: token.span.end - token.span.start, type,
|
|
779
|
+
declaration: declarationNames.has(token.span.start) || previous === 'interface' });
|
|
780
|
+
}
|
|
781
|
+
return result;
|
|
782
|
+
}
|