@soundscript/soundscript 0.1.12 → 0.1.15
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/package.json +15 -6
- package/project-transform/index.js +2 -0
- package/project-transform/index.ts +8 -0
- package/project-transform/src/annotation_syntax.js +948 -0
- package/project-transform/src/annotation_syntax.ts +1217 -0
- package/project-transform/src/build_package.js +475 -0
- package/project-transform/src/build_package.ts +683 -0
- package/project-transform/src/bundled/portable-web-globals.d.ts +153 -0
- package/project-transform/src/bundled/runtime_externs.js +220 -0
- package/project-transform/src/bundled/runtime_externs.ts +237 -0
- package/project-transform/src/bundled/sound-libs/lib.decorators.d.ts +385 -0
- package/project-transform/src/bundled/sound-libs/lib.decorators.legacy.d.ts +22 -0
- package/project-transform/src/bundled/sound-libs/lib.dom.asynciterable.d.ts +42 -0
- package/project-transform/src/bundled/sound-libs/lib.dom.d.ts +39440 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.collection.d.ts +149 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.core.d.ts +657 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.d.ts +28 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.generator.d.ts +77 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.iterable.d.ts +616 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.promise.d.ts +80 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.proxy.d.ts +128 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.reflect.d.ts +144 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.d.ts +46 -0
- package/project-transform/src/bundled/sound-libs/lib.es2015.symbol.wellknown.d.ts +170 -0
- package/project-transform/src/bundled/sound-libs/lib.es2016.array.include.d.ts +116 -0
- package/project-transform/src/bundled/sound-libs/lib.es2016.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.arraybuffer.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.date.d.ts +31 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.object.d.ts +49 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.string.d.ts +45 -0
- package/project-transform/src/bundled/sound-libs/lib.es2017.typedarrays.d.ts +53 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.asyncgenerator.d.ts +77 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.asynciterable.d.ts +57 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.promise.d.ts +30 -0
- package/project-transform/src/bundled/sound-libs/lib.es2018.regexp.d.ts +37 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.array.d.ts +79 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.object.d.ts +47 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.string.d.ts +37 -0
- package/project-transform/src/bundled/sound-libs/lib.es2019.symbol.d.ts +24 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.bigint.d.ts +765 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.d.ts +27 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.date.d.ts +42 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.number.d.ts +28 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.promise.d.ts +49 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.string.d.ts +44 -0
- package/project-transform/src/bundled/sound-libs/lib.es2020.symbol.wellknown.d.ts +41 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.d.ts +23 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.promise.d.ts +48 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.string.d.ts +33 -0
- package/project-transform/src/bundled/sound-libs/lib.es2021.weakref.d.ts +78 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.array.d.ts +121 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.error.d.ts +75 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.object.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.regexp.d.ts +39 -0
- package/project-transform/src/bundled/sound-libs/lib.es2022.string.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.array.d.ts +924 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.collection.d.ts +21 -0
- package/project-transform/src/bundled/sound-libs/lib.es2023.d.ts +22 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.arraybuffer.d.ts +65 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.collection.d.ts +29 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.d.ts +26 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.object.d.ts +33 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.promise.d.ts +35 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.regexp.d.ts +25 -0
- package/project-transform/src/bundled/sound-libs/lib.es2024.string.d.ts +29 -0
- package/project-transform/src/bundled/sound-libs/lib.es5.d.ts +4924 -0
- package/project-transform/src/bundled/sound_stdlib.js +142 -0
- package/project-transform/src/bundled/sound_stdlib.ts +180 -0
- package/project-transform/src/checker/analyze_project.js +1361 -0
- package/project-transform/src/checker/analyze_project.ts +2246 -0
- package/project-transform/src/checker/diagnostics.js +112 -0
- package/project-transform/src/checker/diagnostics.ts +222 -0
- package/project-transform/src/checker/engine/context.js +235 -0
- package/project-transform/src/checker/engine/context.ts +340 -0
- package/project-transform/src/checker/engine/diagnostic_codes.js +72 -0
- package/project-transform/src/checker/engine/diagnostic_codes.ts +95 -0
- package/project-transform/src/checker/engine/facts.js +35 -0
- package/project-transform/src/checker/engine/facts.ts +48 -0
- package/project-transform/src/checker/engine/types.js +1 -0
- package/project-transform/src/checker/engine/types.ts +485 -0
- package/project-transform/src/checker/proof_escape_hatch_diagnostics.js +104 -0
- package/project-transform/src/checker/proof_escape_hatch_diagnostics.ts +173 -0
- package/project-transform/src/checker/rules/async_surface.js +231 -0
- package/project-transform/src/checker/rules/async_surface.ts +335 -0
- package/project-transform/src/checker/rules/class_lifecycle.js +798 -0
- package/project-transform/src/checker/rules/class_lifecycle.ts +1276 -0
- package/project-transform/src/checker/rules/directive_validation.js +571 -0
- package/project-transform/src/checker/rules/directive_validation.ts +938 -0
- package/project-transform/src/checker/rules/directives.js +23 -0
- package/project-transform/src/checker/rules/directives.ts +25 -0
- package/project-transform/src/checker/rules/flow.js +202 -0
- package/project-transform/src/checker/rules/flow.ts +333 -0
- package/project-transform/src/checker/rules/flow_facts.js +601 -0
- package/project-transform/src/checker/rules/flow_facts.ts +978 -0
- package/project-transform/src/checker/rules/flow_invalidation.js +1119 -0
- package/project-transform/src/checker/rules/flow_invalidation.ts +2150 -0
- package/project-transform/src/checker/rules/flow_shared.js +2822 -0
- package/project-transform/src/checker/rules/flow_shared.ts +4383 -0
- package/project-transform/src/checker/rules/foreign_boundary.js +120 -0
- package/project-transform/src/checker/rules/foreign_boundary.ts +196 -0
- package/project-transform/src/checker/rules/foreign_projection.js +279 -0
- package/project-transform/src/checker/rules/foreign_projection.ts +425 -0
- package/project-transform/src/checker/rules/generated_helpers.js +13 -0
- package/project-transform/src/checker/rules/generated_helpers.ts +18 -0
- package/project-transform/src/checker/rules/index.js +35 -0
- package/project-transform/src/checker/rules/index.ts +49 -0
- package/project-transform/src/checker/rules/namespace_object.js +845 -0
- package/project-transform/src/checker/rules/namespace_object.ts +1224 -0
- package/project-transform/src/checker/rules/non_ordinary_recovery.js +1328 -0
- package/project-transform/src/checker/rules/non_ordinary_recovery.ts +2391 -0
- package/project-transform/src/checker/rules/null_prototype.js +3 -0
- package/project-transform/src/checker/rules/null_prototype.ts +6 -0
- package/project-transform/src/checker/rules/overloads.js +181 -0
- package/project-transform/src/checker/rules/overloads.ts +317 -0
- package/project-transform/src/checker/rules/predicate_verification.js +691 -0
- package/project-transform/src/checker/rules/predicate_verification.ts +1088 -0
- package/project-transform/src/checker/rules/prototype_hardening.js +237 -0
- package/project-transform/src/checker/rules/prototype_hardening.ts +343 -0
- package/project-transform/src/checker/rules/receiver_discipline.js +263 -0
- package/project-transform/src/checker/rules/receiver_discipline.ts +356 -0
- package/project-transform/src/checker/rules/relations.js +6861 -0
- package/project-transform/src/checker/rules/relations.ts +12158 -0
- package/project-transform/src/checker/rules/resolved_builtins.js +274 -0
- package/project-transform/src/checker/rules/resolved_builtins.ts +438 -0
- package/project-transform/src/checker/rules/trust.js +217 -0
- package/project-transform/src/checker/rules/trust.ts +301 -0
- package/project-transform/src/checker/rules/type_guards.js +173 -0
- package/project-transform/src/checker/rules/type_guards.ts +257 -0
- package/project-transform/src/checker/rules/universal.js +17 -0
- package/project-transform/src/checker/rules/universal.ts +22 -0
- package/project-transform/src/checker/rules/unsafe_value_origin.js +80 -0
- package/project-transform/src/checker/rules/unsafe_value_origin.ts +125 -0
- package/project-transform/src/checker/rules/unsound_imports.js +218 -0
- package/project-transform/src/checker/rules/unsound_imports.ts +301 -0
- package/project-transform/src/checker/rules/unsound_syntax.js +1695 -0
- package/project-transform/src/checker/rules/unsound_syntax.ts +2540 -0
- package/project-transform/src/checker/rules/value_types.js +206 -0
- package/project-transform/src/checker/rules/value_types.ts +407 -0
- package/project-transform/src/checker/timing.js +43 -0
- package/project-transform/src/checker/timing.ts +78 -0
- package/project-transform/src/checker/unsupported_feature_messages.js +337 -0
- package/project-transform/src/checker/unsupported_feature_messages.ts +531 -0
- package/project-transform/src/cli.js +892 -0
- package/project-transform/src/cli.ts +1476 -0
- package/project-transform/src/compiler/compile_project.js +319 -0
- package/project-transform/src/compiler/compile_project.ts +508 -0
- package/project-transform/src/compiler/errors.js +10 -0
- package/project-transform/src/compiler/errors.ts +29 -0
- package/project-transform/src/compiler/ir.js +1 -0
- package/project-transform/src/compiler/ir.ts +1526 -0
- package/project-transform/src/compiler/lower.js +30550 -0
- package/project-transform/src/compiler/lower.ts +43645 -0
- package/project-transform/src/compiler/lower_arrays.js +140 -0
- package/project-transform/src/compiler/lower_arrays.ts +190 -0
- package/project-transform/src/compiler/lower_strings.js +121 -0
- package/project-transform/src/compiler/lower_strings.ts +198 -0
- package/project-transform/src/compiler/lower_tagged.js +329 -0
- package/project-transform/src/compiler/lower_tagged.ts +427 -0
- package/project-transform/src/compiler/lower_views.js +171 -0
- package/project-transform/src/compiler/lower_views.ts +251 -0
- package/project-transform/src/compiler/object_keys.js +25 -0
- package/project-transform/src/compiler/object_keys.ts +35 -0
- package/project-transform/src/compiler/runtime_ir.js +30 -0
- package/project-transform/src/compiler/runtime_ir.ts +727 -0
- package/project-transform/src/compiler/tagged_boundary.js +18 -0
- package/project-transform/src/compiler/tagged_boundary.ts +37 -0
- package/project-transform/src/compiler/toolchain.js +170 -0
- package/project-transform/src/compiler/toolchain.ts +229 -0
- package/project-transform/src/compiler/unicode_case_data.js +2102 -0
- package/project-transform/src/compiler/unicode_case_data.ts +2112 -0
- package/project-transform/src/compiler/wasm_js_host_runtime.js +656 -0
- package/project-transform/src/compiler/wasm_js_host_runtime.ts +762 -0
- package/project-transform/src/compiler/wat_arrays.js +3132 -0
- package/project-transform/src/compiler/wat_arrays.ts +3768 -0
- package/project-transform/src/compiler/wat_emitter.js +17952 -0
- package/project-transform/src/compiler/wat_emitter.ts +22812 -0
- package/project-transform/src/compiler/wat_strings.js +129 -0
- package/project-transform/src/compiler/wat_strings.ts +187 -0
- package/project-transform/src/compiler/wat_tagged.js +548 -0
- package/project-transform/src/compiler/wat_tagged.ts +674 -0
- package/project-transform/src/compiler_generator_runner.js +153 -0
- package/project-transform/src/compiler_generator_runner.ts +171 -0
- package/project-transform/src/compiler_object_test_helpers.js +69 -0
- package/project-transform/src/compiler_object_test_helpers.ts +96 -0
- package/project-transform/src/compiler_promise_runner.js +2116 -0
- package/project-transform/src/compiler_promise_runner.ts +2184 -0
- package/project-transform/src/compiler_test_helpers.js +854 -0
- package/project-transform/src/compiler_test_helpers.ts +1087 -0
- package/project-transform/src/config.js +568 -0
- package/project-transform/src/config.ts +892 -0
- package/project-transform/src/diagnostic_metadata.js +67 -0
- package/project-transform/src/diagnostic_metadata.ts +99 -0
- package/project-transform/src/diagnostic_reference.js +1368 -0
- package/project-transform/src/diagnostic_reference.ts +1523 -0
- package/project-transform/src/editor_diagnostics_worker.js +176 -0
- package/project-transform/src/editor_diagnostics_worker.ts +250 -0
- package/project-transform/src/editor_projection.js +224 -0
- package/project-transform/src/editor_projection.ts +421 -0
- package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.js +47 -0
- package/project-transform/src/frontend/builtin_expanded_program_test_cleanup.ts +72 -0
- package/project-transform/src/frontend/builtin_macro_support.js +842 -0
- package/project-transform/src/frontend/builtin_macro_support.ts +1386 -0
- package/project-transform/src/frontend/builtin_macros.js +409 -0
- package/project-transform/src/frontend/builtin_macros.ts +542 -0
- package/project-transform/src/frontend/component_poc_runtime.js +279 -0
- package/project-transform/src/frontend/component_poc_runtime.ts +372 -0
- package/project-transform/src/frontend/css_macro.js +148 -0
- package/project-transform/src/frontend/css_macro.ts +222 -0
- package/project-transform/src/frontend/derive_macros.js +2072 -0
- package/project-transform/src/frontend/derive_macros.ts +3188 -0
- package/project-transform/src/frontend/embedded_fragment_support.js +106 -0
- package/project-transform/src/frontend/embedded_fragment_support.ts +172 -0
- package/project-transform/src/frontend/error_normalization.js +403 -0
- package/project-transform/src/frontend/error_normalization.ts +832 -0
- package/project-transform/src/frontend/error_stdlib_support.js +1 -0
- package/project-transform/src/frontend/error_stdlib_support.ts +6 -0
- package/project-transform/src/frontend/expand_project.js +169 -0
- package/project-transform/src/frontend/expand_project.ts +248 -0
- package/project-transform/src/frontend/format_soundscript.js +297 -0
- package/project-transform/src/frontend/format_soundscript.ts +582 -0
- package/project-transform/src/frontend/graphql_macro.js +174 -0
- package/project-transform/src/frontend/graphql_macro.ts +253 -0
- package/project-transform/src/frontend/hash_context.js +83 -0
- package/project-transform/src/frontend/hash_context.ts +113 -0
- package/project-transform/src/frontend/hkt_macro.js +448 -0
- package/project-transform/src/frontend/hkt_macro.ts +897 -0
- package/project-transform/src/frontend/import_binding_usage.js +190 -0
- package/project-transform/src/frontend/import_binding_usage.ts +277 -0
- package/project-transform/src/frontend/macro_advanced_backend_adapter.js +58 -0
- package/project-transform/src/frontend/macro_advanced_backend_adapter.ts +123 -0
- package/project-transform/src/frontend/macro_advanced_context.js +826 -0
- package/project-transform/src/frontend/macro_advanced_context.ts +1102 -0
- package/project-transform/src/frontend/macro_advanced_output.js +21 -0
- package/project-transform/src/frontend/macro_advanced_output.ts +41 -0
- package/project-transform/src/frontend/macro_api.js +353 -0
- package/project-transform/src/frontend/macro_api.ts +1722 -0
- package/project-transform/src/frontend/macro_api_internal.js +35 -0
- package/project-transform/src/frontend/macro_api_internal.ts +80 -0
- package/project-transform/src/frontend/macro_api_module_support.js +39 -0
- package/project-transform/src/frontend/macro_api_module_support.ts +65 -0
- package/project-transform/src/frontend/macro_backend_adapter.js +272 -0
- package/project-transform/src/frontend/macro_backend_adapter.ts +420 -0
- package/project-transform/src/frontend/macro_context.js +816 -0
- package/project-transform/src/frontend/macro_context.ts +1105 -0
- package/project-transform/src/frontend/macro_debug.js +99 -0
- package/project-transform/src/frontend/macro_debug.ts +157 -0
- package/project-transform/src/frontend/macro_definition_support.js +28 -0
- package/project-transform/src/frontend/macro_definition_support.ts +73 -0
- package/project-transform/src/frontend/macro_errors.js +40 -0
- package/project-transform/src/frontend/macro_errors.ts +70 -0
- package/project-transform/src/frontend/macro_expander.js +919 -0
- package/project-transform/src/frontend/macro_expander.ts +1611 -0
- package/project-transform/src/frontend/macro_factory_support.js +176 -0
- package/project-transform/src/frontend/macro_factory_support.ts +263 -0
- package/project-transform/src/frontend/macro_host_ast_internal.js +64 -0
- package/project-transform/src/frontend/macro_host_ast_internal.ts +109 -0
- package/project-transform/src/frontend/macro_index.js +27 -0
- package/project-transform/src/frontend/macro_index.ts +50 -0
- package/project-transform/src/frontend/macro_loader.js +281 -0
- package/project-transform/src/frontend/macro_loader.ts +506 -0
- package/project-transform/src/frontend/macro_operand_semantics.js +838 -0
- package/project-transform/src/frontend/macro_operand_semantics.ts +1489 -0
- package/project-transform/src/frontend/macro_output.js +54 -0
- package/project-transform/src/frontend/macro_output.ts +123 -0
- package/project-transform/src/frontend/macro_parser.js +611 -0
- package/project-transform/src/frontend/macro_parser.ts +832 -0
- package/project-transform/src/frontend/macro_resolver.js +69 -0
- package/project-transform/src/frontend/macro_resolver.ts +125 -0
- package/project-transform/src/frontend/macro_rewrite.js +285 -0
- package/project-transform/src/frontend/macro_rewrite.ts +442 -0
- package/project-transform/src/frontend/macro_runtime_support.js +232 -0
- package/project-transform/src/frontend/macro_runtime_support.ts +324 -0
- package/project-transform/src/frontend/macro_scanner.js +393 -0
- package/project-transform/src/frontend/macro_scanner.ts +455 -0
- package/project-transform/src/frontend/macro_semantic_backend_adapter.js +87 -0
- package/project-transform/src/frontend/macro_semantic_backend_adapter.ts +166 -0
- package/project-transform/src/frontend/macro_semantic_context.js +5 -0
- package/project-transform/src/frontend/macro_semantic_context.ts +12 -0
- package/project-transform/src/frontend/macro_semantic_output.js +24 -0
- package/project-transform/src/frontend/macro_semantic_output.ts +47 -0
- package/project-transform/src/frontend/macro_semantic_types.js +1 -0
- package/project-transform/src/frontend/macro_semantic_types.ts +98 -0
- package/project-transform/src/frontend/macro_semantics.js +1172 -0
- package/project-transform/src/frontend/macro_semantics.ts +1502 -0
- package/project-transform/src/frontend/macro_site_kind_support.js +164 -0
- package/project-transform/src/frontend/macro_site_kind_support.ts +255 -0
- package/project-transform/src/frontend/macro_syntax_internal.js +1950 -0
- package/project-transform/src/frontend/macro_syntax_internal.ts +3338 -0
- package/project-transform/src/frontend/macro_templates.js +57 -0
- package/project-transform/src/frontend/macro_templates.ts +143 -0
- package/project-transform/src/frontend/macro_test_helpers.js +82 -0
- package/project-transform/src/frontend/macro_test_helpers.ts +136 -0
- package/project-transform/src/frontend/macro_types.js +1 -0
- package/project-transform/src/frontend/macro_types.ts +103 -0
- package/project-transform/src/frontend/macro_vm.js +39 -0
- package/project-transform/src/frontend/macro_vm.ts +113 -0
- package/project-transform/src/frontend/match_macro.js +885 -0
- package/project-transform/src/frontend/match_macro.ts +1220 -0
- package/project-transform/src/frontend/numeric_normalization.js +824 -0
- package/project-transform/src/frontend/numeric_normalization.ts +1380 -0
- package/project-transform/src/frontend/numeric_prelude.js +278 -0
- package/project-transform/src/frontend/numeric_prelude.ts +370 -0
- package/project-transform/src/frontend/project_frontend.js +2396 -0
- package/project-transform/src/frontend/project_frontend.ts +3776 -0
- package/project-transform/src/frontend/project_macro_support.js +1401 -0
- package/project-transform/src/frontend/project_macro_support.ts +2137 -0
- package/project-transform/src/frontend/sql_macro.js +175 -0
- package/project-transform/src/frontend/sql_macro.ts +254 -0
- package/project-transform/src/frontend/sql_stdlib_support.js +1 -0
- package/project-transform/src/frontend/sql_stdlib_support.ts +6 -0
- package/project-transform/src/frontend/std_package_support.js +228 -0
- package/project-transform/src/frontend/std_package_support.ts +400 -0
- package/project-transform/src/frontend/value_normalization.js +306 -0
- package/project-transform/src/frontend/value_normalization.ts +599 -0
- package/project-transform/src/lsp/project_service.js +4771 -0
- package/project-transform/src/lsp/project_service.ts +7580 -0
- package/project-transform/src/lsp/protocol.js +9 -0
- package/project-transform/src/lsp/protocol.ts +38 -0
- package/project-transform/src/lsp/server.js +355 -0
- package/project-transform/src/lsp/server.ts +671 -0
- package/project-transform/src/lsp/session.js +49 -0
- package/project-transform/src/lsp/session.ts +48 -0
- package/project-transform/src/lsp/timing.js +43 -0
- package/project-transform/src/lsp/timing.ts +76 -0
- package/project-transform/src/lsp/transport.js +205 -0
- package/project-transform/src/lsp/transport.ts +253 -0
- package/project-transform/src/lsp_main.js +5 -0
- package/project-transform/src/lsp_main.ts +7 -0
- package/project-transform/src/macros.d.ts +1 -0
- package/project-transform/src/macros.js +1 -0
- package/project-transform/src/macros.ts +1 -0
- package/project-transform/src/main.js +24 -0
- package/project-transform/src/main.ts +28 -0
- package/project-transform/src/platform/host.js +264 -0
- package/project-transform/src/platform/host.ts +343 -0
- package/project-transform/src/platform/path.js +8 -0
- package/project-transform/src/platform/path.ts +20 -0
- package/project-transform/src/public_macro_api/macro_api.d.ts +1054 -0
- package/project-transform/src/public_macro_api/macro_semantic_types.d.ts +66 -0
- package/project-transform/src/public_macro_api/macro_types.d.ts +70 -0
- package/project-transform/src/run_program.js +14 -0
- package/project-transform/src/run_program.ts +33 -0
- package/project-transform/src/runtime/materialize.js +371 -0
- package/project-transform/src/runtime/materialize.ts +502 -0
- package/project-transform/src/runtime/on_demand.js +203 -0
- package/project-transform/src/runtime/on_demand.ts +305 -0
- package/project-transform/src/runtime/source_maps.js +205 -0
- package/project-transform/src/runtime/source_maps.ts +297 -0
- package/project-transform/src/runtime/transform.js +148 -0
- package/project-transform/src/runtime/transform.ts +295 -0
- package/project-transform/src/service/types.js +1 -0
- package/project-transform/src/service/types.ts +22 -0
- package/project-transform/src/soundscript_packages.js +477 -0
- package/project-transform/src/soundscript_packages.ts +754 -0
- package/project-transform/src/soundscript_runtime_specifiers.js +88 -0
- package/project-transform/src/soundscript_runtime_specifiers.ts +96 -0
- package/project-transform/src/stdlib/async.d.ts +81 -0
- package/project-transform/src/stdlib/async.js +213 -0
- package/project-transform/src/stdlib/async.ts +315 -0
- package/project-transform/src/stdlib/codec.d.ts +32 -0
- package/project-transform/src/stdlib/codec.js +30 -0
- package/project-transform/src/stdlib/codec.ts +76 -0
- package/project-transform/src/stdlib/compare.d.ts +28 -0
- package/project-transform/src/stdlib/compare.js +115 -0
- package/project-transform/src/stdlib/compare.ts +151 -0
- package/project-transform/src/stdlib/css.d.ts +16 -0
- package/project-transform/src/stdlib/css.js +9 -0
- package/project-transform/src/stdlib/css.ts +28 -0
- package/project-transform/src/stdlib/debug.d.ts +2 -0
- package/project-transform/src/stdlib/debug.js +9 -0
- package/project-transform/src/stdlib/debug.ts +10 -0
- package/project-transform/src/stdlib/decode.d.ts +86 -0
- package/project-transform/src/stdlib/decode.js +254 -0
- package/project-transform/src/stdlib/decode.ts +390 -0
- package/project-transform/src/stdlib/derive.d.ts +6 -0
- package/project-transform/src/stdlib/derive.js +7 -0
- package/project-transform/src/stdlib/derive.ts +7 -0
- package/project-transform/src/stdlib/encode.d.ts +100 -0
- package/project-transform/src/stdlib/encode.js +130 -0
- package/project-transform/src/stdlib/encode.ts +259 -0
- package/project-transform/src/stdlib/failures.d.ts +23 -0
- package/project-transform/src/stdlib/failures.js +41 -0
- package/project-transform/src/stdlib/failures.ts +64 -0
- package/project-transform/src/stdlib/fetch.d.ts +67 -0
- package/project-transform/src/stdlib/fetch.js +5 -0
- package/project-transform/src/stdlib/fetch.ts +11 -0
- package/project-transform/src/stdlib/graphql.d.ts +16 -0
- package/project-transform/src/stdlib/graphql.js +9 -0
- package/project-transform/src/stdlib/graphql.ts +28 -0
- package/project-transform/src/stdlib/hash.d.ts +34 -0
- package/project-transform/src/stdlib/hash.js +110 -0
- package/project-transform/src/stdlib/hash.ts +188 -0
- package/project-transform/src/stdlib/hkt.d.ts +40 -0
- package/project-transform/src/stdlib/hkt.js +3 -0
- package/project-transform/src/stdlib/hkt.ts +41 -0
- package/project-transform/src/stdlib/index.d.ts +9 -0
- package/project-transform/src/stdlib/index.js +15 -0
- package/project-transform/src/stdlib/index.ts +23 -0
- package/project-transform/src/stdlib/json.d.ts +125 -0
- package/project-transform/src/stdlib/json.js +764 -0
- package/project-transform/src/stdlib/json.ts +1034 -0
- package/project-transform/src/stdlib/match.d.ts +11 -0
- package/project-transform/src/stdlib/match.js +13 -0
- package/project-transform/src/stdlib/match.ts +26 -0
- package/project-transform/src/stdlib/numerics.d.ts +523 -0
- package/project-transform/src/stdlib/numerics.js +1356 -0
- package/project-transform/src/stdlib/numerics.ts +1937 -0
- package/project-transform/src/stdlib/random.d.ts +19 -0
- package/project-transform/src/stdlib/random.js +3 -0
- package/project-transform/src/stdlib/random.ts +5 -0
- package/project-transform/src/stdlib/result.d.ts +68 -0
- package/project-transform/src/stdlib/result.js +139 -0
- package/project-transform/src/stdlib/result.ts +248 -0
- package/project-transform/src/stdlib/sql.d.ts +22 -0
- package/project-transform/src/stdlib/sql.js +23 -0
- package/project-transform/src/stdlib/sql.ts +53 -0
- package/project-transform/src/stdlib/text.d.ts +24 -0
- package/project-transform/src/stdlib/text.js +3 -0
- package/project-transform/src/stdlib/text.ts +4 -0
- package/project-transform/src/stdlib/thunk.d.ts +2 -0
- package/project-transform/src/stdlib/thunk.js +9 -0
- package/project-transform/src/stdlib/thunk.ts +15 -0
- package/project-transform/src/stdlib/typeclasses.d.ts +57 -0
- package/project-transform/src/stdlib/typeclasses.js +78 -0
- package/project-transform/src/stdlib/typeclasses.ts +173 -0
- package/project-transform/src/stdlib/url.d.ts +37 -0
- package/project-transform/src/stdlib/url.js +3 -0
- package/project-transform/src/stdlib/url.ts +4 -0
- package/project-transform/src/stdlib/value.d.ts +9 -0
- package/project-transform/src/stdlib/value.js +104 -0
- package/project-transform/src/stdlib/value.ts +133 -0
- package/project-transform/src/test_installed_stdlib.js +147 -0
- package/project-transform/src/test_installed_stdlib.ts +245 -0
- package/project-transform/src/test_macro_package_fixture.js +50 -0
- package/project-transform/src/test_macro_package_fixture.ts +68 -0
- package/project-transform/src/value_deep_safe.js +191 -0
- package/project-transform/src/value_deep_safe.ts +273 -0
|
@@ -0,0 +1,4771 @@
|
|
|
1
|
+
import { dirname, fromFileUrl, join, toFileUrl } from '../platform/path.js';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import { analyzePreparedProject, analyzePreparedProjectForFile, disposePreparedAnalysisProject, getPreparedAnalysisViewForFile, prepareProjectAnalysis, } from '../checker/analyze_project.js';
|
|
4
|
+
import { collectProjectedUnknownValueExportNames, getLineAndCharacterOfPosition, getPositionOfLineAndCharacter, isProjectedSoundscriptDeclarationFile, isSoundscriptSourceFile, isUnsoundImportedModuleForTypeProjection, mapProgramEnclosingRangeToSource, mapProgramRangeToSource, mapSourcePositionToProgram, toProjectedDeclarationSourceFileName, } from '../frontend/project_frontend.js';
|
|
5
|
+
import { formatSoundscriptText } from '../frontend/format_soundscript.js';
|
|
6
|
+
import { isElaboratedBigIntTypeImportName, isElaboratedF64TypeImportName, } from '../frontend/numeric_prelude.js';
|
|
7
|
+
import { createMacroDebugSnapshot, readMacroDebugStageText, } from '../frontend/macro_debug.js';
|
|
8
|
+
import { createAdvancedMacroContext } from '../frontend/macro_advanced_context.js';
|
|
9
|
+
import { createSyntaxOnlyMacroContext } from '../frontend/macro_context.js';
|
|
10
|
+
import { formatMacroSignatureExamples, tryReadMacroSignature } from '../frontend/macro_api.js';
|
|
11
|
+
import { analysisRegionForMacroDefinition, fragmentsForMacroDefinition, parseMacroSyntaxNodeForDefinition, } from '../frontend/macro_definition_support.js';
|
|
12
|
+
import { createPatchedMacroRegion, mapMaterializedRangeToSource, materializeRegionForAnalysis, materializeRegionForHover, resolveBlockCompletionNodeAtSourcePosition, resolveBlockNodeAtSourcePosition, resolveCompletionNodeAtMaterializedRegion, resolveExpressionCompletionNodeAtSourcePosition, resolveExpressionNodeAtSourcePosition, resolveNodeAtMaterializedRegion, wrapMaterializedRegion, } from '../frontend/macro_operand_semantics.js';
|
|
13
|
+
import { collectResolvedMacroPlaceholders, } from '../frontend/macro_resolver.js';
|
|
14
|
+
import { parseMacroInvocationAt } from '../frontend/macro_parser.js';
|
|
15
|
+
import { scanMacroCandidates } from '../frontend/macro_scanner.js';
|
|
16
|
+
import { fileExistsSync, readTextFileSync } from '../platform/host.js';
|
|
17
|
+
import { logLspTiming, measureLspTiming } from './timing.js';
|
|
18
|
+
const projectContextCacheBySession = new WeakMap();
|
|
19
|
+
const resolvedMacroPlaceholderCache = new WeakMap();
|
|
20
|
+
const importedMacroDefinitionCache = new WeakMap();
|
|
21
|
+
function getProjectContextCache(session) {
|
|
22
|
+
let cache = projectContextCacheBySession.get(session);
|
|
23
|
+
if (!cache) {
|
|
24
|
+
cache = new Map();
|
|
25
|
+
projectContextCacheBySession.set(session, cache);
|
|
26
|
+
}
|
|
27
|
+
return cache;
|
|
28
|
+
}
|
|
29
|
+
function projectContextCacheKey(projectPath, mode) {
|
|
30
|
+
return `${projectPath}::${mode}`;
|
|
31
|
+
}
|
|
32
|
+
function aggregateMacroCacheStats(preparedProject) {
|
|
33
|
+
const aggregated = {
|
|
34
|
+
evaluatedModules: 0,
|
|
35
|
+
moduleCacheHits: 0,
|
|
36
|
+
moduleCacheInvalidations: 0,
|
|
37
|
+
moduleCacheMisses: 0,
|
|
38
|
+
};
|
|
39
|
+
for (const view of [preparedProject.tsView, preparedProject.stsView]) {
|
|
40
|
+
if (!view) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
aggregated.evaluatedModules += view.macroCacheStats.evaluatedModules;
|
|
44
|
+
aggregated.moduleCacheHits += view.macroCacheStats.moduleCacheHits;
|
|
45
|
+
aggregated.moduleCacheInvalidations += view.macroCacheStats.moduleCacheInvalidations;
|
|
46
|
+
aggregated.moduleCacheMisses += view.macroCacheStats.moduleCacheMisses;
|
|
47
|
+
}
|
|
48
|
+
return aggregated;
|
|
49
|
+
}
|
|
50
|
+
const SEMANTIC_TOKEN_TYPES = [
|
|
51
|
+
'namespace',
|
|
52
|
+
'type',
|
|
53
|
+
'keyword',
|
|
54
|
+
'class',
|
|
55
|
+
'enum',
|
|
56
|
+
'interface',
|
|
57
|
+
'typeParameter',
|
|
58
|
+
'parameter',
|
|
59
|
+
'variable',
|
|
60
|
+
'property',
|
|
61
|
+
'enumMember',
|
|
62
|
+
'function',
|
|
63
|
+
'method',
|
|
64
|
+
];
|
|
65
|
+
const SEMANTIC_TOKEN_MODIFIERS = [
|
|
66
|
+
'declaration',
|
|
67
|
+
'readonly',
|
|
68
|
+
];
|
|
69
|
+
const SEMANTIC_TOKEN_TYPE_INDICES = new Map(SEMANTIC_TOKEN_TYPES.map((tokenType, index) => [tokenType, index]));
|
|
70
|
+
const SEMANTIC_TOKEN_MODIFIER_INDICES = new Map(SEMANTIC_TOKEN_MODIFIERS.map((modifier, index) => [modifier, index]));
|
|
71
|
+
function findProjectPath(filePath) {
|
|
72
|
+
let currentDirectory = dirname(filePath);
|
|
73
|
+
while (true) {
|
|
74
|
+
for (const fileName of ['tsconfig.soundscript.json', 'tsconfig.json']) {
|
|
75
|
+
const candidate = join(currentDirectory, fileName);
|
|
76
|
+
if (fileExistsSync(candidate)) {
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const parentDirectory = dirname(currentDirectory);
|
|
81
|
+
if (parentDirectory === currentDirectory) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
currentDirectory = parentDirectory;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function toFileOverrideMap(documents) {
|
|
88
|
+
return new Map(documents.map((document) => [fromFileUrl(document.uri), document.text]));
|
|
89
|
+
}
|
|
90
|
+
function collectAdditionalRootNames(projectPath, documents) {
|
|
91
|
+
const projectDirectory = dirname(projectPath);
|
|
92
|
+
return documents
|
|
93
|
+
.map((document) => fromFileUrl(document.uri))
|
|
94
|
+
.filter((filePath) => {
|
|
95
|
+
if (filePath === projectDirectory) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
const withTrailingSlash = projectDirectory.endsWith('/')
|
|
99
|
+
? projectDirectory
|
|
100
|
+
: `${projectDirectory}/`;
|
|
101
|
+
return filePath.startsWith(withTrailingSlash);
|
|
102
|
+
})
|
|
103
|
+
.sort();
|
|
104
|
+
}
|
|
105
|
+
function createProjectDocumentsKey(projectPath, documents) {
|
|
106
|
+
const projectDirectory = dirname(projectPath);
|
|
107
|
+
const withTrailingSlash = projectDirectory.endsWith('/')
|
|
108
|
+
? projectDirectory
|
|
109
|
+
: `${projectDirectory}/`;
|
|
110
|
+
return documents
|
|
111
|
+
.filter((document) => {
|
|
112
|
+
const filePath = fromFileUrl(document.uri);
|
|
113
|
+
return filePath === projectDirectory || filePath.startsWith(withTrailingSlash);
|
|
114
|
+
})
|
|
115
|
+
.map((document) => `${document.uri}:${document.version}`)
|
|
116
|
+
.sort()
|
|
117
|
+
.join('|');
|
|
118
|
+
}
|
|
119
|
+
function createStsDocumentsKey(projectPath, documents) {
|
|
120
|
+
const projectDirectory = dirname(projectPath);
|
|
121
|
+
const withTrailingSlash = projectDirectory.endsWith('/')
|
|
122
|
+
? projectDirectory
|
|
123
|
+
: `${projectDirectory}/`;
|
|
124
|
+
return documents
|
|
125
|
+
.filter((document) => {
|
|
126
|
+
const filePath = fromFileUrl(document.uri);
|
|
127
|
+
if (!(filePath === projectDirectory || filePath.startsWith(withTrailingSlash))) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return filePath === projectPath || isSoundscriptSourceFile(filePath);
|
|
131
|
+
})
|
|
132
|
+
.map((document) => `${document.uri}:${document.version}`)
|
|
133
|
+
.sort()
|
|
134
|
+
.join('|');
|
|
135
|
+
}
|
|
136
|
+
function getProjectContext(filePath, session, requestedMode = 'full') {
|
|
137
|
+
const projectPath = findProjectPath(filePath);
|
|
138
|
+
if (!projectPath) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
const documents = session.getAll();
|
|
142
|
+
const additionalRootNames = collectAdditionalRootNames(projectPath, documents);
|
|
143
|
+
const documentsKey = createProjectDocumentsKey(projectPath, documents);
|
|
144
|
+
const stsDocumentsKey = createStsDocumentsKey(projectPath, documents);
|
|
145
|
+
const projectContextCache = getProjectContextCache(session);
|
|
146
|
+
const mode = requestedMode === 'sts-local' && isSoundscriptSourceFile(filePath)
|
|
147
|
+
? 'sts-local'
|
|
148
|
+
: 'full';
|
|
149
|
+
const cached = projectContextCache.get(projectContextCacheKey(projectPath, mode));
|
|
150
|
+
const alternateCached = projectContextCache.get(projectContextCacheKey(projectPath, mode === 'full' ? 'sts-local' : 'full'));
|
|
151
|
+
const rootsMatch = cached !== undefined &&
|
|
152
|
+
cached.additionalRootNames.length === additionalRootNames.length &&
|
|
153
|
+
cached.additionalRootNames.every((rootName, index) => rootName === additionalRootNames[index]);
|
|
154
|
+
const alternateRootsMatch = alternateCached !== undefined &&
|
|
155
|
+
alternateCached.additionalRootNames.length === additionalRootNames.length &&
|
|
156
|
+
alternateCached.additionalRootNames.every((rootName, index) => rootName === additionalRootNames[index]);
|
|
157
|
+
const canReuseCachedForFull = cached !== undefined &&
|
|
158
|
+
rootsMatch &&
|
|
159
|
+
cached.documentsKey === documentsKey;
|
|
160
|
+
const canReuseCachedForStsLocal = cached !== undefined &&
|
|
161
|
+
rootsMatch &&
|
|
162
|
+
cached.stsDocumentsKey === stsDocumentsKey;
|
|
163
|
+
const reusableContext = rootsMatch ? cached : alternateRootsMatch ? alternateCached : undefined;
|
|
164
|
+
const canReusePreparedProject = reusableContext !== undefined;
|
|
165
|
+
if ((mode === 'full' && canReuseCachedForFull) ||
|
|
166
|
+
(mode === 'sts-local' && canReuseCachedForStsLocal)) {
|
|
167
|
+
return { context: cached, projectPath };
|
|
168
|
+
}
|
|
169
|
+
const prepareStart = performance.now();
|
|
170
|
+
const context = {
|
|
171
|
+
additionalRootNames,
|
|
172
|
+
analyzedResultByFile: new Map(),
|
|
173
|
+
documentsKey,
|
|
174
|
+
mode,
|
|
175
|
+
stsDocumentsKey,
|
|
176
|
+
preparedProject: prepareProjectAnalysis({
|
|
177
|
+
additionalRootNames,
|
|
178
|
+
projectPath,
|
|
179
|
+
workingDirectory: dirname(projectPath),
|
|
180
|
+
fileOverrides: toFileOverrideMap(documents),
|
|
181
|
+
}, canReusePreparedProject ? reusableContext.preparedProject : undefined, { deferTypescriptView: mode === 'sts-local' }),
|
|
182
|
+
};
|
|
183
|
+
const macroCacheStats = aggregateMacroCacheStats(context.preparedProject);
|
|
184
|
+
logLspTiming('project.prepare', performance.now() - prepareStart, {
|
|
185
|
+
projectPath,
|
|
186
|
+
additionalRoots: additionalRootNames.length,
|
|
187
|
+
openDocuments: documents.length,
|
|
188
|
+
cache: canReusePreparedProject
|
|
189
|
+
? reusableContext === cached ? 'incremental-rebuild' : 'cross-mode-rebuild'
|
|
190
|
+
: 'rebuild',
|
|
191
|
+
macroCacheHits: macroCacheStats.moduleCacheHits,
|
|
192
|
+
macroCacheMisses: macroCacheStats.moduleCacheMisses,
|
|
193
|
+
macroCacheInvalidations: macroCacheStats.moduleCacheInvalidations,
|
|
194
|
+
macroModulesEvaluated: macroCacheStats.evaluatedModules,
|
|
195
|
+
}, { always: true });
|
|
196
|
+
disposePreparedAnalysisProject(cached?.preparedProject, context.preparedProject);
|
|
197
|
+
projectContextCache.set(projectContextCacheKey(projectPath, mode), context);
|
|
198
|
+
return { context, projectPath };
|
|
199
|
+
}
|
|
200
|
+
export function getPreparedProjectForTest(uri, session, mode = 'full') {
|
|
201
|
+
const filePath = fromFileUrl(uri);
|
|
202
|
+
return getProjectContext(filePath, session, mode)?.context.preparedProject ?? null;
|
|
203
|
+
}
|
|
204
|
+
function getPreparedProjectContext(filePath, session, mode = isSoundscriptSourceFile(filePath) ? 'sts-local' : 'full') {
|
|
205
|
+
const entry = getProjectContext(filePath, session, mode);
|
|
206
|
+
if (!entry) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return getPreparedAnalysisViewForFile(entry.context.preparedProject, filePath);
|
|
210
|
+
}
|
|
211
|
+
function getPreparedProjectViews(filePath, session) {
|
|
212
|
+
const entry = getProjectContext(filePath, session, 'full');
|
|
213
|
+
if (!entry) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
return [
|
|
217
|
+
entry.context.preparedProject.tsView,
|
|
218
|
+
entry.context.preparedProject.stsView,
|
|
219
|
+
].filter((view) => view !== null);
|
|
220
|
+
}
|
|
221
|
+
function getMacroDebugSnapshotForFile(filePath, session) {
|
|
222
|
+
const view = getPreparedProjectContext(filePath, session, 'full');
|
|
223
|
+
if (!view) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return createMacroDebugSnapshot({
|
|
227
|
+
diagnosticPreparedFiles: view.diagnosticPreparedFiles,
|
|
228
|
+
filePath,
|
|
229
|
+
macroEnvironment: view.macroEnvironment,
|
|
230
|
+
preparedProgram: view.preparedProgram,
|
|
231
|
+
program: view.program,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
function getAnalyzedProjectContext(filePath, session) {
|
|
235
|
+
var _a;
|
|
236
|
+
const entry = getProjectContext(filePath, session, 'full');
|
|
237
|
+
if (!entry) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
(_a = entry.context).analyzedResult ?? (_a.analyzedResult = measureLspTiming('project.analyze', {
|
|
241
|
+
projectPath: entry.projectPath,
|
|
242
|
+
rootFiles: (entry.context.preparedProject.tsView?.program.getRootFileNames().length ?? 0) +
|
|
243
|
+
(entry.context.preparedProject.stsView?.program.getRootFileNames().length ?? 0),
|
|
244
|
+
}, () => analyzePreparedProject(entry.context.preparedProject), { always: true }));
|
|
245
|
+
return entry.context.analyzedResult;
|
|
246
|
+
}
|
|
247
|
+
function getFileLocalAnalyzedProjectContext(filePath, session) {
|
|
248
|
+
if (!isSoundscriptSourceFile(filePath)) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const entry = getProjectContext(filePath, session, 'sts-local');
|
|
252
|
+
if (!entry) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
const cached = entry.context.analyzedResultByFile.get(filePath);
|
|
256
|
+
if (cached) {
|
|
257
|
+
return cached;
|
|
258
|
+
}
|
|
259
|
+
const analyzedResult = measureLspTiming('project.analyzeFile', {
|
|
260
|
+
filePath,
|
|
261
|
+
projectPath: entry.projectPath,
|
|
262
|
+
rootFiles: entry.context.preparedProject.stsView?.program.getRootFileNames().length ?? 0,
|
|
263
|
+
}, () => analyzePreparedProjectForFile(entry.context.preparedProject, filePath), { always: true });
|
|
264
|
+
entry.context.analyzedResultByFile.set(filePath, analyzedResult);
|
|
265
|
+
return analyzedResult;
|
|
266
|
+
}
|
|
267
|
+
function getCollectedResolvedMacroPlaceholders(preparedProject) {
|
|
268
|
+
const cached = resolvedMacroPlaceholderCache.get(preparedProject.preparedProgram);
|
|
269
|
+
if (cached) {
|
|
270
|
+
return cached;
|
|
271
|
+
}
|
|
272
|
+
const collected = measureLspTiming('macro.placeholderCollection', {
|
|
273
|
+
rootFiles: preparedProject.program.getRootFileNames().length,
|
|
274
|
+
}, () => collectResolvedMacroPlaceholders(preparedProject.preparedProgram), { always: true });
|
|
275
|
+
resolvedMacroPlaceholderCache.set(preparedProject.preparedProgram, collected);
|
|
276
|
+
return collected;
|
|
277
|
+
}
|
|
278
|
+
function getImportedMacroDefinitionsForFile(preparedProject, filePath) {
|
|
279
|
+
let byFile = importedMacroDefinitionCache.get(preparedProject.preparedProgram);
|
|
280
|
+
if (!byFile) {
|
|
281
|
+
byFile = new Map();
|
|
282
|
+
importedMacroDefinitionCache.set(preparedProject.preparedProgram, byFile);
|
|
283
|
+
}
|
|
284
|
+
const cached = byFile.get(filePath);
|
|
285
|
+
if (cached) {
|
|
286
|
+
return cached;
|
|
287
|
+
}
|
|
288
|
+
const sourceFile = preparedProject.preparedProgram.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(filePath));
|
|
289
|
+
const definitions = sourceFile
|
|
290
|
+
? preparedProject.macroEnvironment.definitionsForFile(sourceFile)
|
|
291
|
+
: new Map();
|
|
292
|
+
byFile.set(filePath, definitions);
|
|
293
|
+
return definitions;
|
|
294
|
+
}
|
|
295
|
+
function isAugmentDeclarationMacroInvocation(preparedProject, resolved) {
|
|
296
|
+
if (!resolved.placeholder.invocation.declarationSpan) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
const definition = getImportedMacroDefinitionsForFile(preparedProject, resolved.placeholder.fileName).get(resolved.placeholder.invocation.nameText);
|
|
300
|
+
return definition?.expansionMode === 'augment';
|
|
301
|
+
}
|
|
302
|
+
function measureDocumentOperation(operation, uri, fn) {
|
|
303
|
+
return measureLspTiming(operation, { uri }, fn);
|
|
304
|
+
}
|
|
305
|
+
function createNoProjectDiagnostic(filePath) {
|
|
306
|
+
return {
|
|
307
|
+
source: 'cli',
|
|
308
|
+
code: 'SOUNDSCRIPT_NO_PROJECT',
|
|
309
|
+
category: 'warning',
|
|
310
|
+
message: 'No tsconfig.json was found for this file.',
|
|
311
|
+
hint: "Run 'soundscript init' to create a new project, or add a nearby tsconfig.json or tsconfig.soundscript.json.",
|
|
312
|
+
filePath,
|
|
313
|
+
line: 1,
|
|
314
|
+
column: 1,
|
|
315
|
+
endLine: 1,
|
|
316
|
+
endColumn: 1,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function createAnalysisFailureDiagnostic(filePath, error) {
|
|
320
|
+
return {
|
|
321
|
+
source: 'cli',
|
|
322
|
+
code: 'SOUNDSCRIPT_ANALYSIS_ERROR',
|
|
323
|
+
category: 'error',
|
|
324
|
+
message: 'soundscript could not analyze this file.',
|
|
325
|
+
notes: [error instanceof Error ? error.message : String(error)],
|
|
326
|
+
hint: 'Check your project configuration and restart the language server if the problem persists.',
|
|
327
|
+
filePath,
|
|
328
|
+
line: 1,
|
|
329
|
+
column: 1,
|
|
330
|
+
endLine: 1,
|
|
331
|
+
endColumn: 1,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function findDeepestNodeContainingPosition(node, position) {
|
|
335
|
+
if (position < node.getFullStart() || position >= node.getEnd()) {
|
|
336
|
+
return undefined;
|
|
337
|
+
}
|
|
338
|
+
const child = ts.forEachChild(node, (currentChild) => findDeepestNodeContainingPosition(currentChild, position));
|
|
339
|
+
return child ?? node;
|
|
340
|
+
}
|
|
341
|
+
function findCompletionNode(sourceFile, position) {
|
|
342
|
+
const candidatePositions = [
|
|
343
|
+
...new Set([
|
|
344
|
+
position,
|
|
345
|
+
Math.max(0, position - 1),
|
|
346
|
+
]),
|
|
347
|
+
];
|
|
348
|
+
for (const candidatePosition of candidatePositions) {
|
|
349
|
+
const node = findDeepestNodeContainingPosition(sourceFile, candidatePosition);
|
|
350
|
+
if (node && !ts.isSourceFile(node) && node.kind !== ts.SyntaxKind.EndOfFileToken) {
|
|
351
|
+
return node;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
function findCallLikeContainingPosition(node, position) {
|
|
357
|
+
let current = node;
|
|
358
|
+
while (current) {
|
|
359
|
+
if ((ts.isCallExpression(current) || ts.isNewExpression(current)) && current.arguments) {
|
|
360
|
+
const openParen = current.expression.getEnd();
|
|
361
|
+
const closeParen = current.getEnd() - 1;
|
|
362
|
+
if (position >= openParen && position <= closeParen) {
|
|
363
|
+
return current;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
current = current.parent;
|
|
367
|
+
}
|
|
368
|
+
return undefined;
|
|
369
|
+
}
|
|
370
|
+
function activeParameterForCallLike(callLike, position) {
|
|
371
|
+
const argumentsArray = callLike.arguments ?? [];
|
|
372
|
+
let activeParameter = 0;
|
|
373
|
+
for (const argument of argumentsArray) {
|
|
374
|
+
if (position > argument.getEnd()) {
|
|
375
|
+
activeParameter += 1;
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
return Math.min(activeParameter, Math.max(argumentsArray.length - 1, 0));
|
|
381
|
+
}
|
|
382
|
+
function parameterLabelForSignature(checker, parameter, callLike) {
|
|
383
|
+
const declaration = parameter.valueDeclaration ?? parameter.declarations?.[0];
|
|
384
|
+
const name = parameter.getName();
|
|
385
|
+
const isRest = declaration !== undefined && ts.isParameter(declaration) &&
|
|
386
|
+
declaration.dotDotDotToken !== undefined;
|
|
387
|
+
const isOptional = declaration !== undefined &&
|
|
388
|
+
ts.isParameter(declaration) &&
|
|
389
|
+
(declaration.questionToken !== undefined || declaration.initializer !== undefined);
|
|
390
|
+
const parameterType = checker.getTypeOfSymbolAtLocation(parameter, callLike);
|
|
391
|
+
return `${isRest ? '...' : ''}${name}${isOptional ? '?' : ''}: ${normalizeSurfaceTypeDisplayText(checker.typeToString(parameterType))}`;
|
|
392
|
+
}
|
|
393
|
+
function signatureHelpForNode(checker, node, position) {
|
|
394
|
+
const callLike = findCallLikeContainingPosition(node, position);
|
|
395
|
+
if (!callLike) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
const signature = checker.getResolvedSignature(callLike);
|
|
399
|
+
if (!signature) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
const parameters = signature.getParameters();
|
|
403
|
+
const activeParameter = activeParameterForCallLike(callLike, position);
|
|
404
|
+
const signatureKind = ts.isNewExpression(callLike)
|
|
405
|
+
? ts.SignatureKind.Construct
|
|
406
|
+
: ts.SignatureKind.Call;
|
|
407
|
+
const calleeLabel = ts.isNewExpression(callLike)
|
|
408
|
+
? `new ${callLike.expression.getText(callLike.getSourceFile())}`
|
|
409
|
+
: callLike.expression.getText(callLike.getSourceFile());
|
|
410
|
+
return {
|
|
411
|
+
activeParameter,
|
|
412
|
+
activeSignature: 0,
|
|
413
|
+
signatures: [{
|
|
414
|
+
label: `${calleeLabel}${normalizeSurfaceTypeDisplayText(checker.signatureToString(signature, callLike, ts.TypeFormatFlags.NoTruncation, signatureKind))}`,
|
|
415
|
+
parameters: parameters.map((parameter) => ({
|
|
416
|
+
label: parameterLabelForSignature(checker, parameter, callLike),
|
|
417
|
+
})),
|
|
418
|
+
}],
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function macroSignatureOperandDisplayText(operand) {
|
|
422
|
+
if (operand.refinement) {
|
|
423
|
+
return operand.refinement.displayText;
|
|
424
|
+
}
|
|
425
|
+
switch (operand.kind) {
|
|
426
|
+
case 'expr':
|
|
427
|
+
return `<${operand.name}>`;
|
|
428
|
+
case 'template':
|
|
429
|
+
return '`...`';
|
|
430
|
+
case 'block':
|
|
431
|
+
return '{ ... }';
|
|
432
|
+
case 'decl':
|
|
433
|
+
return '<declaration>';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function macroSignatureParameterLabel(operand) {
|
|
437
|
+
return `${operand.name}${operand.optional ? '?' : ''}: ${macroSignatureOperandDisplayText(operand)}`;
|
|
438
|
+
}
|
|
439
|
+
function macroInvocationParameterSpans(invocation) {
|
|
440
|
+
const spans = invocation.argumentSpans.map((argument) => argument.span);
|
|
441
|
+
if (invocation.trailingBlockSpan) {
|
|
442
|
+
spans.push(invocation.trailingBlockSpan);
|
|
443
|
+
}
|
|
444
|
+
if (invocation.declarationSpan) {
|
|
445
|
+
spans.push(invocation.declarationSpan);
|
|
446
|
+
}
|
|
447
|
+
return spans;
|
|
448
|
+
}
|
|
449
|
+
function activeParameterForMacroInvocation(invocation, sourcePosition) {
|
|
450
|
+
const operandSpans = macroInvocationParameterSpans(invocation);
|
|
451
|
+
if (operandSpans.length === 0) {
|
|
452
|
+
return 0;
|
|
453
|
+
}
|
|
454
|
+
let activeParameter = 0;
|
|
455
|
+
for (let index = 0; index < operandSpans.length; index += 1) {
|
|
456
|
+
const span = operandSpans[index];
|
|
457
|
+
if (containsPosition(span.start, span.end, sourcePosition)) {
|
|
458
|
+
return index;
|
|
459
|
+
}
|
|
460
|
+
if (sourcePosition >= span.end) {
|
|
461
|
+
activeParameter = Math.min(index + 1, operandSpans.length - 1);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return activeParameter;
|
|
465
|
+
}
|
|
466
|
+
function signatureHelpForMacroDefinition(definition, invocation, sourcePosition, context) {
|
|
467
|
+
const signature = definition.signature;
|
|
468
|
+
if (!signature) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
const examples = formatMacroSignatureExamples(signature, invocation.nameText);
|
|
472
|
+
let activeSignature = 0;
|
|
473
|
+
if (context) {
|
|
474
|
+
const decoded = tryReadMacroSignature(signature, context);
|
|
475
|
+
if (decoded) {
|
|
476
|
+
const decodedIndex = signature.cases.findIndex((signatureCase) => signatureCase === decoded.signatureCase);
|
|
477
|
+
if (decodedIndex >= 0) {
|
|
478
|
+
activeSignature = decodedIndex;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
activeParameter: activeParameterForMacroInvocation(invocation, sourcePosition),
|
|
484
|
+
activeSignature,
|
|
485
|
+
signatures: signature.cases.map((signatureCase, index) => ({
|
|
486
|
+
label: examples[index],
|
|
487
|
+
parameters: signatureCase.operands.map((operand) => ({
|
|
488
|
+
label: macroSignatureParameterLabel(operand),
|
|
489
|
+
})),
|
|
490
|
+
})),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function createHoverRange(start, end, originalText) {
|
|
494
|
+
const startPosition = getLineAndCharacterOfPosition(originalText, start);
|
|
495
|
+
const endPosition = getLineAndCharacterOfPosition(originalText, end);
|
|
496
|
+
return {
|
|
497
|
+
start: { line: startPosition.line, character: startPosition.character },
|
|
498
|
+
end: { line: endPosition.line, character: endPosition.character },
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function createLocationKey(location) {
|
|
502
|
+
return `${location.uri}:${location.range.start.line}:${location.range.start.character}:${location.range.end.line}:${location.range.end.character}`;
|
|
503
|
+
}
|
|
504
|
+
function createRangeFromOffsets(start, end, text) {
|
|
505
|
+
const safeEnd = Math.max(start, end);
|
|
506
|
+
return createHoverRange(start, safeEnd, text);
|
|
507
|
+
}
|
|
508
|
+
function rangesEqual(left, right) {
|
|
509
|
+
return left.start.line === right.start.line &&
|
|
510
|
+
left.start.character === right.start.character &&
|
|
511
|
+
left.end.line === right.end.line &&
|
|
512
|
+
left.end.character === right.end.character;
|
|
513
|
+
}
|
|
514
|
+
function createRangeForNode(preparedProject, sourceFile, node) {
|
|
515
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
516
|
+
const preparedFile = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
|
|
517
|
+
if (preparedFile) {
|
|
518
|
+
const mappedRange = mapProgramEnclosingRangeToSource(preparedFile, node.getStart(sourceFile), node.getEnd());
|
|
519
|
+
return createRangeFromOffsets(mappedRange.start, mappedRange.end, preparedFile.originalText);
|
|
520
|
+
}
|
|
521
|
+
const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
522
|
+
const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
523
|
+
return {
|
|
524
|
+
start: { line: start.line, character: start.character },
|
|
525
|
+
end: { line: end.line, character: end.character },
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function createMarkdownCodeBlock(value, language = 'ts') {
|
|
529
|
+
return `\`\`\`${language}\n${value}\n\`\`\``;
|
|
530
|
+
}
|
|
531
|
+
function createMarkdownHoverContents(code, details = []) {
|
|
532
|
+
const markdownParts = [createMarkdownCodeBlock(code)];
|
|
533
|
+
if (details.length > 0) {
|
|
534
|
+
markdownParts.push(...details);
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
kind: 'markdown',
|
|
538
|
+
value: markdownParts.join('\n\n'),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function createMarkdownTextHoverContents(summary, details = []) {
|
|
542
|
+
return {
|
|
543
|
+
kind: 'markdown',
|
|
544
|
+
value: [summary, ...details].join('\n\n'),
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function normalizeSurfaceTypeDisplayText(text) {
|
|
548
|
+
return text.replace(/\b[A-Za-z_$][\w$]*\b/gu, (name) => {
|
|
549
|
+
if (isElaboratedF64TypeImportName(name)) {
|
|
550
|
+
return 'number';
|
|
551
|
+
}
|
|
552
|
+
if (isElaboratedBigIntTypeImportName(name)) {
|
|
553
|
+
return 'bigint';
|
|
554
|
+
}
|
|
555
|
+
return name;
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function typeToDisplayString(checker, node) {
|
|
559
|
+
const type = checker.getTypeAtLocation(node);
|
|
560
|
+
return normalizeSurfaceTypeDisplayText(checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation));
|
|
561
|
+
}
|
|
562
|
+
function variableKeywordForDeclaration(node) {
|
|
563
|
+
if ((node.parent.flags & ts.NodeFlags.Const) !== 0) {
|
|
564
|
+
return 'const';
|
|
565
|
+
}
|
|
566
|
+
if ((node.parent.flags & ts.NodeFlags.Let) !== 0) {
|
|
567
|
+
return 'let';
|
|
568
|
+
}
|
|
569
|
+
return 'var';
|
|
570
|
+
}
|
|
571
|
+
function formatSignatureHover(checker, node, symbol, labelPrefix, signatureKind) {
|
|
572
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
|
|
573
|
+
const [signature] = checker.getSignaturesOfType(type, signatureKind);
|
|
574
|
+
if (!signature) {
|
|
575
|
+
return undefined;
|
|
576
|
+
}
|
|
577
|
+
return `${labelPrefix}${normalizeSurfaceTypeDisplayText(checker.signatureToString(signature, node, ts.TypeFormatFlags.NoTruncation, signatureKind))}`;
|
|
578
|
+
}
|
|
579
|
+
function formatSymbolHoverCode(checker, node) {
|
|
580
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
581
|
+
if (!symbol) {
|
|
582
|
+
const typeText = typeToDisplayString(checker, node);
|
|
583
|
+
return typeText.length > 0 ? typeText : null;
|
|
584
|
+
}
|
|
585
|
+
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
586
|
+
const name = symbol.getName();
|
|
587
|
+
if (!declaration || !isUserFacingSymbolName(name)) {
|
|
588
|
+
const typeText = typeToDisplayString(checker, node);
|
|
589
|
+
return typeText.length > 0 ? typeText : null;
|
|
590
|
+
}
|
|
591
|
+
const displayType = typeToDisplayString(checker, node);
|
|
592
|
+
if (ts.isVariableDeclaration(declaration)) {
|
|
593
|
+
return `${variableKeywordForDeclaration(declaration)} ${name}: ${displayType}`;
|
|
594
|
+
}
|
|
595
|
+
if (ts.isParameter(declaration)) {
|
|
596
|
+
return `${declaration.dotDotDotToken ? '...' : ''}${name}: ${displayType}`;
|
|
597
|
+
}
|
|
598
|
+
if (ts.isPropertyDeclaration(declaration) || ts.isPropertySignature(declaration)) {
|
|
599
|
+
const readonlyPrefix = isReadonlyDeclaration(declaration) ? 'readonly ' : '';
|
|
600
|
+
return `${readonlyPrefix}${name}: ${displayType}`;
|
|
601
|
+
}
|
|
602
|
+
if (ts.isFunctionDeclaration(declaration)) {
|
|
603
|
+
return formatSignatureHover(checker, node, symbol, `function ${name}`, ts.SignatureKind.Call) ??
|
|
604
|
+
`function ${name}: ${displayType}`;
|
|
605
|
+
}
|
|
606
|
+
if (ts.isMethodDeclaration(declaration) ||
|
|
607
|
+
ts.isMethodSignature(declaration) ||
|
|
608
|
+
ts.isGetAccessorDeclaration(declaration) ||
|
|
609
|
+
ts.isSetAccessorDeclaration(declaration)) {
|
|
610
|
+
return formatSignatureHover(checker, node, symbol, name, ts.SignatureKind.Call) ??
|
|
611
|
+
`${name}: ${displayType}`;
|
|
612
|
+
}
|
|
613
|
+
if (ts.isConstructorDeclaration(declaration)) {
|
|
614
|
+
return formatSignatureHover(checker, node, symbol, 'new ', ts.SignatureKind.Construct) ??
|
|
615
|
+
`new ${name}`;
|
|
616
|
+
}
|
|
617
|
+
if (ts.isClassDeclaration(declaration)) {
|
|
618
|
+
return `class ${name}`;
|
|
619
|
+
}
|
|
620
|
+
if (ts.isInterfaceDeclaration(declaration)) {
|
|
621
|
+
return `interface ${name}`;
|
|
622
|
+
}
|
|
623
|
+
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
624
|
+
const declaredType = checker.getDeclaredTypeOfSymbol(symbol);
|
|
625
|
+
return `type ${name} = ${normalizeSurfaceTypeDisplayText(checker.typeToString(declaredType, node, ts.TypeFormatFlags.NoTruncation))}`;
|
|
626
|
+
}
|
|
627
|
+
if (ts.isEnumDeclaration(declaration)) {
|
|
628
|
+
return `enum ${name}`;
|
|
629
|
+
}
|
|
630
|
+
if (ts.isEnumMember(declaration)) {
|
|
631
|
+
return `${name} = ${typeToDisplayString(checker, declaration.name)}`;
|
|
632
|
+
}
|
|
633
|
+
if (ts.isModuleDeclaration(declaration)) {
|
|
634
|
+
return `namespace ${name}`;
|
|
635
|
+
}
|
|
636
|
+
const typeText = displayType;
|
|
637
|
+
return typeText.length > 0 ? typeText : null;
|
|
638
|
+
}
|
|
639
|
+
function createMacroSummaryHover(invocation, originalText, details = []) {
|
|
640
|
+
return {
|
|
641
|
+
contents: createMarkdownTextHoverContents(`**macro** \`${invocation.nameText}\``, details),
|
|
642
|
+
range: createHoverRange(invocation.nameSpan.start, invocation.nameSpan.end, originalText),
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function createCustomMacroSummaryContents(macroName, body, details = []) {
|
|
646
|
+
const trimmedBody = body.trim();
|
|
647
|
+
if (trimmedBody.startsWith('**macro** `')) {
|
|
648
|
+
const value = details.length > 0 && !body.includes('Accepted forms:')
|
|
649
|
+
? `${body}\n\n${details.join('\n\n')}`
|
|
650
|
+
: body;
|
|
651
|
+
return {
|
|
652
|
+
kind: 'markdown',
|
|
653
|
+
value,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
return createMarkdownTextHoverContents(`**macro** \`${macroName}\``, [
|
|
657
|
+
body,
|
|
658
|
+
...details,
|
|
659
|
+
]);
|
|
660
|
+
}
|
|
661
|
+
function createCustomMacroSummaryHover(invocation, originalText, body, details = []) {
|
|
662
|
+
return {
|
|
663
|
+
contents: createCustomMacroSummaryContents(invocation.nameText, body, details),
|
|
664
|
+
range: createHoverRange(invocation.nameSpan.start, invocation.nameSpan.end, originalText),
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function signatureHoverDetails(definition, macroName) {
|
|
668
|
+
if (!definition.signature) {
|
|
669
|
+
return [];
|
|
670
|
+
}
|
|
671
|
+
const sections = [
|
|
672
|
+
[
|
|
673
|
+
'Accepted forms:',
|
|
674
|
+
...formatMacroSignatureExamples(definition.signature, macroName).map((example) => createMarkdownCodeBlock(example)),
|
|
675
|
+
].join('\n'),
|
|
676
|
+
];
|
|
677
|
+
const operandDescriptions = new Map();
|
|
678
|
+
for (const signatureCase of definition.signature.cases) {
|
|
679
|
+
for (const operand of signatureCase.operands) {
|
|
680
|
+
if (operand.description && !operandDescriptions.has(operand.name)) {
|
|
681
|
+
operandDescriptions.set(operand.name, operand.description);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (operandDescriptions.size > 0) {
|
|
686
|
+
sections.push([
|
|
687
|
+
'Operands:',
|
|
688
|
+
...[...operandDescriptions.entries()].map(([name, description]) => `- \`${name}\`: ${description}`),
|
|
689
|
+
].join('\n'));
|
|
690
|
+
}
|
|
691
|
+
return sections;
|
|
692
|
+
}
|
|
693
|
+
function getInvocationBlockSpan(invocation) {
|
|
694
|
+
if (invocation.trailingBlockSpan) {
|
|
695
|
+
return invocation.trailingBlockSpan;
|
|
696
|
+
}
|
|
697
|
+
if (invocation.invocationKind === 'block') {
|
|
698
|
+
const [firstArgument] = invocation.argumentSpans;
|
|
699
|
+
if (firstArgument?.kind === 'BlockArg') {
|
|
700
|
+
return firstArgument.span;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return undefined;
|
|
704
|
+
}
|
|
705
|
+
function isIdentifierPart(character) {
|
|
706
|
+
return character !== undefined && /[\p{ID_Continue}_$\u200C\u200D]/u.test(character);
|
|
707
|
+
}
|
|
708
|
+
function createTokenHoverRange(position, originalText) {
|
|
709
|
+
let start = position;
|
|
710
|
+
let end = position + 1;
|
|
711
|
+
while (start > 0 && isIdentifierPart(originalText[start - 1])) {
|
|
712
|
+
start -= 1;
|
|
713
|
+
}
|
|
714
|
+
while (end < originalText.length && isIdentifierPart(originalText[end])) {
|
|
715
|
+
end += 1;
|
|
716
|
+
}
|
|
717
|
+
return createHoverRange(start, end, originalText);
|
|
718
|
+
}
|
|
719
|
+
function containsPosition(start, end, position) {
|
|
720
|
+
return position >= start && position < end;
|
|
721
|
+
}
|
|
722
|
+
function getIdentifierPrefixAtPosition(text, position) {
|
|
723
|
+
let start = position;
|
|
724
|
+
while (start > 0 && isIdentifierPart(text[start - 1])) {
|
|
725
|
+
start -= 1;
|
|
726
|
+
}
|
|
727
|
+
return text.slice(start, position);
|
|
728
|
+
}
|
|
729
|
+
function isUserFacingSymbolName(name) {
|
|
730
|
+
return !name.startsWith('__@') && !name.startsWith('__sts_');
|
|
731
|
+
}
|
|
732
|
+
function isReadonlyDeclaration(node) {
|
|
733
|
+
if (ts.isVariableDeclaration(node)) {
|
|
734
|
+
return (node.parent.flags & ts.NodeFlags.Const) !== 0;
|
|
735
|
+
}
|
|
736
|
+
if (ts.isPropertyDeclaration(node) ||
|
|
737
|
+
ts.isPropertySignature(node) ||
|
|
738
|
+
ts.isParameter(node)) {
|
|
739
|
+
return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ReadonlyKeyword) ??
|
|
740
|
+
false;
|
|
741
|
+
}
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
function getDeclarationNameNode(node) {
|
|
745
|
+
if (ts.isVariableDeclaration(node)) {
|
|
746
|
+
return node.name;
|
|
747
|
+
}
|
|
748
|
+
if (ts.isParameter(node) || ts.isBindingElement(node)) {
|
|
749
|
+
return node.name;
|
|
750
|
+
}
|
|
751
|
+
if (ts.isFunctionDeclaration(node) ||
|
|
752
|
+
ts.isClassDeclaration(node) ||
|
|
753
|
+
ts.isMethodDeclaration(node) ||
|
|
754
|
+
ts.isPropertyDeclaration(node) ||
|
|
755
|
+
ts.isInterfaceDeclaration(node) ||
|
|
756
|
+
ts.isTypeAliasDeclaration(node) ||
|
|
757
|
+
ts.isEnumDeclaration(node) ||
|
|
758
|
+
ts.isEnumMember(node) ||
|
|
759
|
+
ts.isModuleDeclaration(node) ||
|
|
760
|
+
ts.isPropertySignature(node) ||
|
|
761
|
+
ts.isMethodSignature(node) ||
|
|
762
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
763
|
+
ts.isSetAccessorDeclaration(node)) {
|
|
764
|
+
return node.name;
|
|
765
|
+
}
|
|
766
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
767
|
+
return node;
|
|
768
|
+
}
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
function getDeclarationNameText(node) {
|
|
772
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
773
|
+
return 'constructor';
|
|
774
|
+
}
|
|
775
|
+
const nameNode = getDeclarationNameNode(node);
|
|
776
|
+
if (!nameNode) {
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
if (ts.isIdentifier(nameNode) || ts.isPrivateIdentifier(nameNode)) {
|
|
780
|
+
return nameNode.text;
|
|
781
|
+
}
|
|
782
|
+
if (ts.isStringLiteral(nameNode) || ts.isNumericLiteral(nameNode)) {
|
|
783
|
+
return nameNode.text;
|
|
784
|
+
}
|
|
785
|
+
if (ts.isComputedPropertyName(nameNode)) {
|
|
786
|
+
return nameNode.getText(nameNode.getSourceFile());
|
|
787
|
+
}
|
|
788
|
+
return nameNode.getText(nameNode.getSourceFile());
|
|
789
|
+
}
|
|
790
|
+
function documentSymbolKindForNode(node) {
|
|
791
|
+
if (ts.isVariableDeclaration(node)) {
|
|
792
|
+
const declarationList = node.parent;
|
|
793
|
+
return (declarationList.flags & ts.NodeFlags.Const) !== 0 ? 14 : 13;
|
|
794
|
+
}
|
|
795
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
796
|
+
return 12;
|
|
797
|
+
}
|
|
798
|
+
if (ts.isClassDeclaration(node)) {
|
|
799
|
+
return 5;
|
|
800
|
+
}
|
|
801
|
+
if (ts.isMethodDeclaration(node) ||
|
|
802
|
+
ts.isMethodSignature(node) ||
|
|
803
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
804
|
+
ts.isSetAccessorDeclaration(node)) {
|
|
805
|
+
return 6;
|
|
806
|
+
}
|
|
807
|
+
if (ts.isPropertyDeclaration(node)) {
|
|
808
|
+
return 8;
|
|
809
|
+
}
|
|
810
|
+
if (ts.isPropertySignature(node)) {
|
|
811
|
+
return 7;
|
|
812
|
+
}
|
|
813
|
+
if (ts.isConstructorDeclaration(node)) {
|
|
814
|
+
return 9;
|
|
815
|
+
}
|
|
816
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
817
|
+
return 11;
|
|
818
|
+
}
|
|
819
|
+
if (ts.isEnumDeclaration(node)) {
|
|
820
|
+
return 10;
|
|
821
|
+
}
|
|
822
|
+
if (ts.isEnumMember(node)) {
|
|
823
|
+
return 22;
|
|
824
|
+
}
|
|
825
|
+
if (ts.isModuleDeclaration(node)) {
|
|
826
|
+
return 2;
|
|
827
|
+
}
|
|
828
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
829
|
+
return 13;
|
|
830
|
+
}
|
|
831
|
+
return undefined;
|
|
832
|
+
}
|
|
833
|
+
function semanticTokenTypeForNode(node) {
|
|
834
|
+
if (ts.isBindingElement(node)) {
|
|
835
|
+
let current = node.parent;
|
|
836
|
+
while (current) {
|
|
837
|
+
if (ts.isParameter(current)) {
|
|
838
|
+
return 'parameter';
|
|
839
|
+
}
|
|
840
|
+
if (ts.isVariableDeclaration(current)) {
|
|
841
|
+
return 'variable';
|
|
842
|
+
}
|
|
843
|
+
current = current.parent;
|
|
844
|
+
}
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
if (ts.isVariableDeclaration(node)) {
|
|
848
|
+
return 'variable';
|
|
849
|
+
}
|
|
850
|
+
if (ts.isFunctionDeclaration(node)) {
|
|
851
|
+
return 'function';
|
|
852
|
+
}
|
|
853
|
+
if (ts.isClassDeclaration(node)) {
|
|
854
|
+
return 'class';
|
|
855
|
+
}
|
|
856
|
+
if (ts.isMethodDeclaration(node) ||
|
|
857
|
+
ts.isMethodSignature(node) ||
|
|
858
|
+
ts.isGetAccessorDeclaration(node) ||
|
|
859
|
+
ts.isSetAccessorDeclaration(node)) {
|
|
860
|
+
return 'method';
|
|
861
|
+
}
|
|
862
|
+
if (ts.isPropertyDeclaration(node) || ts.isPropertySignature(node)) {
|
|
863
|
+
return 'property';
|
|
864
|
+
}
|
|
865
|
+
if (ts.isParameter(node)) {
|
|
866
|
+
return 'parameter';
|
|
867
|
+
}
|
|
868
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
869
|
+
return 'interface';
|
|
870
|
+
}
|
|
871
|
+
if (ts.isEnumDeclaration(node)) {
|
|
872
|
+
return 'enum';
|
|
873
|
+
}
|
|
874
|
+
if (ts.isEnumMember(node)) {
|
|
875
|
+
return 'enumMember';
|
|
876
|
+
}
|
|
877
|
+
if (ts.isModuleDeclaration(node)) {
|
|
878
|
+
return 'namespace';
|
|
879
|
+
}
|
|
880
|
+
if (ts.isTypeAliasDeclaration(node) || ts.isTypeParameterDeclaration(node)) {
|
|
881
|
+
return 'type';
|
|
882
|
+
}
|
|
883
|
+
return undefined;
|
|
884
|
+
}
|
|
885
|
+
function declarationNodeForSemanticToken(checker, node) {
|
|
886
|
+
const nameNode = getDeclarationNameNode(node.parent);
|
|
887
|
+
if (nameNode === node) {
|
|
888
|
+
return node.parent;
|
|
889
|
+
}
|
|
890
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
891
|
+
if (!symbol) {
|
|
892
|
+
return undefined;
|
|
893
|
+
}
|
|
894
|
+
return symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
895
|
+
}
|
|
896
|
+
function semanticTokenClassification(checker, node) {
|
|
897
|
+
if (ts.isIdentifier(node)) {
|
|
898
|
+
const parent = node.parent;
|
|
899
|
+
if ((ts.isImportSpecifier(parent) && parent.name === node && !parent.isTypeOnly) ||
|
|
900
|
+
ts.isNamespaceImport(parent) ||
|
|
901
|
+
(ts.isImportClause(parent) && parent.name === node && !parent.isTypeOnly)) {
|
|
902
|
+
return {
|
|
903
|
+
type: 'variable',
|
|
904
|
+
modifiers: ['declaration', 'readonly'],
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const declarationNode = declarationNodeForSemanticToken(checker, node);
|
|
909
|
+
if (!declarationNode) {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
const type = semanticTokenTypeForNode(declarationNode);
|
|
913
|
+
if (!type) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
const modifiers = [];
|
|
917
|
+
if (getDeclarationNameNode(declarationNode) === node) {
|
|
918
|
+
modifiers.push('declaration');
|
|
919
|
+
}
|
|
920
|
+
if (isReadonlyDeclaration(declarationNode)) {
|
|
921
|
+
modifiers.push('readonly');
|
|
922
|
+
}
|
|
923
|
+
return { type, modifiers };
|
|
924
|
+
}
|
|
925
|
+
function encodeSemanticTokens(tokens) {
|
|
926
|
+
const sortedTokens = [...tokens].sort((left, right) => left.line - right.line || left.startCharacter - right.startCharacter ||
|
|
927
|
+
left.length - right.length);
|
|
928
|
+
const data = [];
|
|
929
|
+
let previousLine = 0;
|
|
930
|
+
let previousStartCharacter = 0;
|
|
931
|
+
for (const token of sortedTokens) {
|
|
932
|
+
const deltaLine = token.line - previousLine;
|
|
933
|
+
const deltaStart = deltaLine === 0
|
|
934
|
+
? token.startCharacter - previousStartCharacter
|
|
935
|
+
: token.startCharacter;
|
|
936
|
+
const tokenTypeIndex = SEMANTIC_TOKEN_TYPE_INDICES.get(token.tokenType);
|
|
937
|
+
if (tokenTypeIndex === undefined) {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
const modifierMask = token.modifiers.reduce((mask, modifier) => {
|
|
941
|
+
const modifierIndex = SEMANTIC_TOKEN_MODIFIER_INDICES.get(modifier);
|
|
942
|
+
return modifierIndex === undefined ? mask : mask | (1 << modifierIndex);
|
|
943
|
+
}, 0);
|
|
944
|
+
data.push(deltaLine, deltaStart, token.length, tokenTypeIndex, modifierMask);
|
|
945
|
+
previousLine = token.line;
|
|
946
|
+
previousStartCharacter = token.startCharacter;
|
|
947
|
+
}
|
|
948
|
+
return { data };
|
|
949
|
+
}
|
|
950
|
+
function pushSemanticTokenForRange(tokens, text, start, end, tokenType, modifiers) {
|
|
951
|
+
if (end <= start) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
const startPosition = getLineAndCharacterOfPosition(text, start);
|
|
955
|
+
const endPosition = getLineAndCharacterOfPosition(text, end);
|
|
956
|
+
if (startPosition.line !== endPosition.line) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
tokens.push({
|
|
960
|
+
line: startPosition.line,
|
|
961
|
+
startCharacter: startPosition.character,
|
|
962
|
+
length: end - start,
|
|
963
|
+
tokenType,
|
|
964
|
+
modifiers,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
function pushMacroSemanticTokens(tokens, originalText, semanticTokens) {
|
|
968
|
+
for (const semanticToken of semanticTokens) {
|
|
969
|
+
pushSemanticTokenForRange(tokens, originalText, semanticToken.span.start, semanticToken.span.end, semanticToken.type, [...(semanticToken.modifiers ?? [])]);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function collectSemanticTokensFromSourceFile(preparedProject, sourceFile, checker) {
|
|
973
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
974
|
+
const preparedFile = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
|
|
975
|
+
if (!preparedFile) {
|
|
976
|
+
return { data: [] };
|
|
977
|
+
}
|
|
978
|
+
const preparedSourceFile = preparedFile;
|
|
979
|
+
const filePath = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
980
|
+
const tokens = [];
|
|
981
|
+
function visit(node) {
|
|
982
|
+
if (ts.isIdentifier(node) || ts.isPrivateIdentifier(node)) {
|
|
983
|
+
const name = node.text;
|
|
984
|
+
if (isUserFacingSymbolName(name)) {
|
|
985
|
+
const classification = semanticTokenClassification(checker, node);
|
|
986
|
+
if (classification) {
|
|
987
|
+
const mappedRange = mapProgramRangeToSource(preparedSourceFile, node.getStart(sourceFile), node.getEnd());
|
|
988
|
+
if (!mappedRange.intersectsReplacement && mappedRange.end > mappedRange.start) {
|
|
989
|
+
pushSemanticTokenForRange(tokens, preparedSourceFile.originalText, mappedRange.start, mappedRange.end, classification.type, classification.modifiers);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
ts.forEachChild(node, visit);
|
|
995
|
+
}
|
|
996
|
+
visit(sourceFile);
|
|
997
|
+
const scanResult = scanMacroCandidates(sourceFileName, preparedFile.originalText);
|
|
998
|
+
for (const hash of scanResult.hashes) {
|
|
999
|
+
if (hash.kind !== 'macro-start') {
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
const parsed = parseMacroInvocationAt(sourceFileName, preparedFile.originalText, hash.span.start);
|
|
1003
|
+
if ('reason' in parsed) {
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
pushSemanticTokenForRange(tokens, preparedFile.originalText, parsed.nameSpan.start, parsed.nameSpan.end, 'function', []);
|
|
1007
|
+
}
|
|
1008
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject).filter((entry) => entry.sourceFile.fileName === sourceFile.fileName);
|
|
1009
|
+
for (const match of collected) {
|
|
1010
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
1011
|
+
if (!artifacts) {
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
const semanticTokens = [
|
|
1015
|
+
...artifacts.fragments.flatMap((fragment) => fragment.semanticTokens ?? []),
|
|
1016
|
+
...(artifacts.definition.semanticTokens && artifacts.node
|
|
1017
|
+
? artifacts.definition.semanticTokens({ node: artifacts.node })
|
|
1018
|
+
: []),
|
|
1019
|
+
];
|
|
1020
|
+
if (semanticTokens.length === 0) {
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
pushMacroSemanticTokens(tokens, preparedFile.originalText, semanticTokens);
|
|
1024
|
+
}
|
|
1025
|
+
return encodeSemanticTokens(tokens);
|
|
1026
|
+
}
|
|
1027
|
+
function findSourceRootDirectory(filePath) {
|
|
1028
|
+
let currentDirectory = dirname(filePath);
|
|
1029
|
+
while (true) {
|
|
1030
|
+
if (currentDirectory.endsWith('/src') || currentDirectory === 'src') {
|
|
1031
|
+
return currentDirectory;
|
|
1032
|
+
}
|
|
1033
|
+
const parentDirectory = dirname(currentDirectory);
|
|
1034
|
+
if (parentDirectory === currentDirectory) {
|
|
1035
|
+
return undefined;
|
|
1036
|
+
}
|
|
1037
|
+
currentDirectory = parentDirectory;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function createStarterTsconfigText(filePath) {
|
|
1041
|
+
const sourceRootDirectory = findSourceRootDirectory(filePath);
|
|
1042
|
+
const include = sourceRootDirectory ? ['src/**/*.ts', 'src/**/*.sts'] : ['**/*.ts', '**/*.sts'];
|
|
1043
|
+
return `${JSON.stringify({
|
|
1044
|
+
compilerOptions: {
|
|
1045
|
+
strict: true,
|
|
1046
|
+
noEmit: true,
|
|
1047
|
+
target: 'ES2022',
|
|
1048
|
+
module: 'ESNext',
|
|
1049
|
+
},
|
|
1050
|
+
include,
|
|
1051
|
+
}, null, 2)}\n`;
|
|
1052
|
+
}
|
|
1053
|
+
function suggestedProjectPathForFile(filePath) {
|
|
1054
|
+
const sourceRootDirectory = findSourceRootDirectory(filePath);
|
|
1055
|
+
const projectDirectory = sourceRootDirectory ? dirname(sourceRootDirectory) : dirname(filePath);
|
|
1056
|
+
return join(projectDirectory, 'tsconfig.json');
|
|
1057
|
+
}
|
|
1058
|
+
function readCodeActionDocumentText(uri, session) {
|
|
1059
|
+
const openDocument = session.get(uri);
|
|
1060
|
+
if (openDocument) {
|
|
1061
|
+
return openDocument.text;
|
|
1062
|
+
}
|
|
1063
|
+
const filePath = fromFileUrl(uri);
|
|
1064
|
+
try {
|
|
1065
|
+
return readTextFileSync(filePath);
|
|
1066
|
+
}
|
|
1067
|
+
catch {
|
|
1068
|
+
return undefined;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
function documentLineTexts(text) {
|
|
1072
|
+
return text.split('\n');
|
|
1073
|
+
}
|
|
1074
|
+
function lineIndentation(lineText) {
|
|
1075
|
+
const match = lineText.match(/^\s*/u);
|
|
1076
|
+
return match?.[0] ?? '';
|
|
1077
|
+
}
|
|
1078
|
+
function deleteWholeLineEdit(lines, line) {
|
|
1079
|
+
const lineText = lines[line];
|
|
1080
|
+
if (lineText === undefined) {
|
|
1081
|
+
return undefined;
|
|
1082
|
+
}
|
|
1083
|
+
if (line < lines.length - 1) {
|
|
1084
|
+
return {
|
|
1085
|
+
newText: '',
|
|
1086
|
+
range: {
|
|
1087
|
+
start: { line, character: 0 },
|
|
1088
|
+
end: { line: line + 1, character: 0 },
|
|
1089
|
+
},
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
newText: '',
|
|
1094
|
+
range: {
|
|
1095
|
+
start: { line, character: 0 },
|
|
1096
|
+
end: { line, character: lineText.length },
|
|
1097
|
+
},
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function isAnnotationCommentLine(lineText) {
|
|
1101
|
+
return /^\s*\/\/\s*#\[/u.test(lineText);
|
|
1102
|
+
}
|
|
1103
|
+
function annotationNameForLine(lineText) {
|
|
1104
|
+
const match = lineText.match(/^\s*\/\/\s*#\[([A-Za-z_$][\w$.-]*)/u);
|
|
1105
|
+
return match?.[1];
|
|
1106
|
+
}
|
|
1107
|
+
function diagnosticEvidenceValue(diagnostic, label) {
|
|
1108
|
+
return diagnostic.data?.metadata?.evidence?.find((fact) => fact.label === label)?.value;
|
|
1109
|
+
}
|
|
1110
|
+
function replaceLineEdit(lines, line, newText) {
|
|
1111
|
+
const lineText = lines[line];
|
|
1112
|
+
if (lineText === undefined) {
|
|
1113
|
+
return undefined;
|
|
1114
|
+
}
|
|
1115
|
+
return {
|
|
1116
|
+
newText,
|
|
1117
|
+
range: {
|
|
1118
|
+
start: { line, character: 0 },
|
|
1119
|
+
end: { line, character: lineText.length },
|
|
1120
|
+
},
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
function annotationLineReplacement(lineText, annotationName) {
|
|
1124
|
+
if (!isAnnotationCommentLine(lineText)) {
|
|
1125
|
+
return undefined;
|
|
1126
|
+
}
|
|
1127
|
+
const indentation = lineIndentation(lineText);
|
|
1128
|
+
return `${indentation}// #[${annotationName}]`;
|
|
1129
|
+
}
|
|
1130
|
+
function findAttachedAnnotationLine(lines, startLine, annotationName) {
|
|
1131
|
+
for (let line = Math.min(startLine, lines.length - 1); line >= 0; line -= 1) {
|
|
1132
|
+
const lineText = lines[line] ?? '';
|
|
1133
|
+
if (isAnnotationCommentLine(lineText)) {
|
|
1134
|
+
if (!annotationName || annotationNameForLine(lineText) === annotationName) {
|
|
1135
|
+
return line;
|
|
1136
|
+
}
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (line === startLine) {
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (lineText.trim() === '') {
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
return undefined;
|
|
1146
|
+
}
|
|
1147
|
+
return undefined;
|
|
1148
|
+
}
|
|
1149
|
+
function createRemoveUnknownAnnotationCodeAction(uri, diagnostic, text) {
|
|
1150
|
+
if (diagnostic.code !== 'SOUND1007') {
|
|
1151
|
+
return undefined;
|
|
1152
|
+
}
|
|
1153
|
+
const line = diagnostic.range?.start.line;
|
|
1154
|
+
if (line === undefined) {
|
|
1155
|
+
return undefined;
|
|
1156
|
+
}
|
|
1157
|
+
const lines = documentLineTexts(text);
|
|
1158
|
+
const lineText = lines[line];
|
|
1159
|
+
if (lineText === undefined || !isAnnotationCommentLine(lineText)) {
|
|
1160
|
+
return undefined;
|
|
1161
|
+
}
|
|
1162
|
+
const edit = deleteWholeLineEdit(lines, line);
|
|
1163
|
+
if (!edit) {
|
|
1164
|
+
return undefined;
|
|
1165
|
+
}
|
|
1166
|
+
return {
|
|
1167
|
+
title: 'Remove unknown annotation comment',
|
|
1168
|
+
kind: 'quickfix',
|
|
1169
|
+
edit: {
|
|
1170
|
+
changes: {
|
|
1171
|
+
[uri]: [edit],
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function createRemoveUnsupportedAnnotationArgumentsCodeAction(uri, diagnostic, text) {
|
|
1177
|
+
if (diagnostic.code !== 'SOUND1028') {
|
|
1178
|
+
return undefined;
|
|
1179
|
+
}
|
|
1180
|
+
const line = diagnostic.range?.start.line;
|
|
1181
|
+
if (line === undefined) {
|
|
1182
|
+
return undefined;
|
|
1183
|
+
}
|
|
1184
|
+
const lines = documentLineTexts(text);
|
|
1185
|
+
const lineText = lines[line];
|
|
1186
|
+
if (lineText === undefined) {
|
|
1187
|
+
return undefined;
|
|
1188
|
+
}
|
|
1189
|
+
const annotationName = diagnosticEvidenceValue(diagnostic, 'annotationName') ??
|
|
1190
|
+
annotationNameForLine(lineText);
|
|
1191
|
+
if (!annotationName) {
|
|
1192
|
+
return undefined;
|
|
1193
|
+
}
|
|
1194
|
+
const newText = annotationLineReplacement(lineText, annotationName);
|
|
1195
|
+
if (!newText || newText === lineText) {
|
|
1196
|
+
return undefined;
|
|
1197
|
+
}
|
|
1198
|
+
const edit = replaceLineEdit(lines, line, newText);
|
|
1199
|
+
if (!edit) {
|
|
1200
|
+
return undefined;
|
|
1201
|
+
}
|
|
1202
|
+
return {
|
|
1203
|
+
title: 'Remove unsupported annotation arguments',
|
|
1204
|
+
kind: 'quickfix',
|
|
1205
|
+
edit: {
|
|
1206
|
+
changes: {
|
|
1207
|
+
[uri]: [edit],
|
|
1208
|
+
},
|
|
1209
|
+
},
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
function createVarianceContractRewriteCodeAction(uri, diagnostic, text) {
|
|
1213
|
+
if (diagnostic.code !== 'SOUND1031' && diagnostic.code !== 'SOUND1032') {
|
|
1214
|
+
return undefined;
|
|
1215
|
+
}
|
|
1216
|
+
const replacementContract = diagnostic.data?.metadata?.secondarySymbol;
|
|
1217
|
+
if (!replacementContract || !replacementContract.startsWith('// #[variance(')) {
|
|
1218
|
+
return undefined;
|
|
1219
|
+
}
|
|
1220
|
+
const startLine = diagnostic.range?.start.line;
|
|
1221
|
+
if (startLine === undefined) {
|
|
1222
|
+
return undefined;
|
|
1223
|
+
}
|
|
1224
|
+
const lines = documentLineTexts(text);
|
|
1225
|
+
const annotationLine = findAttachedAnnotationLine(lines, startLine, 'variance');
|
|
1226
|
+
if (annotationLine === undefined) {
|
|
1227
|
+
return undefined;
|
|
1228
|
+
}
|
|
1229
|
+
const edit = replaceLineEdit(lines, annotationLine, replacementContract);
|
|
1230
|
+
if (!edit) {
|
|
1231
|
+
return undefined;
|
|
1232
|
+
}
|
|
1233
|
+
return {
|
|
1234
|
+
title: diagnostic.code === 'SOUND1031'
|
|
1235
|
+
? 'Rewrite checked variance contract'
|
|
1236
|
+
: 'Align checked variance contract',
|
|
1237
|
+
kind: 'quickfix',
|
|
1238
|
+
edit: {
|
|
1239
|
+
changes: {
|
|
1240
|
+
[uri]: [edit],
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function collectTopLevelBindingNames(sourceFile) {
|
|
1246
|
+
const names = new Set();
|
|
1247
|
+
const collectBindingName = (name) => {
|
|
1248
|
+
if (ts.isIdentifier(name)) {
|
|
1249
|
+
names.add(name.text);
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
for (const element of name.elements) {
|
|
1253
|
+
if (!ts.isOmittedExpression(element)) {
|
|
1254
|
+
collectBindingName(element.name);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
for (const statement of sourceFile.statements) {
|
|
1259
|
+
if (ts.isImportDeclaration(statement)) {
|
|
1260
|
+
if (statement.importClause?.name) {
|
|
1261
|
+
names.add(statement.importClause.name.text);
|
|
1262
|
+
}
|
|
1263
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
1264
|
+
if (namedBindings) {
|
|
1265
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
1266
|
+
names.add(namedBindings.name.text);
|
|
1267
|
+
}
|
|
1268
|
+
else {
|
|
1269
|
+
for (const element of namedBindings.elements) {
|
|
1270
|
+
names.add(element.name.text);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement) ||
|
|
1277
|
+
ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement) ||
|
|
1278
|
+
ts.isEnumDeclaration(statement)) {
|
|
1279
|
+
if (statement.name) {
|
|
1280
|
+
names.add(statement.name.text);
|
|
1281
|
+
}
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1284
|
+
if (ts.isVariableStatement(statement)) {
|
|
1285
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1286
|
+
collectBindingName(declaration.name);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return names;
|
|
1291
|
+
}
|
|
1292
|
+
function createUniqueMacroAlias(annotationName, sourceFile) {
|
|
1293
|
+
const baseAlias = `macro${annotationName[0]?.toUpperCase() ?? ''}${annotationName.slice(1)}`;
|
|
1294
|
+
const names = collectTopLevelBindingNames(sourceFile);
|
|
1295
|
+
let alias = baseAlias;
|
|
1296
|
+
let suffix = 2;
|
|
1297
|
+
while (names.has(alias)) {
|
|
1298
|
+
alias = `${baseAlias}${suffix}`;
|
|
1299
|
+
suffix += 1;
|
|
1300
|
+
}
|
|
1301
|
+
return alias;
|
|
1302
|
+
}
|
|
1303
|
+
function findNamedImportAliasEdit(filePath, text, importSpecifier, importedBinding, alias) {
|
|
1304
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1305
|
+
for (const statement of sourceFile.statements) {
|
|
1306
|
+
if (!ts.isImportDeclaration(statement)) {
|
|
1307
|
+
continue;
|
|
1308
|
+
}
|
|
1309
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier) ||
|
|
1310
|
+
statement.moduleSpecifier.text !== importSpecifier) {
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
1314
|
+
if (!namedBindings || !ts.isNamedImports(namedBindings)) {
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
for (const element of namedBindings.elements) {
|
|
1318
|
+
if (element.name.text !== importedBinding) {
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
const importStart = getLineAndCharacterOfPosition(text, element.getStart(sourceFile));
|
|
1322
|
+
const importEnd = getLineAndCharacterOfPosition(text, element.getEnd());
|
|
1323
|
+
const importedName = element.propertyName?.text ?? element.name.text;
|
|
1324
|
+
return {
|
|
1325
|
+
newText: `${importedName} as ${alias}`,
|
|
1326
|
+
range: {
|
|
1327
|
+
start: { line: importStart.line, character: importStart.character },
|
|
1328
|
+
end: { line: importEnd.line, character: importEnd.character },
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return undefined;
|
|
1334
|
+
}
|
|
1335
|
+
function createAliasReservedAnnotationMacroCodeAction(uri, filePath, diagnostic, text) {
|
|
1336
|
+
if (diagnostic.code !== 'SOUND1033') {
|
|
1337
|
+
return undefined;
|
|
1338
|
+
}
|
|
1339
|
+
const line = diagnostic.range?.start.line;
|
|
1340
|
+
if (line === undefined) {
|
|
1341
|
+
return undefined;
|
|
1342
|
+
}
|
|
1343
|
+
const annotationName = diagnosticEvidenceValue(diagnostic, 'annotationName');
|
|
1344
|
+
const importSpecifier = diagnosticEvidenceValue(diagnostic, 'importSpecifier');
|
|
1345
|
+
const importedBinding = diagnosticEvidenceValue(diagnostic, 'importedBinding');
|
|
1346
|
+
if (!annotationName || !importSpecifier || !importedBinding) {
|
|
1347
|
+
return undefined;
|
|
1348
|
+
}
|
|
1349
|
+
const lines = documentLineTexts(text);
|
|
1350
|
+
const lineText = lines[line];
|
|
1351
|
+
if (lineText === undefined || !isAnnotationCommentLine(lineText)) {
|
|
1352
|
+
return undefined;
|
|
1353
|
+
}
|
|
1354
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1355
|
+
const alias = createUniqueMacroAlias(annotationName, sourceFile);
|
|
1356
|
+
const importEdit = findNamedImportAliasEdit(filePath, text, importSpecifier, importedBinding, alias);
|
|
1357
|
+
if (!importEdit) {
|
|
1358
|
+
return undefined;
|
|
1359
|
+
}
|
|
1360
|
+
const annotationReplacement = annotationLineReplacement(lineText, alias);
|
|
1361
|
+
if (!annotationReplacement) {
|
|
1362
|
+
return undefined;
|
|
1363
|
+
}
|
|
1364
|
+
const annotationEdit = replaceLineEdit(lines, line, annotationReplacement);
|
|
1365
|
+
if (!annotationEdit) {
|
|
1366
|
+
return undefined;
|
|
1367
|
+
}
|
|
1368
|
+
return {
|
|
1369
|
+
title: 'Alias imported annotation macro',
|
|
1370
|
+
kind: 'quickfix',
|
|
1371
|
+
edit: {
|
|
1372
|
+
changes: {
|
|
1373
|
+
[uri]: [importEdit, annotationEdit],
|
|
1374
|
+
},
|
|
1375
|
+
},
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
function isDynamicImportCallExpression(node) {
|
|
1379
|
+
return !!node &&
|
|
1380
|
+
ts.isCallExpression(node) &&
|
|
1381
|
+
node.expression.kind === ts.SyntaxKind.ImportKeyword &&
|
|
1382
|
+
node.arguments.length === 1 &&
|
|
1383
|
+
ts.isStringLiteral(node.arguments[0]);
|
|
1384
|
+
}
|
|
1385
|
+
function isRequireCallExpression(node) {
|
|
1386
|
+
return !!node &&
|
|
1387
|
+
ts.isCallExpression(node) &&
|
|
1388
|
+
ts.isIdentifier(node.expression) &&
|
|
1389
|
+
node.expression.text === 'require' &&
|
|
1390
|
+
node.arguments.length === 1 &&
|
|
1391
|
+
ts.isStringLiteral(node.arguments[0]);
|
|
1392
|
+
}
|
|
1393
|
+
function unwrapAwaitedImportInitializer(node) {
|
|
1394
|
+
if (!node) {
|
|
1395
|
+
return undefined;
|
|
1396
|
+
}
|
|
1397
|
+
if (ts.isAwaitExpression(node)) {
|
|
1398
|
+
return node.expression;
|
|
1399
|
+
}
|
|
1400
|
+
return ts.isExpression(node) ? node : undefined;
|
|
1401
|
+
}
|
|
1402
|
+
function variableDeclarationInteropBoundaryLine(declaration, text, sourceFile) {
|
|
1403
|
+
const initializer = unwrapAwaitedImportInitializer(declaration.initializer);
|
|
1404
|
+
if (!isRequireCallExpression(initializer) && !isDynamicImportCallExpression(initializer)) {
|
|
1405
|
+
return undefined;
|
|
1406
|
+
}
|
|
1407
|
+
const statement = declaration.parent?.parent;
|
|
1408
|
+
const startNode = statement && ts.isVariableStatement(statement) ? statement : declaration;
|
|
1409
|
+
return getLineAndCharacterOfPosition(text, startNode.getStart(sourceFile)).line;
|
|
1410
|
+
}
|
|
1411
|
+
function collectBindingNames(name) {
|
|
1412
|
+
if (ts.isIdentifier(name)) {
|
|
1413
|
+
return [name.text];
|
|
1414
|
+
}
|
|
1415
|
+
const names = [];
|
|
1416
|
+
for (const element of name.elements) {
|
|
1417
|
+
if (!ts.isOmittedExpression(element)) {
|
|
1418
|
+
names.push(...collectBindingNames(element.name));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
return names;
|
|
1422
|
+
}
|
|
1423
|
+
function collectInteropBoundaryBindings(sourceFile, text) {
|
|
1424
|
+
const bindings = [];
|
|
1425
|
+
const addBinding = (name, startNode) => {
|
|
1426
|
+
const position = startNode.getStart(sourceFile);
|
|
1427
|
+
bindings.push({
|
|
1428
|
+
name,
|
|
1429
|
+
position,
|
|
1430
|
+
line: getLineAndCharacterOfPosition(text, position).line,
|
|
1431
|
+
});
|
|
1432
|
+
};
|
|
1433
|
+
const visit = (node) => {
|
|
1434
|
+
if (ts.isImportDeclaration(node) && !node.importClause?.isTypeOnly) {
|
|
1435
|
+
if (node.importClause?.name) {
|
|
1436
|
+
addBinding(node.importClause.name.text, node);
|
|
1437
|
+
}
|
|
1438
|
+
const namedBindings = node.importClause?.namedBindings;
|
|
1439
|
+
if (namedBindings) {
|
|
1440
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
1441
|
+
addBinding(namedBindings.name.text, node);
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
for (const element of namedBindings.elements) {
|
|
1445
|
+
if (!element.isTypeOnly) {
|
|
1446
|
+
addBinding(element.name.text, node);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (ts.isImportEqualsDeclaration(node)) {
|
|
1453
|
+
addBinding(node.name.text, node);
|
|
1454
|
+
}
|
|
1455
|
+
if (ts.isVariableDeclaration(node)) {
|
|
1456
|
+
const initializer = unwrapAwaitedImportInitializer(node.initializer);
|
|
1457
|
+
if (isRequireCallExpression(initializer) || isDynamicImportCallExpression(initializer)) {
|
|
1458
|
+
const startNode = node.parent?.parent && ts.isVariableStatement(node.parent.parent)
|
|
1459
|
+
? node.parent.parent
|
|
1460
|
+
: node;
|
|
1461
|
+
for (const name of collectBindingNames(node.name)) {
|
|
1462
|
+
addBinding(name, startNode);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
ts.forEachChild(node, visit);
|
|
1467
|
+
};
|
|
1468
|
+
visit(sourceFile);
|
|
1469
|
+
return bindings;
|
|
1470
|
+
}
|
|
1471
|
+
function bindingInteropBoundaryLine(sourceFile, text, node) {
|
|
1472
|
+
const bindings = collectInteropBoundaryBindings(sourceFile, text);
|
|
1473
|
+
if (bindings.length === 0) {
|
|
1474
|
+
return undefined;
|
|
1475
|
+
}
|
|
1476
|
+
const pickBoundaryLine = (name) => {
|
|
1477
|
+
const position = node.getStart(sourceFile);
|
|
1478
|
+
const candidates = bindings
|
|
1479
|
+
.filter((binding) => binding.name === name && binding.position <= position)
|
|
1480
|
+
.sort((left, right) => right.position - left.position);
|
|
1481
|
+
return candidates[0]?.line;
|
|
1482
|
+
};
|
|
1483
|
+
if (ts.isIdentifier(node)) {
|
|
1484
|
+
const directLine = pickBoundaryLine(node.text);
|
|
1485
|
+
if (directLine !== undefined) {
|
|
1486
|
+
return directLine;
|
|
1487
|
+
}
|
|
1488
|
+
const parent = node.parent;
|
|
1489
|
+
if (ts.isPropertyAccessExpression(parent) &&
|
|
1490
|
+
parent.name === node &&
|
|
1491
|
+
ts.isIdentifier(parent.expression)) {
|
|
1492
|
+
return pickBoundaryLine(parent.expression.text);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
|
|
1496
|
+
return pickBoundaryLine(node.expression.text);
|
|
1497
|
+
}
|
|
1498
|
+
return undefined;
|
|
1499
|
+
}
|
|
1500
|
+
function findInteropBoundaryStartLine(filePath, text, diagnostic, _session) {
|
|
1501
|
+
const start = diagnostic.range?.start;
|
|
1502
|
+
if (!start) {
|
|
1503
|
+
return undefined;
|
|
1504
|
+
}
|
|
1505
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1506
|
+
const sourcePosition = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1507
|
+
let current = findDeepestNodeContainingPosition(sourceFile, sourcePosition);
|
|
1508
|
+
const bindingLine = current ? bindingInteropBoundaryLine(sourceFile, text, current) : undefined;
|
|
1509
|
+
if (bindingLine !== undefined) {
|
|
1510
|
+
return bindingLine;
|
|
1511
|
+
}
|
|
1512
|
+
while (current) {
|
|
1513
|
+
if (ts.isImportDeclaration(current) ||
|
|
1514
|
+
ts.isImportEqualsDeclaration(current) ||
|
|
1515
|
+
ts.isVariableStatement(current) ||
|
|
1516
|
+
ts.isExpressionStatement(current)) {
|
|
1517
|
+
return getLineAndCharacterOfPosition(text, current.getStart(sourceFile)).line;
|
|
1518
|
+
}
|
|
1519
|
+
current = current.parent;
|
|
1520
|
+
}
|
|
1521
|
+
return start.line;
|
|
1522
|
+
}
|
|
1523
|
+
function createAddInteropCodeAction(uri, filePath, diagnostic, text, session) {
|
|
1524
|
+
if (diagnostic.code !== 'SOUND1005') {
|
|
1525
|
+
return undefined;
|
|
1526
|
+
}
|
|
1527
|
+
const line = findInteropBoundaryStartLine(filePath, text, diagnostic, session);
|
|
1528
|
+
if (line === undefined) {
|
|
1529
|
+
return undefined;
|
|
1530
|
+
}
|
|
1531
|
+
const lines = documentLineTexts(text);
|
|
1532
|
+
const lineText = lines[line];
|
|
1533
|
+
if (lineText === undefined) {
|
|
1534
|
+
return undefined;
|
|
1535
|
+
}
|
|
1536
|
+
const indentation = lineIndentation(lineText);
|
|
1537
|
+
return {
|
|
1538
|
+
title: 'Add #[interop] boundary',
|
|
1539
|
+
kind: 'quickfix',
|
|
1540
|
+
edit: {
|
|
1541
|
+
changes: {
|
|
1542
|
+
[uri]: [{
|
|
1543
|
+
newText: `${indentation}// #[interop]\n`,
|
|
1544
|
+
range: {
|
|
1545
|
+
start: { line, character: 0 },
|
|
1546
|
+
end: { line, character: 0 },
|
|
1547
|
+
},
|
|
1548
|
+
}],
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function createAddExternCodeAction(uri, diagnostic, text) {
|
|
1554
|
+
if (diagnostic.code !== 'SOUND1029') {
|
|
1555
|
+
return undefined;
|
|
1556
|
+
}
|
|
1557
|
+
const line = diagnostic.range?.start.line;
|
|
1558
|
+
if (line === undefined) {
|
|
1559
|
+
return undefined;
|
|
1560
|
+
}
|
|
1561
|
+
const lines = documentLineTexts(text);
|
|
1562
|
+
const lineText = lines[line];
|
|
1563
|
+
if (lineText === undefined) {
|
|
1564
|
+
return undefined;
|
|
1565
|
+
}
|
|
1566
|
+
const indentation = lineIndentation(lineText);
|
|
1567
|
+
return {
|
|
1568
|
+
title: 'Add #[extern] boundary',
|
|
1569
|
+
kind: 'quickfix',
|
|
1570
|
+
edit: {
|
|
1571
|
+
changes: {
|
|
1572
|
+
[uri]: [{
|
|
1573
|
+
newText: `${indentation}// #[extern]\n`,
|
|
1574
|
+
range: {
|
|
1575
|
+
start: { line, character: 0 },
|
|
1576
|
+
end: { line, character: 0 },
|
|
1577
|
+
},
|
|
1578
|
+
}],
|
|
1579
|
+
},
|
|
1580
|
+
},
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function createUnsupportedFeatureRewriteCodeAction(uri, filePath, diagnostic, text) {
|
|
1584
|
+
if (diagnostic.code !== 'SOUND1022') {
|
|
1585
|
+
return undefined;
|
|
1586
|
+
}
|
|
1587
|
+
const featureId = diagnostic.data?.metadata?.featureId;
|
|
1588
|
+
if (featureId !== 'unsupported.varDeclaration' &&
|
|
1589
|
+
featureId !== 'unsupported.looseEquality' &&
|
|
1590
|
+
featureId !== 'unsupported.voidZero' &&
|
|
1591
|
+
featureId !== 'unsupported.legacyOctalLiteral') {
|
|
1592
|
+
return undefined;
|
|
1593
|
+
}
|
|
1594
|
+
const start = diagnostic.range?.start;
|
|
1595
|
+
if (!start) {
|
|
1596
|
+
return undefined;
|
|
1597
|
+
}
|
|
1598
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1599
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1600
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
1601
|
+
if (featureId === 'unsupported.varDeclaration') {
|
|
1602
|
+
while (current && !ts.isVariableDeclarationList(current)) {
|
|
1603
|
+
current = current.parent;
|
|
1604
|
+
}
|
|
1605
|
+
if (!current) {
|
|
1606
|
+
return undefined;
|
|
1607
|
+
}
|
|
1608
|
+
const keywordStart = current.getStart(sourceFile);
|
|
1609
|
+
const keywordEnd = keywordStart + 3;
|
|
1610
|
+
if (text.slice(keywordStart, keywordEnd) !== 'var') {
|
|
1611
|
+
return undefined;
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
title: 'Replace `var` with `let`',
|
|
1615
|
+
kind: 'quickfix',
|
|
1616
|
+
edit: {
|
|
1617
|
+
changes: {
|
|
1618
|
+
[uri]: [{
|
|
1619
|
+
newText: 'let',
|
|
1620
|
+
range: createRangeFromOffsets(keywordStart, keywordEnd, text),
|
|
1621
|
+
}],
|
|
1622
|
+
},
|
|
1623
|
+
},
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
if (featureId === 'unsupported.looseEquality') {
|
|
1627
|
+
while (current &&
|
|
1628
|
+
(!ts.isBinaryExpression(current) ||
|
|
1629
|
+
(current.operatorToken.kind !== ts.SyntaxKind.EqualsEqualsToken &&
|
|
1630
|
+
current.operatorToken.kind !== ts.SyntaxKind.ExclamationEqualsToken))) {
|
|
1631
|
+
current = current.parent;
|
|
1632
|
+
}
|
|
1633
|
+
if (!current || !ts.isBinaryExpression(current)) {
|
|
1634
|
+
return undefined;
|
|
1635
|
+
}
|
|
1636
|
+
const operatorStart = current.operatorToken.getStart(sourceFile);
|
|
1637
|
+
const operatorEnd = current.operatorToken.getEnd();
|
|
1638
|
+
const replacement = current.operatorToken.kind === ts.SyntaxKind.EqualsEqualsToken
|
|
1639
|
+
? '==='
|
|
1640
|
+
: '!==';
|
|
1641
|
+
return {
|
|
1642
|
+
title: `Replace \`${current.operatorToken.getText(sourceFile)}\` with \`${replacement}\``,
|
|
1643
|
+
kind: 'quickfix',
|
|
1644
|
+
edit: {
|
|
1645
|
+
changes: {
|
|
1646
|
+
[uri]: [{
|
|
1647
|
+
newText: replacement,
|
|
1648
|
+
range: createRangeFromOffsets(operatorStart, operatorEnd, text),
|
|
1649
|
+
}],
|
|
1650
|
+
},
|
|
1651
|
+
},
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
if (featureId === 'unsupported.voidZero') {
|
|
1655
|
+
while (current && !ts.isVoidExpression(current)) {
|
|
1656
|
+
current = current.parent;
|
|
1657
|
+
}
|
|
1658
|
+
if (!current || !ts.isVoidExpression(current)) {
|
|
1659
|
+
return undefined;
|
|
1660
|
+
}
|
|
1661
|
+
return {
|
|
1662
|
+
title: 'Replace `void 0` with `undefined`',
|
|
1663
|
+
kind: 'quickfix',
|
|
1664
|
+
edit: {
|
|
1665
|
+
changes: {
|
|
1666
|
+
[uri]: [{
|
|
1667
|
+
newText: 'undefined',
|
|
1668
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
1669
|
+
}],
|
|
1670
|
+
},
|
|
1671
|
+
},
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
while (current && !ts.isNumericLiteral(current)) {
|
|
1675
|
+
current = current.parent;
|
|
1676
|
+
}
|
|
1677
|
+
if (!current || !ts.isNumericLiteral(current)) {
|
|
1678
|
+
return undefined;
|
|
1679
|
+
}
|
|
1680
|
+
const literalText = current.getText(sourceFile);
|
|
1681
|
+
if (!/^0[0-7]+$/.test(literalText)) {
|
|
1682
|
+
return undefined;
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
title: `Rewrite \`${literalText}\` as \`0o${literalText.slice(1)}\``,
|
|
1686
|
+
kind: 'quickfix',
|
|
1687
|
+
edit: {
|
|
1688
|
+
changes: {
|
|
1689
|
+
[uri]: [{
|
|
1690
|
+
newText: `0o${literalText.slice(1)}`,
|
|
1691
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
1692
|
+
}],
|
|
1693
|
+
},
|
|
1694
|
+
},
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
function createProofEscapeHatchRewriteCodeAction(uri, filePath, diagnostic, text) {
|
|
1698
|
+
if (diagnostic.code !== 'SOUND1002' && diagnostic.code !== 'SOUND1003') {
|
|
1699
|
+
return undefined;
|
|
1700
|
+
}
|
|
1701
|
+
const start = diagnostic.range?.start;
|
|
1702
|
+
if (!start) {
|
|
1703
|
+
return undefined;
|
|
1704
|
+
}
|
|
1705
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1706
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1707
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
1708
|
+
if (diagnostic.code === 'SOUND1002') {
|
|
1709
|
+
while (current &&
|
|
1710
|
+
!ts.isAsExpression(current) &&
|
|
1711
|
+
!ts.isTypeAssertionExpression(current)) {
|
|
1712
|
+
current = current.parent;
|
|
1713
|
+
}
|
|
1714
|
+
if (!current ||
|
|
1715
|
+
(!ts.isAsExpression(current) && !ts.isTypeAssertionExpression(current))) {
|
|
1716
|
+
return undefined;
|
|
1717
|
+
}
|
|
1718
|
+
return {
|
|
1719
|
+
title: 'Remove unchecked type assertion',
|
|
1720
|
+
kind: 'quickfix',
|
|
1721
|
+
edit: {
|
|
1722
|
+
changes: {
|
|
1723
|
+
[uri]: [{
|
|
1724
|
+
newText: text.slice(current.expression.getStart(sourceFile), current.expression.getEnd()),
|
|
1725
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
1726
|
+
}],
|
|
1727
|
+
},
|
|
1728
|
+
},
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
while (current && !ts.isNonNullExpression(current)) {
|
|
1732
|
+
current = current.parent;
|
|
1733
|
+
}
|
|
1734
|
+
if (!current || !ts.isNonNullExpression(current)) {
|
|
1735
|
+
return undefined;
|
|
1736
|
+
}
|
|
1737
|
+
return {
|
|
1738
|
+
title: 'Remove non-null assertion',
|
|
1739
|
+
kind: 'quickfix',
|
|
1740
|
+
edit: {
|
|
1741
|
+
changes: {
|
|
1742
|
+
[uri]: [{
|
|
1743
|
+
newText: text.slice(current.expression.getStart(sourceFile), current.expression.getEnd()),
|
|
1744
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
1745
|
+
}],
|
|
1746
|
+
},
|
|
1747
|
+
},
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
function createAnyTypeRewriteCodeAction(uri, filePath, diagnostic, text) {
|
|
1751
|
+
if (diagnostic.code !== 'SOUND1001') {
|
|
1752
|
+
return undefined;
|
|
1753
|
+
}
|
|
1754
|
+
const start = diagnostic.range?.start;
|
|
1755
|
+
if (!start) {
|
|
1756
|
+
return undefined;
|
|
1757
|
+
}
|
|
1758
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1759
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1760
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
1761
|
+
while (current && current.kind !== ts.SyntaxKind.AnyKeyword) {
|
|
1762
|
+
current = current.parent;
|
|
1763
|
+
}
|
|
1764
|
+
if (!current || current.kind !== ts.SyntaxKind.AnyKeyword) {
|
|
1765
|
+
return undefined;
|
|
1766
|
+
}
|
|
1767
|
+
return {
|
|
1768
|
+
title: 'Replace `any` with `unknown`',
|
|
1769
|
+
kind: 'quickfix',
|
|
1770
|
+
edit: {
|
|
1771
|
+
changes: {
|
|
1772
|
+
[uri]: [{
|
|
1773
|
+
newText: 'unknown',
|
|
1774
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
1775
|
+
}],
|
|
1776
|
+
},
|
|
1777
|
+
},
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function mappedSourceRangeForLookupNode(lookup, node) {
|
|
1781
|
+
return mapProgramEnclosingRangeToSource(lookup.preparedFile, node.getStart(lookup.sourceFile), node.getEnd());
|
|
1782
|
+
}
|
|
1783
|
+
function findConditionExpressionAtSourcePosition(lookup, sourcePosition) {
|
|
1784
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
1785
|
+
if (mappedPosition.insideReplacement) {
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
let current = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
1789
|
+
while (current && !ts.isExpression(current)) {
|
|
1790
|
+
current = current.parent;
|
|
1791
|
+
}
|
|
1792
|
+
if (!current || !ts.isExpression(current)) {
|
|
1793
|
+
return null;
|
|
1794
|
+
}
|
|
1795
|
+
let expression = current;
|
|
1796
|
+
while (expression.parent &&
|
|
1797
|
+
ts.isExpression(expression.parent) &&
|
|
1798
|
+
expression.parent.getStart(lookup.sourceFile) <= mappedPosition.position &&
|
|
1799
|
+
mappedPosition.position < expression.parent.getEnd()) {
|
|
1800
|
+
expression = expression.parent;
|
|
1801
|
+
}
|
|
1802
|
+
return expression;
|
|
1803
|
+
}
|
|
1804
|
+
function isNullishType(type) {
|
|
1805
|
+
return (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0;
|
|
1806
|
+
}
|
|
1807
|
+
function isDefinitelyBooleanTypeForCodeAction(type) {
|
|
1808
|
+
if (type.isUnion()) {
|
|
1809
|
+
return type.types.every((part) => isDefinitelyBooleanTypeForCodeAction(part));
|
|
1810
|
+
}
|
|
1811
|
+
if (type.isIntersection()) {
|
|
1812
|
+
return type.types.every((part) => isDefinitelyBooleanTypeForCodeAction(part));
|
|
1813
|
+
}
|
|
1814
|
+
return (type.flags & ts.TypeFlags.BooleanLike) !== 0;
|
|
1815
|
+
}
|
|
1816
|
+
function isAlwaysTruthyPresentType(type) {
|
|
1817
|
+
if (type.isIntersection()) {
|
|
1818
|
+
return type.types.every((part) => isAlwaysTruthyPresentType(part));
|
|
1819
|
+
}
|
|
1820
|
+
const flags = type.flags;
|
|
1821
|
+
return (flags & (ts.TypeFlags.Object | ts.TypeFlags.NonPrimitive | ts.TypeFlags.ESSymbolLike)) !==
|
|
1822
|
+
0;
|
|
1823
|
+
}
|
|
1824
|
+
function explicitNullishComparisonSuffix(type) {
|
|
1825
|
+
const constituents = type.isUnion() ? type.types : [type];
|
|
1826
|
+
let hasNull = false;
|
|
1827
|
+
let hasUndefined = false;
|
|
1828
|
+
let hasPresentConstituent = false;
|
|
1829
|
+
for (const part of constituents) {
|
|
1830
|
+
if (isNullishType(part)) {
|
|
1831
|
+
hasNull || (hasNull = (part.flags & ts.TypeFlags.Null) !== 0);
|
|
1832
|
+
hasUndefined || (hasUndefined = (part.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Void)) !== 0);
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
hasPresentConstituent = true;
|
|
1836
|
+
if (!isAlwaysTruthyPresentType(part)) {
|
|
1837
|
+
return null;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
if (!hasPresentConstituent || (hasNull === hasUndefined)) {
|
|
1841
|
+
return null;
|
|
1842
|
+
}
|
|
1843
|
+
return hasNull ? ' !== null' : ' !== undefined';
|
|
1844
|
+
}
|
|
1845
|
+
function explicitNullishComparisonText(lookup, expression, text) {
|
|
1846
|
+
const type = lookup.checker.getTypeAtLocation(expression);
|
|
1847
|
+
const suffix = explicitNullishComparisonSuffix(type);
|
|
1848
|
+
if (!suffix) {
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
const mappedRange = mappedSourceRangeForLookupNode(lookup, expression);
|
|
1852
|
+
const expressionText = text.slice(mappedRange.start, mappedRange.end);
|
|
1853
|
+
return {
|
|
1854
|
+
expressionText,
|
|
1855
|
+
mappedRange,
|
|
1856
|
+
newText: `${expressionText}${suffix}`,
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function createExplicitNullishConditionCodeAction(uri, filePath, diagnostic, text, session) {
|
|
1860
|
+
if (diagnostic.code !== 'SOUND1022' ||
|
|
1861
|
+
diagnostic.data?.metadata?.featureId !== 'unsupported.nonBooleanCondition') {
|
|
1862
|
+
return undefined;
|
|
1863
|
+
}
|
|
1864
|
+
const start = diagnostic.range?.start;
|
|
1865
|
+
if (!start) {
|
|
1866
|
+
return undefined;
|
|
1867
|
+
}
|
|
1868
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
1869
|
+
if (!preparedProject) {
|
|
1870
|
+
return undefined;
|
|
1871
|
+
}
|
|
1872
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
1873
|
+
if (!lookup) {
|
|
1874
|
+
return undefined;
|
|
1875
|
+
}
|
|
1876
|
+
const sourcePosition = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1877
|
+
const expression = findConditionExpressionAtSourcePosition(lookup, sourcePosition);
|
|
1878
|
+
if (!expression) {
|
|
1879
|
+
return undefined;
|
|
1880
|
+
}
|
|
1881
|
+
const comparison = explicitNullishComparisonText(lookup, expression, text);
|
|
1882
|
+
if (!comparison) {
|
|
1883
|
+
return undefined;
|
|
1884
|
+
}
|
|
1885
|
+
return {
|
|
1886
|
+
title: `Replace truthiness check with \`${comparison.newText}\``,
|
|
1887
|
+
kind: 'quickfix',
|
|
1888
|
+
edit: {
|
|
1889
|
+
changes: {
|
|
1890
|
+
[uri]: [{
|
|
1891
|
+
newText: comparison.newText,
|
|
1892
|
+
range: createRangeFromOffsets(comparison.mappedRange.start, comparison.mappedRange.end, text),
|
|
1893
|
+
}],
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
function createExplicitBooleanLogicalOperatorCodeAction(uri, filePath, diagnostic, text, session) {
|
|
1899
|
+
if (diagnostic.code !== 'SOUND1022' ||
|
|
1900
|
+
diagnostic.data?.metadata?.featureId !== 'unsupported.nonBooleanLogicalOperator') {
|
|
1901
|
+
return undefined;
|
|
1902
|
+
}
|
|
1903
|
+
const start = diagnostic.range?.start;
|
|
1904
|
+
if (!start) {
|
|
1905
|
+
return undefined;
|
|
1906
|
+
}
|
|
1907
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
1908
|
+
if (!preparedProject) {
|
|
1909
|
+
return undefined;
|
|
1910
|
+
}
|
|
1911
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
1912
|
+
if (!lookup) {
|
|
1913
|
+
return undefined;
|
|
1914
|
+
}
|
|
1915
|
+
const sourcePosition = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
1916
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
1917
|
+
if (mappedPosition.insideReplacement) {
|
|
1918
|
+
return undefined;
|
|
1919
|
+
}
|
|
1920
|
+
let current = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
1921
|
+
while (current &&
|
|
1922
|
+
(!ts.isBinaryExpression(current) ||
|
|
1923
|
+
(current.operatorToken.kind !== ts.SyntaxKind.AmpersandAmpersandToken &&
|
|
1924
|
+
current.operatorToken.kind !== ts.SyntaxKind.BarBarToken))) {
|
|
1925
|
+
current = current.parent;
|
|
1926
|
+
}
|
|
1927
|
+
if (!current || !ts.isBinaryExpression(current)) {
|
|
1928
|
+
return undefined;
|
|
1929
|
+
}
|
|
1930
|
+
const leftType = lookup.checker.getTypeAtLocation(current.left);
|
|
1931
|
+
const rightType = lookup.checker.getTypeAtLocation(current.right);
|
|
1932
|
+
const leftIsBoolean = isDefinitelyBooleanTypeForCodeAction(leftType);
|
|
1933
|
+
const rightIsBoolean = isDefinitelyBooleanTypeForCodeAction(rightType);
|
|
1934
|
+
const leftComparison = leftIsBoolean
|
|
1935
|
+
? null
|
|
1936
|
+
: explicitNullishComparisonText(lookup, current.left, text);
|
|
1937
|
+
const rightComparison = rightIsBoolean
|
|
1938
|
+
? null
|
|
1939
|
+
: explicitNullishComparisonText(lookup, current.right, text);
|
|
1940
|
+
if ((!leftIsBoolean && !leftComparison) || (!rightIsBoolean && !rightComparison)) {
|
|
1941
|
+
return undefined;
|
|
1942
|
+
}
|
|
1943
|
+
const mappedRange = mappedSourceRangeForLookupNode(lookup, current);
|
|
1944
|
+
const leftRange = mappedSourceRangeForLookupNode(lookup, current.left);
|
|
1945
|
+
const rightRange = mappedSourceRangeForLookupNode(lookup, current.right);
|
|
1946
|
+
const operatorText = current.operatorToken.getText(lookup.sourceFile);
|
|
1947
|
+
const leftText = leftComparison?.newText ?? text.slice(leftRange.start, leftRange.end);
|
|
1948
|
+
const rightText = rightComparison?.newText ?? text.slice(rightRange.start, rightRange.end);
|
|
1949
|
+
const newText = `${leftText} ${operatorText} ${rightText}`;
|
|
1950
|
+
return {
|
|
1951
|
+
title: `Make \`${operatorText}\` operands explicitly boolean`,
|
|
1952
|
+
kind: 'quickfix',
|
|
1953
|
+
edit: {
|
|
1954
|
+
changes: {
|
|
1955
|
+
[uri]: [{
|
|
1956
|
+
newText,
|
|
1957
|
+
range: createRangeFromOffsets(mappedRange.start, mappedRange.end, text),
|
|
1958
|
+
}],
|
|
1959
|
+
},
|
|
1960
|
+
},
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
function isTypeScriptPragmaCommentLine(lineText) {
|
|
1964
|
+
const trimmed = lineText.trim();
|
|
1965
|
+
return trimmed.startsWith('// @ts-') || trimmed.startsWith('/* @ts-');
|
|
1966
|
+
}
|
|
1967
|
+
function createRemoveTypeScriptPragmaCodeAction(uri, diagnostic, text) {
|
|
1968
|
+
if (diagnostic.code !== 'SOUND1023') {
|
|
1969
|
+
return undefined;
|
|
1970
|
+
}
|
|
1971
|
+
const line = diagnostic.range?.start.line;
|
|
1972
|
+
if (line === undefined) {
|
|
1973
|
+
return undefined;
|
|
1974
|
+
}
|
|
1975
|
+
const lines = documentLineTexts(text);
|
|
1976
|
+
const lineText = lines[line];
|
|
1977
|
+
if (lineText === undefined || !isTypeScriptPragmaCommentLine(lineText)) {
|
|
1978
|
+
return undefined;
|
|
1979
|
+
}
|
|
1980
|
+
const edit = deleteWholeLineEdit(lines, line);
|
|
1981
|
+
if (!edit) {
|
|
1982
|
+
return undefined;
|
|
1983
|
+
}
|
|
1984
|
+
return {
|
|
1985
|
+
title: 'Remove TypeScript pragma comment',
|
|
1986
|
+
kind: 'quickfix',
|
|
1987
|
+
edit: {
|
|
1988
|
+
changes: {
|
|
1989
|
+
[uri]: [edit],
|
|
1990
|
+
},
|
|
1991
|
+
},
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
function createRemoveInvalidAnnotationTargetCodeAction(uri, diagnostic, text) {
|
|
1995
|
+
if (diagnostic.code !== 'SOUND1027') {
|
|
1996
|
+
return undefined;
|
|
1997
|
+
}
|
|
1998
|
+
const startLine = diagnostic.range?.start.line;
|
|
1999
|
+
if (startLine === undefined) {
|
|
2000
|
+
return undefined;
|
|
2001
|
+
}
|
|
2002
|
+
const lines = documentLineTexts(text);
|
|
2003
|
+
const primarySymbol = diagnostic.data?.metadata?.primarySymbol;
|
|
2004
|
+
const annotationName = primarySymbol?.match(/^#\[([A-Za-z_$][\w$.-]*)\]$/u)?.[1];
|
|
2005
|
+
const annotationLine = findAttachedAnnotationLine(lines, startLine, annotationName);
|
|
2006
|
+
const lineText = annotationLine === undefined ? undefined : lines[annotationLine];
|
|
2007
|
+
if (annotationLine === undefined || lineText === undefined || !isAnnotationCommentLine(lineText)) {
|
|
2008
|
+
return undefined;
|
|
2009
|
+
}
|
|
2010
|
+
const edit = deleteWholeLineEdit(lines, annotationLine);
|
|
2011
|
+
if (!edit) {
|
|
2012
|
+
return undefined;
|
|
2013
|
+
}
|
|
2014
|
+
return {
|
|
2015
|
+
title: 'Remove invalid annotation comment',
|
|
2016
|
+
kind: 'quickfix',
|
|
2017
|
+
edit: {
|
|
2018
|
+
changes: {
|
|
2019
|
+
[uri]: [edit],
|
|
2020
|
+
},
|
|
2021
|
+
},
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
function createRemoveAmbientExportCodeAction(uri, filePath, diagnostic, text) {
|
|
2025
|
+
if (diagnostic.code !== 'SOUND1030') {
|
|
2026
|
+
return undefined;
|
|
2027
|
+
}
|
|
2028
|
+
const start = diagnostic.range?.start;
|
|
2029
|
+
if (!start) {
|
|
2030
|
+
return undefined;
|
|
2031
|
+
}
|
|
2032
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
2033
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
2034
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
2035
|
+
while (current) {
|
|
2036
|
+
if (ts.canHaveModifiers(current)) {
|
|
2037
|
+
const modifiers = ts.getModifiers(current);
|
|
2038
|
+
const exportModifier = modifiers?.find((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
|
2039
|
+
if (exportModifier) {
|
|
2040
|
+
let editEnd = exportModifier.getEnd();
|
|
2041
|
+
while (editEnd < text.length && /\s/.test(text[editEnd] ?? '')) {
|
|
2042
|
+
editEnd++;
|
|
2043
|
+
break;
|
|
2044
|
+
}
|
|
2045
|
+
return {
|
|
2046
|
+
title: 'Remove `export` from ambient runtime declaration',
|
|
2047
|
+
kind: 'quickfix',
|
|
2048
|
+
edit: {
|
|
2049
|
+
changes: {
|
|
2050
|
+
[uri]: [{
|
|
2051
|
+
newText: '',
|
|
2052
|
+
range: createRangeFromOffsets(exportModifier.getStart(sourceFile), editEnd, text),
|
|
2053
|
+
}],
|
|
2054
|
+
},
|
|
2055
|
+
},
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
current = current.parent;
|
|
2060
|
+
}
|
|
2061
|
+
return undefined;
|
|
2062
|
+
}
|
|
2063
|
+
function createThrowNonErrorCodeAction(uri, filePath, diagnostic, text) {
|
|
2064
|
+
if (diagnostic.code !== 'SOUND1025') {
|
|
2065
|
+
return undefined;
|
|
2066
|
+
}
|
|
2067
|
+
const start = diagnostic.range?.start;
|
|
2068
|
+
if (!start) {
|
|
2069
|
+
return undefined;
|
|
2070
|
+
}
|
|
2071
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
2072
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
2073
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
2074
|
+
while (current && !ts.isThrowStatement(current)) {
|
|
2075
|
+
current = current.parent;
|
|
2076
|
+
}
|
|
2077
|
+
if (!current || !ts.isThrowStatement(current) || !current.expression) {
|
|
2078
|
+
return undefined;
|
|
2079
|
+
}
|
|
2080
|
+
const expression = current.expression;
|
|
2081
|
+
return {
|
|
2082
|
+
title: 'Wrap thrown value in `new Error(...)`',
|
|
2083
|
+
kind: 'quickfix',
|
|
2084
|
+
edit: {
|
|
2085
|
+
changes: {
|
|
2086
|
+
[uri]: [{
|
|
2087
|
+
newText: `new Error(String(${text.slice(expression.getStart(sourceFile), expression.getEnd())}))`,
|
|
2088
|
+
range: createRangeFromOffsets(expression.getStart(sourceFile), expression.getEnd(), text),
|
|
2089
|
+
}],
|
|
2090
|
+
},
|
|
2091
|
+
},
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function createReceiverSensitiveBindCodeAction(uri, filePath, diagnostic, text) {
|
|
2095
|
+
if (diagnostic.code !== 'SOUND1035') {
|
|
2096
|
+
return undefined;
|
|
2097
|
+
}
|
|
2098
|
+
const start = diagnostic.range?.start;
|
|
2099
|
+
if (!start) {
|
|
2100
|
+
return undefined;
|
|
2101
|
+
}
|
|
2102
|
+
const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
2103
|
+
const position = getPositionOfLineAndCharacter(text, start.line, start.character);
|
|
2104
|
+
let current = findDeepestNodeContainingPosition(sourceFile, position);
|
|
2105
|
+
while (current &&
|
|
2106
|
+
!ts.isPropertyAccessExpression(current) &&
|
|
2107
|
+
!ts.isElementAccessExpression(current)) {
|
|
2108
|
+
current = current.parent;
|
|
2109
|
+
}
|
|
2110
|
+
if (!current || (!ts.isPropertyAccessExpression(current) && !ts.isElementAccessExpression(current))) {
|
|
2111
|
+
return undefined;
|
|
2112
|
+
}
|
|
2113
|
+
const receiverText = text.slice(current.expression.getStart(sourceFile), current.expression.getEnd());
|
|
2114
|
+
const callableText = text.slice(current.getStart(sourceFile), current.getEnd());
|
|
2115
|
+
return {
|
|
2116
|
+
title: 'Bind the receiver for the extracted method',
|
|
2117
|
+
kind: 'quickfix',
|
|
2118
|
+
edit: {
|
|
2119
|
+
changes: {
|
|
2120
|
+
[uri]: [{
|
|
2121
|
+
newText: `${callableText}.bind(${receiverText})`,
|
|
2122
|
+
range: createRangeFromOffsets(current.getStart(sourceFile), current.getEnd(), text),
|
|
2123
|
+
}],
|
|
2124
|
+
},
|
|
2125
|
+
},
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
function createRemoveMalformedAnnotationCodeAction(uri, diagnostic, text) {
|
|
2129
|
+
if (diagnostic.code !== 'SOUND1006') {
|
|
2130
|
+
return undefined;
|
|
2131
|
+
}
|
|
2132
|
+
const line = diagnostic.range?.start.line;
|
|
2133
|
+
if (line === undefined) {
|
|
2134
|
+
return undefined;
|
|
2135
|
+
}
|
|
2136
|
+
const lines = documentLineTexts(text);
|
|
2137
|
+
const lineText = lines[line];
|
|
2138
|
+
if (lineText === undefined || !isAnnotationCommentLine(lineText)) {
|
|
2139
|
+
return undefined;
|
|
2140
|
+
}
|
|
2141
|
+
const edit = deleteWholeLineEdit(lines, line);
|
|
2142
|
+
if (!edit) {
|
|
2143
|
+
return undefined;
|
|
2144
|
+
}
|
|
2145
|
+
return {
|
|
2146
|
+
title: 'Remove malformed annotation comment',
|
|
2147
|
+
kind: 'quickfix',
|
|
2148
|
+
edit: {
|
|
2149
|
+
changes: {
|
|
2150
|
+
[uri]: [edit],
|
|
2151
|
+
},
|
|
2152
|
+
},
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
function createRemoveDuplicateAnnotationsCodeAction(uri, diagnostic, text) {
|
|
2156
|
+
if (diagnostic.code !== 'SOUND1026') {
|
|
2157
|
+
return undefined;
|
|
2158
|
+
}
|
|
2159
|
+
const startLine = diagnostic.range?.start.line;
|
|
2160
|
+
if (startLine === undefined) {
|
|
2161
|
+
return undefined;
|
|
2162
|
+
}
|
|
2163
|
+
const lines = documentLineTexts(text);
|
|
2164
|
+
if (!isAnnotationCommentLine(lines[startLine] ?? '')) {
|
|
2165
|
+
return undefined;
|
|
2166
|
+
}
|
|
2167
|
+
const edits = [];
|
|
2168
|
+
const seenNames = new Set();
|
|
2169
|
+
for (let line = startLine; line < lines.length; line += 1) {
|
|
2170
|
+
const lineText = lines[line] ?? '';
|
|
2171
|
+
if (!isAnnotationCommentLine(lineText)) {
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
const annotationName = annotationNameForLine(lineText);
|
|
2175
|
+
if (!annotationName) {
|
|
2176
|
+
continue;
|
|
2177
|
+
}
|
|
2178
|
+
if (seenNames.has(annotationName)) {
|
|
2179
|
+
const edit = deleteWholeLineEdit(lines, line);
|
|
2180
|
+
if (edit) {
|
|
2181
|
+
edits.push(edit);
|
|
2182
|
+
}
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
seenNames.add(annotationName);
|
|
2186
|
+
}
|
|
2187
|
+
if (edits.length === 0) {
|
|
2188
|
+
return undefined;
|
|
2189
|
+
}
|
|
2190
|
+
return {
|
|
2191
|
+
title: 'Remove duplicate annotation entries',
|
|
2192
|
+
kind: 'quickfix',
|
|
2193
|
+
edit: {
|
|
2194
|
+
changes: {
|
|
2195
|
+
[uri]: edits,
|
|
2196
|
+
},
|
|
2197
|
+
},
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
function createDocumentSymbolChildren(preparedProject, sourceFile, nodes) {
|
|
2201
|
+
const symbols = [];
|
|
2202
|
+
for (const node of nodes) {
|
|
2203
|
+
const symbol = createDocumentSymbol(preparedProject, sourceFile, node);
|
|
2204
|
+
if (symbol) {
|
|
2205
|
+
symbols.push(symbol);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
return symbols;
|
|
2209
|
+
}
|
|
2210
|
+
function childNodesForDocumentSymbol(node) {
|
|
2211
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
2212
|
+
return [...node.members];
|
|
2213
|
+
}
|
|
2214
|
+
if (ts.isEnumDeclaration(node)) {
|
|
2215
|
+
return [...node.members];
|
|
2216
|
+
}
|
|
2217
|
+
if (ts.isModuleDeclaration(node) && node.body && ts.isModuleBlock(node.body)) {
|
|
2218
|
+
return [...node.body.statements];
|
|
2219
|
+
}
|
|
2220
|
+
return [];
|
|
2221
|
+
}
|
|
2222
|
+
function createDocumentSymbol(preparedProject, sourceFile, node) {
|
|
2223
|
+
const kind = documentSymbolKindForNode(node);
|
|
2224
|
+
const name = getDeclarationNameText(node);
|
|
2225
|
+
const nameNode = getDeclarationNameNode(node);
|
|
2226
|
+
if (!kind || !name || !nameNode || !isUserFacingSymbolName(name)) {
|
|
2227
|
+
return null;
|
|
2228
|
+
}
|
|
2229
|
+
const children = createDocumentSymbolChildren(preparedProject, sourceFile, childNodesForDocumentSymbol(node));
|
|
2230
|
+
return {
|
|
2231
|
+
children: children.length > 0 ? children : undefined,
|
|
2232
|
+
kind,
|
|
2233
|
+
name,
|
|
2234
|
+
range: createRangeForNode(preparedProject, sourceFile, node),
|
|
2235
|
+
selectionRange: createRangeForNode(preparedProject, sourceFile, nameNode),
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
function topLevelDocumentSymbols(preparedProject, sourceFile) {
|
|
2239
|
+
const symbols = [];
|
|
2240
|
+
for (const statement of sourceFile.statements) {
|
|
2241
|
+
if (ts.isVariableStatement(statement)) {
|
|
2242
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
2243
|
+
const symbol = createDocumentSymbol(preparedProject, sourceFile, declaration);
|
|
2244
|
+
if (symbol) {
|
|
2245
|
+
symbols.push(symbol);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
const symbol = createDocumentSymbol(preparedProject, sourceFile, statement);
|
|
2251
|
+
if (symbol) {
|
|
2252
|
+
symbols.push(symbol);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
return symbols;
|
|
2256
|
+
}
|
|
2257
|
+
function toCompletionItemKind(symbol) {
|
|
2258
|
+
if ((symbol.flags & ts.SymbolFlags.Function) !== 0 || (symbol.flags & ts.SymbolFlags.Method) !== 0) {
|
|
2259
|
+
return 3;
|
|
2260
|
+
}
|
|
2261
|
+
if ((symbol.flags & ts.SymbolFlags.Class) !== 0) {
|
|
2262
|
+
return 7;
|
|
2263
|
+
}
|
|
2264
|
+
if ((symbol.flags & ts.SymbolFlags.Interface) !== 0) {
|
|
2265
|
+
return 8;
|
|
2266
|
+
}
|
|
2267
|
+
if ((symbol.flags & ts.SymbolFlags.TypeAlias) !== 0) {
|
|
2268
|
+
return 25;
|
|
2269
|
+
}
|
|
2270
|
+
if ((symbol.flags & ts.SymbolFlags.Enum) !== 0) {
|
|
2271
|
+
return 13;
|
|
2272
|
+
}
|
|
2273
|
+
if ((symbol.flags & ts.SymbolFlags.EnumMember) !== 0) {
|
|
2274
|
+
return 21;
|
|
2275
|
+
}
|
|
2276
|
+
if ((symbol.flags & ts.SymbolFlags.Module) !== 0 || (symbol.flags & ts.SymbolFlags.Namespace) !== 0) {
|
|
2277
|
+
return 9;
|
|
2278
|
+
}
|
|
2279
|
+
if ((symbol.flags & ts.SymbolFlags.Property) !== 0) {
|
|
2280
|
+
return 10;
|
|
2281
|
+
}
|
|
2282
|
+
if ((symbol.flags & ts.SymbolFlags.Variable) !== 0 ||
|
|
2283
|
+
(symbol.flags & ts.SymbolFlags.BlockScopedVariable) !== 0) {
|
|
2284
|
+
return 6;
|
|
2285
|
+
}
|
|
2286
|
+
if ((symbol.flags & ts.SymbolFlags.TypeParameter) !== 0) {
|
|
2287
|
+
return 25;
|
|
2288
|
+
}
|
|
2289
|
+
return undefined;
|
|
2290
|
+
}
|
|
2291
|
+
function createCompletionItemsForSymbols(checker, symbols, location, prefix) {
|
|
2292
|
+
const uniqueItems = new Map();
|
|
2293
|
+
for (const symbol of symbols) {
|
|
2294
|
+
if (!isUserFacingSymbolName(symbol.name)) {
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2297
|
+
if (prefix.length > 0 && !symbol.name.startsWith(prefix)) {
|
|
2298
|
+
continue;
|
|
2299
|
+
}
|
|
2300
|
+
const completionItem = {
|
|
2301
|
+
label: symbol.name,
|
|
2302
|
+
kind: toCompletionItemKind(symbol),
|
|
2303
|
+
};
|
|
2304
|
+
try {
|
|
2305
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, location);
|
|
2306
|
+
const detail = checker.typeToString(type);
|
|
2307
|
+
if (detail.length > 0 && detail !== 'any') {
|
|
2308
|
+
completionItem.detail = detail;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
catch {
|
|
2312
|
+
// Best-effort detail only.
|
|
2313
|
+
}
|
|
2314
|
+
uniqueItems.set(symbol.name, completionItem);
|
|
2315
|
+
}
|
|
2316
|
+
return [...uniqueItems.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
2317
|
+
}
|
|
2318
|
+
function findPropertyAccessCompletionTarget(node) {
|
|
2319
|
+
let current = node;
|
|
2320
|
+
while (current) {
|
|
2321
|
+
if (ts.isPropertyAccessExpression(current)) {
|
|
2322
|
+
return current;
|
|
2323
|
+
}
|
|
2324
|
+
current = current.parent;
|
|
2325
|
+
}
|
|
2326
|
+
return undefined;
|
|
2327
|
+
}
|
|
2328
|
+
function completionItemsForNode(checker, node, originalText, sourcePosition) {
|
|
2329
|
+
const propertyAccess = findPropertyAccessCompletionTarget(node);
|
|
2330
|
+
if (propertyAccess) {
|
|
2331
|
+
const memberPrefix = getIdentifierPrefixAtPosition(originalText, sourcePosition);
|
|
2332
|
+
const apparentType = checker.getApparentType(checker.getTypeAtLocation(propertyAccess.expression));
|
|
2333
|
+
return createCompletionItemsForSymbols(checker, checker.getPropertiesOfType(apparentType), propertyAccess.expression, memberPrefix);
|
|
2334
|
+
}
|
|
2335
|
+
const scopePrefix = getIdentifierPrefixAtPosition(originalText, sourcePosition);
|
|
2336
|
+
const scopeFlags = ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace |
|
|
2337
|
+
ts.SymbolFlags.Alias;
|
|
2338
|
+
return createCompletionItemsForSymbols(checker, checker.getSymbolsInScope(node, scopeFlags), node, scopePrefix);
|
|
2339
|
+
}
|
|
2340
|
+
function findResolvedMacroContainingPosition(filePath, sourcePosition, collected) {
|
|
2341
|
+
return collected.find((entry) => entry.resolved.placeholder.fileName === filePath &&
|
|
2342
|
+
containsPosition(entry.resolved.placeholder.invocation.span.start, entry.resolved.placeholder.invocation.span.end, sourcePosition));
|
|
2343
|
+
}
|
|
2344
|
+
function resolveImportedMacroDefinition(preparedProject, filePath, match) {
|
|
2345
|
+
return getImportedMacroDefinitionsForFile(preparedProject, filePath).get(match.resolved.placeholder.invocation.nameText) ?? null;
|
|
2346
|
+
}
|
|
2347
|
+
function resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, sourcePosition, useCompletion = false) {
|
|
2348
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
2349
|
+
if (!artifacts?.node) {
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
const region = analysisRegionForMacroDefinition(artifacts.definition, {
|
|
2353
|
+
macro: artifacts.context,
|
|
2354
|
+
node: artifacts.node,
|
|
2355
|
+
offset: sourcePosition - artifacts.node.span.start,
|
|
2356
|
+
});
|
|
2357
|
+
if (!region) {
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
const effectiveSourcePosition = useCompletion && sourcePosition === region.sourceSpan.end
|
|
2361
|
+
? Math.max(region.sourceSpan.start, sourcePosition - 1)
|
|
2362
|
+
: sourcePosition;
|
|
2363
|
+
const originalText = match.resolved.placeholder.preparedFile.originalText;
|
|
2364
|
+
const materialized = materializeRegionForHover(match.resolved.placeholder.invocation.fileName, originalText, region.sourceSpan, effectiveSourcePosition);
|
|
2365
|
+
if ('kind' in materialized) {
|
|
2366
|
+
return materialized;
|
|
2367
|
+
}
|
|
2368
|
+
const materializedRegion = materialized;
|
|
2369
|
+
const completionMaterialized = useCompletion && sourcePosition === region.sourceSpan.end
|
|
2370
|
+
? { ...materializedRegion, hoverPosition: materializedRegion.text.length }
|
|
2371
|
+
: materializedRegion;
|
|
2372
|
+
const wrappedRegion = wrapMaterializedRegion(completionMaterialized, region.prefixText, region.suffixText);
|
|
2373
|
+
return useCompletion
|
|
2374
|
+
? resolveCompletionNodeAtMaterializedRegion(preparedProject.preparedProgram, match.resolved, wrappedRegion)
|
|
2375
|
+
: resolveNodeAtMaterializedRegion(preparedProject.preparedProgram, match.resolved, wrappedRegion);
|
|
2376
|
+
}
|
|
2377
|
+
function parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match) {
|
|
2378
|
+
const definition = resolveImportedMacroDefinition(preparedProject, filePath, match);
|
|
2379
|
+
if (!definition) {
|
|
2380
|
+
return null;
|
|
2381
|
+
}
|
|
2382
|
+
try {
|
|
2383
|
+
const context = createAdvancedMacroContext(preparedProject.preparedProgram, match.resolved);
|
|
2384
|
+
return {
|
|
2385
|
+
context,
|
|
2386
|
+
definition,
|
|
2387
|
+
fragments: fragmentsForMacroDefinition(definition, context),
|
|
2388
|
+
node: parseMacroSyntaxNodeForDefinition(definition, context),
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
catch {
|
|
2392
|
+
return null;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
function findMacroFragmentAtPosition(fragments, sourcePosition) {
|
|
2396
|
+
return fragments.find((fragment) => containsPosition(fragment.span.start, fragment.span.end, sourcePosition)) ?? null;
|
|
2397
|
+
}
|
|
2398
|
+
function hoverHookForMacroInvocation(preparedProject, filePath, match, sourcePosition, originalText) {
|
|
2399
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
2400
|
+
if (!artifacts) {
|
|
2401
|
+
return null;
|
|
2402
|
+
}
|
|
2403
|
+
const fragment = findMacroFragmentAtPosition(artifacts.fragments, sourcePosition);
|
|
2404
|
+
if (fragment?.hover) {
|
|
2405
|
+
const hover = fragment.hover(sourcePosition);
|
|
2406
|
+
if (hover) {
|
|
2407
|
+
return {
|
|
2408
|
+
contents: {
|
|
2409
|
+
kind: 'markdown',
|
|
2410
|
+
value: hover.contents,
|
|
2411
|
+
},
|
|
2412
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (artifacts.definition.positionHover && artifacts.node) {
|
|
2417
|
+
if (!containsPosition(artifacts.node.span.start, artifacts.node.span.end, sourcePosition)) {
|
|
2418
|
+
return null;
|
|
2419
|
+
}
|
|
2420
|
+
const hover = artifacts.definition.positionHover({
|
|
2421
|
+
macro: artifacts.context,
|
|
2422
|
+
node: artifacts.node,
|
|
2423
|
+
offset: sourcePosition - artifacts.node.span.start,
|
|
2424
|
+
});
|
|
2425
|
+
if (hover) {
|
|
2426
|
+
return {
|
|
2427
|
+
contents: {
|
|
2428
|
+
kind: 'markdown',
|
|
2429
|
+
value: hover.contents,
|
|
2430
|
+
},
|
|
2431
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
return null;
|
|
2436
|
+
}
|
|
2437
|
+
function summaryHoverForMacroInvocation(preparedProject, filePath, match, sourcePosition, originalText) {
|
|
2438
|
+
const definition = resolveImportedMacroDefinition(preparedProject, filePath, match);
|
|
2439
|
+
if (!definition) {
|
|
2440
|
+
return null;
|
|
2441
|
+
}
|
|
2442
|
+
const invocation = match.resolved.placeholder.invocation;
|
|
2443
|
+
if (!containsPosition(invocation.nameSpan.start, invocation.nameSpan.end, sourcePosition)) {
|
|
2444
|
+
return null;
|
|
2445
|
+
}
|
|
2446
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
2447
|
+
if (artifacts?.definition.hover && artifacts.node) {
|
|
2448
|
+
if (!containsPosition(artifacts.node.span.start, artifacts.node.span.end, sourcePosition)) {
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
const hover = artifacts.definition.hover({
|
|
2452
|
+
node: artifacts.node,
|
|
2453
|
+
offset: sourcePosition - artifacts.node.span.start,
|
|
2454
|
+
});
|
|
2455
|
+
if (hover) {
|
|
2456
|
+
const details = signatureHoverDetails(definition, invocation.nameText);
|
|
2457
|
+
return createCustomMacroSummaryHover(invocation, originalText, hover.contents, details);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
const details = signatureHoverDetails(definition, invocation.nameText);
|
|
2461
|
+
if (details.length === 0) {
|
|
2462
|
+
return null;
|
|
2463
|
+
}
|
|
2464
|
+
return createMacroSummaryHover(invocation, originalText, details);
|
|
2465
|
+
}
|
|
2466
|
+
function importedMacroSummaryHoverForNode(preparedProject, filePath, node, originalText, sourcePosition) {
|
|
2467
|
+
if (!ts.isIdentifier(node)) {
|
|
2468
|
+
return null;
|
|
2469
|
+
}
|
|
2470
|
+
let localMacroName = null;
|
|
2471
|
+
if (ts.isImportSpecifier(node.parent)) {
|
|
2472
|
+
if (node.parent.name === node || node.parent.propertyName === node) {
|
|
2473
|
+
localMacroName = node.parent.name.text;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
if (!localMacroName) {
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
const definition = getImportedMacroDefinitionsForFile(preparedProject, filePath).get(localMacroName) ?? null;
|
|
2480
|
+
if (!definition) {
|
|
2481
|
+
return null;
|
|
2482
|
+
}
|
|
2483
|
+
const details = signatureHoverDetails(definition, localMacroName);
|
|
2484
|
+
const customBody = genericHoverBodyForImportedMacro(definition, localMacroName);
|
|
2485
|
+
return {
|
|
2486
|
+
contents: customBody
|
|
2487
|
+
? createCustomMacroSummaryContents(localMacroName, customBody, details)
|
|
2488
|
+
: createMarkdownTextHoverContents(`**macro** \`${localMacroName}\``, details),
|
|
2489
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
function scanQuotedAnnotationString(text, start) {
|
|
2493
|
+
const quote = text[start];
|
|
2494
|
+
let index = start + 1;
|
|
2495
|
+
while (index < text.length) {
|
|
2496
|
+
const character = text[index];
|
|
2497
|
+
if (character === '\\') {
|
|
2498
|
+
index += 2;
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
if (character === quote) {
|
|
2502
|
+
return index + 1;
|
|
2503
|
+
}
|
|
2504
|
+
index += 1;
|
|
2505
|
+
}
|
|
2506
|
+
return text.length;
|
|
2507
|
+
}
|
|
2508
|
+
const BUILTIN_ANNOTATION_HOVER_DETAILS = {
|
|
2509
|
+
extern: {
|
|
2510
|
+
summary: 'Marks a local ambient runtime declaration as an explicit extern boundary.',
|
|
2511
|
+
syntax: '// #[extern]',
|
|
2512
|
+
details: [
|
|
2513
|
+
'Use `#[extern]` only for same-file runtime-provided declarations such as host globals or compiler-injected helpers.',
|
|
2514
|
+
'This attaches to local ambient declarations, not to ordinary imports.',
|
|
2515
|
+
],
|
|
2516
|
+
},
|
|
2517
|
+
interop: {
|
|
2518
|
+
summary: 'Marks an import-like boundary where unsound foreign values enter soundscript.',
|
|
2519
|
+
syntax: '// #[interop]',
|
|
2520
|
+
details: [
|
|
2521
|
+
'Use `#[interop]` on imports, `require(...)`, or dynamic `import(...)` boundaries that intentionally cross from `.ts`, JavaScript, or declaration-only code into `.sts`.',
|
|
2522
|
+
'Validate the imported value at the boundary before relying on stronger types inside soundscript.',
|
|
2523
|
+
],
|
|
2524
|
+
},
|
|
2525
|
+
unsafe: {
|
|
2526
|
+
summary: 'Marks a local proof-override site inside soundscript.',
|
|
2527
|
+
syntax: '// #[unsafe]',
|
|
2528
|
+
details: [
|
|
2529
|
+
'Use `#[unsafe]` only when you are intentionally overriding a local proof obligation.',
|
|
2530
|
+
'This is a local escape hatch, not a foreign-boundary marker.',
|
|
2531
|
+
],
|
|
2532
|
+
},
|
|
2533
|
+
variance: {
|
|
2534
|
+
summary: 'Declares a checked variance contract on a generic interface or type alias.',
|
|
2535
|
+
syntax: '// #[variance(T: out, U: in)]',
|
|
2536
|
+
details: [
|
|
2537
|
+
'Use named arguments such as `T: out`, `U: in`, `R: inout`, or `X: independent`, once per declared type parameter.',
|
|
2538
|
+
'The contract is checked, not trusted: soundscript verifies that the declaration surface actually proves the stated variance.',
|
|
2539
|
+
],
|
|
2540
|
+
},
|
|
2541
|
+
};
|
|
2542
|
+
function parseAnnotationHoverItem(rawItemText, absoluteStart, absoluteEnd) {
|
|
2543
|
+
const trimmedText = rawItemText.trim();
|
|
2544
|
+
if (trimmedText.length === 0) {
|
|
2545
|
+
return null;
|
|
2546
|
+
}
|
|
2547
|
+
const leadingWhitespaceLength = rawItemText.length - rawItemText.trimStart().length;
|
|
2548
|
+
const itemStart = absoluteStart + leadingWhitespaceLength;
|
|
2549
|
+
const itemEnd = absoluteEnd - (rawItemText.length - rawItemText.trimEnd().length);
|
|
2550
|
+
const openParenIndex = trimmedText.indexOf('(');
|
|
2551
|
+
if (openParenIndex === -1) {
|
|
2552
|
+
return {
|
|
2553
|
+
end: itemEnd,
|
|
2554
|
+
name: trimmedText,
|
|
2555
|
+
nameEnd: itemStart + trimmedText.length,
|
|
2556
|
+
nameStart: itemStart,
|
|
2557
|
+
start: itemStart,
|
|
2558
|
+
text: trimmedText,
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
let parenDepth = 0;
|
|
2562
|
+
let bracketDepth = 0;
|
|
2563
|
+
let braceDepth = 0;
|
|
2564
|
+
let closeParenIndex = -1;
|
|
2565
|
+
for (let index = openParenIndex; index < trimmedText.length; index += 1) {
|
|
2566
|
+
const character = trimmedText[index];
|
|
2567
|
+
if (character === '"' || character === "'") {
|
|
2568
|
+
index = scanQuotedAnnotationString(trimmedText, index) - 1;
|
|
2569
|
+
continue;
|
|
2570
|
+
}
|
|
2571
|
+
if (character === '(') {
|
|
2572
|
+
parenDepth += 1;
|
|
2573
|
+
continue;
|
|
2574
|
+
}
|
|
2575
|
+
if (character === ')') {
|
|
2576
|
+
parenDepth -= 1;
|
|
2577
|
+
if (parenDepth === 0) {
|
|
2578
|
+
closeParenIndex = index;
|
|
2579
|
+
break;
|
|
2580
|
+
}
|
|
2581
|
+
continue;
|
|
2582
|
+
}
|
|
2583
|
+
if (character === '[') {
|
|
2584
|
+
bracketDepth += 1;
|
|
2585
|
+
continue;
|
|
2586
|
+
}
|
|
2587
|
+
if (character === ']') {
|
|
2588
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
2589
|
+
continue;
|
|
2590
|
+
}
|
|
2591
|
+
if (character === '{') {
|
|
2592
|
+
braceDepth += 1;
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
if (character === '}') {
|
|
2596
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
2597
|
+
continue;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
const name = trimmedText.slice(0, openParenIndex).trim();
|
|
2601
|
+
const rawNamePrefix = rawItemText.slice(0, rawItemText.indexOf(name));
|
|
2602
|
+
const nameStart = absoluteStart + rawNamePrefix.length;
|
|
2603
|
+
return {
|
|
2604
|
+
argumentsText: closeParenIndex === -1
|
|
2605
|
+
? undefined
|
|
2606
|
+
: trimmedText.slice(openParenIndex + 1, closeParenIndex),
|
|
2607
|
+
end: itemEnd,
|
|
2608
|
+
name,
|
|
2609
|
+
nameEnd: nameStart + name.length,
|
|
2610
|
+
nameStart,
|
|
2611
|
+
start: itemStart,
|
|
2612
|
+
text: trimmedText,
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function findAnnotationHoverItemAtPosition(originalText, sourcePosition) {
|
|
2616
|
+
const lineStart = originalText.lastIndexOf('\n', Math.max(0, sourcePosition - 1)) + 1;
|
|
2617
|
+
const newlineIndex = originalText.indexOf('\n', sourcePosition);
|
|
2618
|
+
const lineEnd = newlineIndex === -1 ? originalText.length : newlineIndex;
|
|
2619
|
+
const lineText = originalText.slice(lineStart, lineEnd);
|
|
2620
|
+
const openMatch = /\/\/\s*#\[/u.exec(lineText);
|
|
2621
|
+
if (!openMatch) {
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
const bodyStart = lineStart + openMatch.index + openMatch[0].length;
|
|
2625
|
+
let closingBracketIndex = -1;
|
|
2626
|
+
let parenDepth = 0;
|
|
2627
|
+
let bracketDepth = 0;
|
|
2628
|
+
let braceDepth = 0;
|
|
2629
|
+
for (let index = bodyStart; index < lineEnd; index += 1) {
|
|
2630
|
+
const character = originalText[index];
|
|
2631
|
+
if (character === '"' || character === "'") {
|
|
2632
|
+
index = scanQuotedAnnotationString(originalText, index) - 1;
|
|
2633
|
+
continue;
|
|
2634
|
+
}
|
|
2635
|
+
if (character === '(') {
|
|
2636
|
+
parenDepth += 1;
|
|
2637
|
+
continue;
|
|
2638
|
+
}
|
|
2639
|
+
if (character === ')') {
|
|
2640
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
2641
|
+
continue;
|
|
2642
|
+
}
|
|
2643
|
+
if (character === '[') {
|
|
2644
|
+
bracketDepth += 1;
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
if (character === ']') {
|
|
2648
|
+
if (parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
2649
|
+
closingBracketIndex = index;
|
|
2650
|
+
break;
|
|
2651
|
+
}
|
|
2652
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
2653
|
+
continue;
|
|
2654
|
+
}
|
|
2655
|
+
if (character === '{') {
|
|
2656
|
+
braceDepth += 1;
|
|
2657
|
+
continue;
|
|
2658
|
+
}
|
|
2659
|
+
if (character === '}') {
|
|
2660
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
if (closingBracketIndex === -1 || sourcePosition < bodyStart || sourcePosition > closingBracketIndex) {
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
let itemStart = bodyStart;
|
|
2668
|
+
parenDepth = 0;
|
|
2669
|
+
bracketDepth = 0;
|
|
2670
|
+
braceDepth = 0;
|
|
2671
|
+
for (let index = bodyStart; index <= closingBracketIndex; index += 1) {
|
|
2672
|
+
const character = index === closingBracketIndex ? ',' : originalText[index];
|
|
2673
|
+
if (index < closingBracketIndex && (character === '"' || character === "'")) {
|
|
2674
|
+
index = scanQuotedAnnotationString(originalText, index) - 1;
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
if (index < closingBracketIndex && character === '(') {
|
|
2678
|
+
parenDepth += 1;
|
|
2679
|
+
continue;
|
|
2680
|
+
}
|
|
2681
|
+
if (index < closingBracketIndex && character === ')') {
|
|
2682
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
2683
|
+
continue;
|
|
2684
|
+
}
|
|
2685
|
+
if (index < closingBracketIndex && character === '[') {
|
|
2686
|
+
bracketDepth += 1;
|
|
2687
|
+
continue;
|
|
2688
|
+
}
|
|
2689
|
+
if (index < closingBracketIndex && character === ']') {
|
|
2690
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
if (index < closingBracketIndex && character === '{') {
|
|
2694
|
+
braceDepth += 1;
|
|
2695
|
+
continue;
|
|
2696
|
+
}
|
|
2697
|
+
if (index < closingBracketIndex && character === '}') {
|
|
2698
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
2699
|
+
continue;
|
|
2700
|
+
}
|
|
2701
|
+
if (character === ',' && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
2702
|
+
if (sourcePosition >= itemStart && sourcePosition <= index) {
|
|
2703
|
+
return parseAnnotationHoverItem(originalText.slice(itemStart, index), itemStart, index);
|
|
2704
|
+
}
|
|
2705
|
+
itemStart = index + 1;
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return null;
|
|
2709
|
+
}
|
|
2710
|
+
function builtinAnnotationHoverContents(annotationName) {
|
|
2711
|
+
const details = BUILTIN_ANNOTATION_HOVER_DETAILS[annotationName];
|
|
2712
|
+
if (!details) {
|
|
2713
|
+
return null;
|
|
2714
|
+
}
|
|
2715
|
+
return createMarkdownTextHoverContents(`**annotation** \`${annotationName}\``, [
|
|
2716
|
+
createMarkdownCodeBlock(details.syntax),
|
|
2717
|
+
details.summary,
|
|
2718
|
+
...details.details,
|
|
2719
|
+
]);
|
|
2720
|
+
}
|
|
2721
|
+
function annotationHover(preparedProject, filePath, sourcePosition, originalText) {
|
|
2722
|
+
const annotation = findAnnotationHoverItemAtPosition(originalText, sourcePosition);
|
|
2723
|
+
if (!annotation || annotation.name.length === 0) {
|
|
2724
|
+
return null;
|
|
2725
|
+
}
|
|
2726
|
+
const importedMacroDefinition = getImportedMacroDefinitionsForFile(preparedProject, filePath).get(annotation.name);
|
|
2727
|
+
if (importedMacroDefinition) {
|
|
2728
|
+
const details = signatureHoverDetails(importedMacroDefinition, annotation.name);
|
|
2729
|
+
const customBody = genericHoverBodyForImportedMacro(importedMacroDefinition, annotation.name);
|
|
2730
|
+
return {
|
|
2731
|
+
contents: customBody
|
|
2732
|
+
? createCustomMacroSummaryContents(annotation.name, customBody, details)
|
|
2733
|
+
: createMarkdownTextHoverContents(`**macro** \`${annotation.name}\``, details),
|
|
2734
|
+
range: createHoverRange(annotation.nameStart, annotation.nameEnd, originalText),
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
const builtinContents = builtinAnnotationHoverContents(annotation.name);
|
|
2738
|
+
if (builtinContents) {
|
|
2739
|
+
return {
|
|
2740
|
+
contents: builtinContents,
|
|
2741
|
+
range: createHoverRange(annotation.nameStart, annotation.nameEnd, originalText),
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
const syntax = annotation.argumentsText === undefined
|
|
2745
|
+
? `// #[${annotation.name}]`
|
|
2746
|
+
: `// #[${annotation.name}(${annotation.argumentsText})]`;
|
|
2747
|
+
return {
|
|
2748
|
+
contents: createMarkdownTextHoverContents(`**annotation** \`${annotation.name}\``, [
|
|
2749
|
+
createMarkdownCodeBlock(syntax),
|
|
2750
|
+
'soundscript parsed this as an annotation comment.',
|
|
2751
|
+
]),
|
|
2752
|
+
range: createHoverRange(annotation.nameStart, annotation.nameEnd, originalText),
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
function genericHoverBodyForImportedMacro(definition, macroName) {
|
|
2756
|
+
if (!definition.hover) {
|
|
2757
|
+
return null;
|
|
2758
|
+
}
|
|
2759
|
+
try {
|
|
2760
|
+
const syntheticText = buildSyntheticMacroHoverInvocationText(definition.signature, macroName);
|
|
2761
|
+
const parsed = parseMacroInvocationAt('<macro-hover>.sts', syntheticText, 0);
|
|
2762
|
+
if (!('reason' in parsed)) {
|
|
2763
|
+
const syntheticContext = createSyntaxOnlyMacroContext(parsed, syntheticText);
|
|
2764
|
+
const syntheticNode = parseMacroSyntaxNodeForDefinition(definition, syntheticContext) ??
|
|
2765
|
+
syntheticContext.parsedSyntax();
|
|
2766
|
+
if (syntheticNode) {
|
|
2767
|
+
const parsedHover = definition.hover({ node: syntheticNode, offset: 0 })?.contents ?? null;
|
|
2768
|
+
if (parsedHover) {
|
|
2769
|
+
return parsedHover;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
catch {
|
|
2775
|
+
// Fall through to the shallow invocation fallback below.
|
|
2776
|
+
}
|
|
2777
|
+
const syntheticNode = {
|
|
2778
|
+
args: [],
|
|
2779
|
+
block: null,
|
|
2780
|
+
declaration: null,
|
|
2781
|
+
form: 'arglist',
|
|
2782
|
+
hasBlock: false,
|
|
2783
|
+
kind: 'invocation',
|
|
2784
|
+
name: macroName,
|
|
2785
|
+
span: { fileName: '<macro-hover>', start: 0, end: macroName.length },
|
|
2786
|
+
text() {
|
|
2787
|
+
return macroName;
|
|
2788
|
+
},
|
|
2789
|
+
};
|
|
2790
|
+
try {
|
|
2791
|
+
return definition.hover({ node: syntheticNode, offset: 0 })?.contents ?? null;
|
|
2792
|
+
}
|
|
2793
|
+
catch {
|
|
2794
|
+
return null;
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
function buildSyntheticMacroHoverInvocationText(signature, macroName) {
|
|
2798
|
+
if (!signature || signature.cases.length === 0) {
|
|
2799
|
+
return `#${macroName}`;
|
|
2800
|
+
}
|
|
2801
|
+
const [signatureCase] = signature.cases;
|
|
2802
|
+
const exprArgs = signatureCase.operands
|
|
2803
|
+
.filter((operand) => operand.kind === 'expr')
|
|
2804
|
+
.map((operand, index) => syntheticExprOperandText(operand, index));
|
|
2805
|
+
const templateArg = signatureCase.operands.find((operand) => operand.kind === 'template');
|
|
2806
|
+
const blockArg = signatureCase.operands.find((operand) => operand.kind === 'block');
|
|
2807
|
+
const declArg = signatureCase.operands.find((operand) => operand.kind === 'decl');
|
|
2808
|
+
let text = `#${macroName}`;
|
|
2809
|
+
if (templateArg) {
|
|
2810
|
+
text += ` ${syntheticTemplateOperandText()}`;
|
|
2811
|
+
}
|
|
2812
|
+
else if (exprArgs.length === 1) {
|
|
2813
|
+
text += `(${exprArgs[0]})`;
|
|
2814
|
+
}
|
|
2815
|
+
else if (exprArgs.length > 1) {
|
|
2816
|
+
text += `(${exprArgs.join(', ')})`;
|
|
2817
|
+
}
|
|
2818
|
+
if (blockArg) {
|
|
2819
|
+
text += ' { }';
|
|
2820
|
+
}
|
|
2821
|
+
if (declArg) {
|
|
2822
|
+
text += exprArgs.length === 0 && !templateArg && !blockArg
|
|
2823
|
+
? ' function macroHoverProbe() {}'
|
|
2824
|
+
: ' class MacroHoverProbe {}';
|
|
2825
|
+
}
|
|
2826
|
+
return text;
|
|
2827
|
+
}
|
|
2828
|
+
function syntheticExprOperandText(operand, index) {
|
|
2829
|
+
switch (operand.refinement?.kind) {
|
|
2830
|
+
case 'array_literal':
|
|
2831
|
+
return '[]';
|
|
2832
|
+
case 'call':
|
|
2833
|
+
return 'probe()';
|
|
2834
|
+
case 'function':
|
|
2835
|
+
return '() => value';
|
|
2836
|
+
case 'identifier':
|
|
2837
|
+
return 'value';
|
|
2838
|
+
default:
|
|
2839
|
+
return index === 0 ? 'value' : `value${index + 1}`;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
function syntheticTemplateOperandText() {
|
|
2843
|
+
return '`value`';
|
|
2844
|
+
}
|
|
2845
|
+
function completionHookForMacroInvocation(preparedProject, filePath, match, sourcePosition) {
|
|
2846
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
2847
|
+
if (!artifacts) {
|
|
2848
|
+
return null;
|
|
2849
|
+
}
|
|
2850
|
+
const fragment = findMacroFragmentAtPosition(artifacts.fragments, sourcePosition);
|
|
2851
|
+
if (fragment?.completions) {
|
|
2852
|
+
const completions = fragment.completions(sourcePosition);
|
|
2853
|
+
return completions.length > 0 ? [...completions] : null;
|
|
2854
|
+
}
|
|
2855
|
+
if (!artifacts.definition.completions || !artifacts.node) {
|
|
2856
|
+
return null;
|
|
2857
|
+
}
|
|
2858
|
+
if (!containsPosition(artifacts.node.span.start, artifacts.node.span.end, sourcePosition)) {
|
|
2859
|
+
return null;
|
|
2860
|
+
}
|
|
2861
|
+
const completions = artifacts.definition.completions({
|
|
2862
|
+
node: artifacts.node,
|
|
2863
|
+
offset: sourcePosition - artifacts.node.span.start,
|
|
2864
|
+
});
|
|
2865
|
+
return completions.length > 0 ? [...completions] : null;
|
|
2866
|
+
}
|
|
2867
|
+
function bindingOccurrencesForMacroInvocation(preparedProject, filePath, match) {
|
|
2868
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
2869
|
+
if (!artifacts) {
|
|
2870
|
+
return null;
|
|
2871
|
+
}
|
|
2872
|
+
const bindings = [
|
|
2873
|
+
...artifacts.fragments.flatMap((fragment) => fragment.bindings ?? []),
|
|
2874
|
+
];
|
|
2875
|
+
if (artifacts.definition.bindings && artifacts.node) {
|
|
2876
|
+
bindings.push(...artifacts.definition.bindings({ node: artifacts.node }));
|
|
2877
|
+
}
|
|
2878
|
+
return bindings.length > 0 ? bindings : null;
|
|
2879
|
+
}
|
|
2880
|
+
function findMacroBindingOccurrence(preparedProject, filePath, sourcePosition) {
|
|
2881
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
2882
|
+
const match = findResolvedMacroContainingPosition(filePath, sourcePosition, collected);
|
|
2883
|
+
if (!match) {
|
|
2884
|
+
return null;
|
|
2885
|
+
}
|
|
2886
|
+
const occurrences = bindingOccurrencesForMacroInvocation(preparedProject, filePath, match);
|
|
2887
|
+
if (!occurrences) {
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
const occurrence = occurrences.find((entry) => containsPosition(entry.span.start, entry.span.end, sourcePosition));
|
|
2891
|
+
if (!occurrence) {
|
|
2892
|
+
return null;
|
|
2893
|
+
}
|
|
2894
|
+
return { match, occurrence, occurrences };
|
|
2895
|
+
}
|
|
2896
|
+
function createSourceSpanLocation(filePath, originalText, span) {
|
|
2897
|
+
return {
|
|
2898
|
+
uri: toFileUrl(filePath).href,
|
|
2899
|
+
range: createRangeFromOffsets(span.start, span.end, originalText),
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
function hoverMacroInvocation(preparedProject, filePath, sourcePosition, originalText) {
|
|
2903
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
2904
|
+
const match = findResolvedMacroContainingPosition(filePath, sourcePosition, collected);
|
|
2905
|
+
if (!match) {
|
|
2906
|
+
return null;
|
|
2907
|
+
}
|
|
2908
|
+
const invocation = match.resolved.placeholder.invocation;
|
|
2909
|
+
const hookHover = hoverHookForMacroInvocation(preparedProject, filePath, match, sourcePosition, originalText);
|
|
2910
|
+
if (hookHover) {
|
|
2911
|
+
return hookHover;
|
|
2912
|
+
}
|
|
2913
|
+
if (!isIdentifierPart(originalText[sourcePosition])) {
|
|
2914
|
+
return null;
|
|
2915
|
+
}
|
|
2916
|
+
const hookAnalysisNode = resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, sourcePosition);
|
|
2917
|
+
if (hookAnalysisNode) {
|
|
2918
|
+
if ('kind' in hookAnalysisNode) {
|
|
2919
|
+
return createMacroSummaryHover(hookAnalysisNode.invocation, originalText);
|
|
2920
|
+
}
|
|
2921
|
+
const value = formatSymbolHoverCode(hookAnalysisNode.checker, hookAnalysisNode.node) ??
|
|
2922
|
+
hookAnalysisNode.semantics.typeOfNode(hookAnalysisNode.node).displayText;
|
|
2923
|
+
return {
|
|
2924
|
+
contents: createMarkdownHoverContents(value),
|
|
2925
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
const expressionNode = resolveExpressionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
2929
|
+
if (expressionNode) {
|
|
2930
|
+
if ('kind' in expressionNode) {
|
|
2931
|
+
return createMacroSummaryHover(expressionNode.invocation, originalText);
|
|
2932
|
+
}
|
|
2933
|
+
const value = formatSymbolHoverCode(expressionNode.checker, expressionNode.node) ??
|
|
2934
|
+
expressionNode.semantics.typeOfNode(expressionNode.node).displayText;
|
|
2935
|
+
return {
|
|
2936
|
+
contents: createMarkdownHoverContents(value),
|
|
2937
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
const blockNode = resolveBlockNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
2941
|
+
if (blockNode) {
|
|
2942
|
+
if ('kind' in blockNode) {
|
|
2943
|
+
return createMacroSummaryHover(blockNode.invocation, originalText);
|
|
2944
|
+
}
|
|
2945
|
+
const value = formatSymbolHoverCode(blockNode.checker, blockNode.node) ??
|
|
2946
|
+
blockNode.semantics.typeOfNode(blockNode.node).displayText;
|
|
2947
|
+
return {
|
|
2948
|
+
contents: createMarkdownHoverContents(value),
|
|
2949
|
+
range: createTokenHoverRange(sourcePosition, originalText),
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
const summaryHover = summaryHoverForMacroInvocation(preparedProject, filePath, match, sourcePosition, originalText);
|
|
2953
|
+
if (summaryHover) {
|
|
2954
|
+
return summaryHover;
|
|
2955
|
+
}
|
|
2956
|
+
return createMacroSummaryHover(invocation, originalText);
|
|
2957
|
+
}
|
|
2958
|
+
function isSuppressedFallbackHoverNode(node) {
|
|
2959
|
+
if (ts.isIdentifier(node) ||
|
|
2960
|
+
ts.isPrivateIdentifier(node) ||
|
|
2961
|
+
ts.isStringLiteralLike(node) ||
|
|
2962
|
+
ts.isNumericLiteral(node) ||
|
|
2963
|
+
ts.isNoSubstitutionTemplateLiteral(node) ||
|
|
2964
|
+
node.kind === ts.SyntaxKind.ThisKeyword ||
|
|
2965
|
+
node.kind === ts.SyntaxKind.SuperKeyword ||
|
|
2966
|
+
node.kind === ts.SyntaxKind.TrueKeyword ||
|
|
2967
|
+
node.kind === ts.SyntaxKind.FalseKeyword ||
|
|
2968
|
+
node.kind === ts.SyntaxKind.NullKeyword) {
|
|
2969
|
+
return false;
|
|
2970
|
+
}
|
|
2971
|
+
if (ts.tokenToString(node.kind) !== undefined) {
|
|
2972
|
+
return true;
|
|
2973
|
+
}
|
|
2974
|
+
return ts.isReturnStatement(node);
|
|
2975
|
+
}
|
|
2976
|
+
function importDeclarationForNode(node) {
|
|
2977
|
+
let current = node;
|
|
2978
|
+
while (current && !ts.isSourceFile(current)) {
|
|
2979
|
+
if (ts.isImportDeclaration(current)) {
|
|
2980
|
+
return current;
|
|
2981
|
+
}
|
|
2982
|
+
current = current.parent;
|
|
2983
|
+
}
|
|
2984
|
+
return null;
|
|
2985
|
+
}
|
|
2986
|
+
function importBindingHoverKindForDeclaration(declaration) {
|
|
2987
|
+
if (ts.isImportSpecifier(declaration)) {
|
|
2988
|
+
const importClause = declaration.parent.parent;
|
|
2989
|
+
return declaration.isTypeOnly || (ts.isImportClause(importClause) && importClause.isTypeOnly)
|
|
2990
|
+
? 'type'
|
|
2991
|
+
: 'value';
|
|
2992
|
+
}
|
|
2993
|
+
if (ts.isNamespaceImport(declaration)) {
|
|
2994
|
+
const importClause = declaration.parent.parent;
|
|
2995
|
+
return ts.isImportClause(importClause) && importClause.isTypeOnly ? 'type' : 'value';
|
|
2996
|
+
}
|
|
2997
|
+
if (ts.isImportClause(declaration)) {
|
|
2998
|
+
return declaration.isTypeOnly ? 'type' : 'value';
|
|
2999
|
+
}
|
|
3000
|
+
return null;
|
|
3001
|
+
}
|
|
3002
|
+
function lookupFilePathForSingleFileProgram(filePath) {
|
|
3003
|
+
return filePath.endsWith('.sts') ? `${filePath}.ts` : filePath;
|
|
3004
|
+
}
|
|
3005
|
+
function createSingleFileLookupProgram(filePath, originalText) {
|
|
3006
|
+
const lookupFilePath = lookupFilePathForSingleFileProgram(filePath);
|
|
3007
|
+
const options = {
|
|
3008
|
+
allowJs: true,
|
|
3009
|
+
module: ts.ModuleKind.ESNext,
|
|
3010
|
+
noLib: true,
|
|
3011
|
+
noResolve: true,
|
|
3012
|
+
target: ts.ScriptTarget.Latest,
|
|
3013
|
+
};
|
|
3014
|
+
const host = ts.createCompilerHost(options, true);
|
|
3015
|
+
host.getSourceFile = (candidatePath, languageVersion) => {
|
|
3016
|
+
if (candidatePath !== lookupFilePath) {
|
|
3017
|
+
return undefined;
|
|
3018
|
+
}
|
|
3019
|
+
return ts.createSourceFile(candidatePath, originalText, languageVersion, true, ts.ScriptKind.TS);
|
|
3020
|
+
};
|
|
3021
|
+
host.fileExists = (candidatePath) => candidatePath === lookupFilePath;
|
|
3022
|
+
host.readFile = (candidatePath) => candidatePath === lookupFilePath ? originalText : undefined;
|
|
3023
|
+
host.writeFile = () => { };
|
|
3024
|
+
return {
|
|
3025
|
+
lookupFilePath,
|
|
3026
|
+
program: ts.createProgram([lookupFilePath], options, host),
|
|
3027
|
+
};
|
|
3028
|
+
}
|
|
3029
|
+
function createResolvedSingleFileLookupProgram(filePath, originalText, compilerOptions) {
|
|
3030
|
+
const lookupFilePath = lookupFilePathForSingleFileProgram(filePath);
|
|
3031
|
+
const options = {
|
|
3032
|
+
...compilerOptions,
|
|
3033
|
+
allowJs: true,
|
|
3034
|
+
noEmit: true,
|
|
3035
|
+
};
|
|
3036
|
+
const host = ts.createCompilerHost(options, true);
|
|
3037
|
+
const baseGetSourceFile = host.getSourceFile.bind(host);
|
|
3038
|
+
const baseFileExists = host.fileExists.bind(host);
|
|
3039
|
+
const baseReadFile = host.readFile.bind(host);
|
|
3040
|
+
host.getSourceFile = (candidatePath, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
3041
|
+
if (candidatePath === lookupFilePath) {
|
|
3042
|
+
return ts.createSourceFile(candidatePath, originalText, languageVersion, true, ts.ScriptKind.TS);
|
|
3043
|
+
}
|
|
3044
|
+
return baseGetSourceFile(candidatePath, languageVersion, onError, shouldCreateNewSourceFile);
|
|
3045
|
+
};
|
|
3046
|
+
host.fileExists = (candidatePath) => candidatePath === lookupFilePath || baseFileExists(candidatePath);
|
|
3047
|
+
host.readFile = (candidatePath) => candidatePath === lookupFilePath ? originalText : baseReadFile(candidatePath);
|
|
3048
|
+
host.writeFile = () => { };
|
|
3049
|
+
return {
|
|
3050
|
+
lookupFilePath,
|
|
3051
|
+
program: ts.createProgram([lookupFilePath], options, host),
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
function resolvedTypeImportHoverCode(filePath, originalText, sourcePosition, compilerOptions) {
|
|
3055
|
+
const { program, lookupFilePath } = createResolvedSingleFileLookupProgram(filePath, originalText, compilerOptions);
|
|
3056
|
+
const sourceFile = program.getSourceFile(lookupFilePath);
|
|
3057
|
+
if (!sourceFile) {
|
|
3058
|
+
return null;
|
|
3059
|
+
}
|
|
3060
|
+
const node = findDeepestNodeContainingPosition(sourceFile, sourcePosition);
|
|
3061
|
+
if (!node || !ts.isIdentifier(node)) {
|
|
3062
|
+
return null;
|
|
3063
|
+
}
|
|
3064
|
+
const checker = program.getTypeChecker();
|
|
3065
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
3066
|
+
if (!symbol) {
|
|
3067
|
+
return null;
|
|
3068
|
+
}
|
|
3069
|
+
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
3070
|
+
if (!declaration) {
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
return formatSymbolHoverCode(checker, getDefinitionTargetNode(declaration)) ??
|
|
3074
|
+
formatSymbolHoverCode(checker, node) ??
|
|
3075
|
+
null;
|
|
3076
|
+
}
|
|
3077
|
+
function importBindingDeclarationForSymbol(symbol) {
|
|
3078
|
+
const declarations = symbol.declarations ??
|
|
3079
|
+
(symbol.valueDeclaration ? [symbol.valueDeclaration] : []);
|
|
3080
|
+
return declarations.find((declaration) => ts.isImportSpecifier(declaration) ||
|
|
3081
|
+
ts.isNamespaceImport(declaration) ||
|
|
3082
|
+
ts.isImportClause(declaration)) ?? null;
|
|
3083
|
+
}
|
|
3084
|
+
function projectedForeignImportHover(preparedProject, filePath, sourcePosition, originalText) {
|
|
3085
|
+
const { program, lookupFilePath } = createSingleFileLookupProgram(filePath, originalText);
|
|
3086
|
+
const sourceFile = program.getSourceFile(lookupFilePath);
|
|
3087
|
+
if (!sourceFile) {
|
|
3088
|
+
return null;
|
|
3089
|
+
}
|
|
3090
|
+
const node = findDeepestNodeContainingPosition(sourceFile, sourcePosition);
|
|
3091
|
+
if (!node || !ts.isIdentifier(node)) {
|
|
3092
|
+
return null;
|
|
3093
|
+
}
|
|
3094
|
+
const symbol = program.getTypeChecker().getSymbolAtLocation(node);
|
|
3095
|
+
if (!symbol) {
|
|
3096
|
+
return null;
|
|
3097
|
+
}
|
|
3098
|
+
const bindingDeclaration = importBindingDeclarationForSymbol(symbol);
|
|
3099
|
+
if (!bindingDeclaration) {
|
|
3100
|
+
return null;
|
|
3101
|
+
}
|
|
3102
|
+
const hoverKind = importBindingHoverKindForDeclaration(bindingDeclaration);
|
|
3103
|
+
if (!hoverKind) {
|
|
3104
|
+
return null;
|
|
3105
|
+
}
|
|
3106
|
+
const importDeclaration = importDeclarationForNode(bindingDeclaration);
|
|
3107
|
+
if (!importDeclaration || !ts.isStringLiteral(importDeclaration.moduleSpecifier)) {
|
|
3108
|
+
return null;
|
|
3109
|
+
}
|
|
3110
|
+
if (!isUnsoundImportedModuleForTypeProjection(importDeclaration.moduleSpecifier.text, filePath, preparedProject.preparedProgram.options, ts.sys)) {
|
|
3111
|
+
return null;
|
|
3112
|
+
}
|
|
3113
|
+
if (hoverKind === 'value' &&
|
|
3114
|
+
ts.isImportSpecifier(bindingDeclaration) &&
|
|
3115
|
+
!collectProjectedUnknownValueExportNames(importDeclaration.moduleSpecifier.text, filePath, preparedProject.preparedProgram.options, ts.sys).has((bindingDeclaration.propertyName ?? bindingDeclaration.name).text)) {
|
|
3116
|
+
return null;
|
|
3117
|
+
}
|
|
3118
|
+
const hoverCode = hoverKind === 'type'
|
|
3119
|
+
? resolvedTypeImportHoverCode(filePath, originalText, sourcePosition, preparedProject.preparedProgram.options)
|
|
3120
|
+
: `const ${node.text}: unknown`;
|
|
3121
|
+
if (!hoverCode) {
|
|
3122
|
+
return null;
|
|
3123
|
+
}
|
|
3124
|
+
return {
|
|
3125
|
+
contents: createMarkdownHoverContents(hoverCode),
|
|
3126
|
+
range: createHoverRange(node.getStart(sourceFile), node.getEnd(), originalText),
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
function resolveSymbolAtNode(checker, node) {
|
|
3130
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
3131
|
+
if (!symbol) {
|
|
3132
|
+
return undefined;
|
|
3133
|
+
}
|
|
3134
|
+
if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {
|
|
3135
|
+
const aliasedSymbol = checker.getAliasedSymbol(symbol);
|
|
3136
|
+
if (aliasedSymbol) {
|
|
3137
|
+
return aliasedSymbol;
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
return symbol;
|
|
3141
|
+
}
|
|
3142
|
+
function analysisContainerName(node) {
|
|
3143
|
+
if (ts.isClassDeclaration(node) ||
|
|
3144
|
+
ts.isClassExpression(node) ||
|
|
3145
|
+
ts.isFunctionDeclaration(node) ||
|
|
3146
|
+
ts.isFunctionExpression(node) ||
|
|
3147
|
+
ts.isInterfaceDeclaration(node) ||
|
|
3148
|
+
ts.isMethodDeclaration(node) ||
|
|
3149
|
+
ts.isMethodSignature(node) ||
|
|
3150
|
+
ts.isModuleDeclaration(node) ||
|
|
3151
|
+
ts.isTypeAliasDeclaration(node)) {
|
|
3152
|
+
return node.name && ts.isIdentifier(node.name) ? node.name.text : null;
|
|
3153
|
+
}
|
|
3154
|
+
return null;
|
|
3155
|
+
}
|
|
3156
|
+
function namedContainerPath(node) {
|
|
3157
|
+
const path = [];
|
|
3158
|
+
let current = node.parent;
|
|
3159
|
+
while (current && !ts.isSourceFile(current)) {
|
|
3160
|
+
const name = analysisContainerName(current);
|
|
3161
|
+
if (name) {
|
|
3162
|
+
path.push({
|
|
3163
|
+
kind: current.kind,
|
|
3164
|
+
name,
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
current = current.parent;
|
|
3168
|
+
}
|
|
3169
|
+
return path.reverse();
|
|
3170
|
+
}
|
|
3171
|
+
function findNamedDescendantContainer(root, target) {
|
|
3172
|
+
let match = null;
|
|
3173
|
+
function visit(node) {
|
|
3174
|
+
if (match) {
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
if (node.kind === target.kind && analysisContainerName(node) === target.name) {
|
|
3178
|
+
match = node;
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
ts.forEachChild(node, visit);
|
|
3182
|
+
}
|
|
3183
|
+
ts.forEachChild(root, visit);
|
|
3184
|
+
return match;
|
|
3185
|
+
}
|
|
3186
|
+
function resolveAnalysisContainerForSourceNode(sourceNode, analysisSourceFile) {
|
|
3187
|
+
let container = analysisSourceFile;
|
|
3188
|
+
for (const target of namedContainerPath(sourceNode)) {
|
|
3189
|
+
const next = findNamedDescendantContainer(container, target);
|
|
3190
|
+
if (!next) {
|
|
3191
|
+
break;
|
|
3192
|
+
}
|
|
3193
|
+
container = next;
|
|
3194
|
+
}
|
|
3195
|
+
return container;
|
|
3196
|
+
}
|
|
3197
|
+
function isDeclarationNameNode(node) {
|
|
3198
|
+
const parent = node.parent;
|
|
3199
|
+
return parent?.name === node;
|
|
3200
|
+
}
|
|
3201
|
+
function countNamedNodeOccurrence(container, target) {
|
|
3202
|
+
if ((!ts.isIdentifier(target) && !ts.isPrivateIdentifier(target)) || !('text' in target)) {
|
|
3203
|
+
return null;
|
|
3204
|
+
}
|
|
3205
|
+
let occurrence = 0;
|
|
3206
|
+
let found = null;
|
|
3207
|
+
const targetText = target.text;
|
|
3208
|
+
const visit = (node) => {
|
|
3209
|
+
if (found !== null) {
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
if ((ts.isIdentifier(node) || ts.isPrivateIdentifier(node)) &&
|
|
3213
|
+
'text' in node &&
|
|
3214
|
+
node.text === targetText) {
|
|
3215
|
+
if (node === target) {
|
|
3216
|
+
found = occurrence;
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
occurrence += 1;
|
|
3220
|
+
}
|
|
3221
|
+
ts.forEachChild(node, visit);
|
|
3222
|
+
};
|
|
3223
|
+
visit(container);
|
|
3224
|
+
return found;
|
|
3225
|
+
}
|
|
3226
|
+
function findNamedNodeOccurrence(container, kind, name, occurrenceIndex) {
|
|
3227
|
+
let occurrence = 0;
|
|
3228
|
+
let found = null;
|
|
3229
|
+
const visit = (node) => {
|
|
3230
|
+
if (found !== null) {
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (node.kind === kind &&
|
|
3234
|
+
(ts.isIdentifier(node) || ts.isPrivateIdentifier(node)) &&
|
|
3235
|
+
'text' in node &&
|
|
3236
|
+
node.text === name) {
|
|
3237
|
+
if (occurrence === occurrenceIndex) {
|
|
3238
|
+
found = node;
|
|
3239
|
+
return;
|
|
3240
|
+
}
|
|
3241
|
+
occurrence += 1;
|
|
3242
|
+
}
|
|
3243
|
+
ts.forEachChild(node, visit);
|
|
3244
|
+
};
|
|
3245
|
+
visit(container);
|
|
3246
|
+
return found;
|
|
3247
|
+
}
|
|
3248
|
+
function symbolDeclaredWithinContainer(symbol, container) {
|
|
3249
|
+
const declarations = symbol.declarations ??
|
|
3250
|
+
(symbol.valueDeclaration ? [symbol.valueDeclaration] : []);
|
|
3251
|
+
return declarations.some((declaration) => {
|
|
3252
|
+
let current = declaration;
|
|
3253
|
+
while (current) {
|
|
3254
|
+
if (current === container) {
|
|
3255
|
+
return true;
|
|
3256
|
+
}
|
|
3257
|
+
current = current.parent;
|
|
3258
|
+
}
|
|
3259
|
+
return false;
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
function getAnalysisBackedHoverDetails(preparedProject, filePath, sourceNode) {
|
|
3263
|
+
if (!ts.isIdentifier(sourceNode) && !ts.isPrivateIdentifier(sourceNode)) {
|
|
3264
|
+
return null;
|
|
3265
|
+
}
|
|
3266
|
+
const analysisSourceFile = preparedProject.program.getSourceFile(preparedProject.analysisPreparedProgram.toProgramFileName(filePath));
|
|
3267
|
+
if (!analysisSourceFile) {
|
|
3268
|
+
return null;
|
|
3269
|
+
}
|
|
3270
|
+
const analysisChecker = preparedProject.analysisContext.checker;
|
|
3271
|
+
const sourceContainer = resolveAnalysisContainerForSourceNode(sourceNode, sourceNode.getSourceFile());
|
|
3272
|
+
const analysisContainer = resolveAnalysisContainerForSourceNode(sourceNode, analysisSourceFile);
|
|
3273
|
+
const occurrenceIndex = countNamedNodeOccurrence(sourceContainer, sourceNode);
|
|
3274
|
+
const analysisNode = occurrenceIndex === null || !('text' in sourceNode)
|
|
3275
|
+
? null
|
|
3276
|
+
: findNamedNodeOccurrence(analysisContainer, sourceNode.kind, sourceNode.text, occurrenceIndex);
|
|
3277
|
+
if (analysisNode &&
|
|
3278
|
+
!ts.isSourceFile(analysisNode) &&
|
|
3279
|
+
(ts.isIdentifier(analysisNode) || ts.isPrivateIdentifier(analysisNode)) &&
|
|
3280
|
+
analysisNode.getText(analysisSourceFile) === sourceNode.getText(sourceNode.getSourceFile())) {
|
|
3281
|
+
const typeText = analysisChecker.typeToString(analysisChecker.getTypeAtLocation(analysisNode), analysisNode, ts.TypeFormatFlags.NoTruncation);
|
|
3282
|
+
return {
|
|
3283
|
+
code: formatSymbolHoverCode(analysisChecker, analysisNode) ?? typeText,
|
|
3284
|
+
typeText,
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
const container = resolveAnalysisContainerForSourceNode(sourceNode, analysisSourceFile);
|
|
3288
|
+
const scopeFlags = ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace |
|
|
3289
|
+
ts.SymbolFlags.Alias;
|
|
3290
|
+
const candidates = analysisChecker.getSymbolsInScope(container, scopeFlags).filter((candidate) => candidate.name === sourceNode.text);
|
|
3291
|
+
if (candidates.length === 0) {
|
|
3292
|
+
return null;
|
|
3293
|
+
}
|
|
3294
|
+
const symbol = candidates.find((candidate) => symbolDeclaredWithinContainer(candidate, container)) ??
|
|
3295
|
+
candidates[0];
|
|
3296
|
+
const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
3297
|
+
if (!declaration) {
|
|
3298
|
+
return null;
|
|
3299
|
+
}
|
|
3300
|
+
const definitionTarget = getDefinitionTargetNode(declaration);
|
|
3301
|
+
const typeText = analysisChecker.typeToString(analysisChecker.getTypeAtLocation(definitionTarget), definitionTarget, ts.TypeFormatFlags.NoTruncation);
|
|
3302
|
+
return {
|
|
3303
|
+
code: formatSymbolHoverCode(analysisChecker, definitionTarget) ?? typeText,
|
|
3304
|
+
typeText,
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3307
|
+
function getSourceFileModuleSymbol(checker, sourceFile) {
|
|
3308
|
+
return checker.getSymbolAtLocation(sourceFile) ??
|
|
3309
|
+
sourceFile.symbol;
|
|
3310
|
+
}
|
|
3311
|
+
function canonicalSoundscriptSourceFileName(preparedProject, fileName) {
|
|
3312
|
+
const sourceFileName = toProjectedDeclarationSourceFileName(preparedProject.preparedProgram.toSourceFileName(fileName));
|
|
3313
|
+
return isSoundscriptSourceFile(sourceFileName) ? sourceFileName : null;
|
|
3314
|
+
}
|
|
3315
|
+
function collectCrossViewDeclarations(preparedProject, symbol, relatedViews) {
|
|
3316
|
+
const declarations = symbol.declarations ??
|
|
3317
|
+
(symbol.valueDeclaration ? [symbol.valueDeclaration] : []);
|
|
3318
|
+
if (declarations.length === 0) {
|
|
3319
|
+
return [];
|
|
3320
|
+
}
|
|
3321
|
+
const symbolName = symbol.getName();
|
|
3322
|
+
if (symbolName.length === 0) {
|
|
3323
|
+
return [];
|
|
3324
|
+
}
|
|
3325
|
+
const matches = [];
|
|
3326
|
+
const seen = new Set();
|
|
3327
|
+
for (const declaration of declarations) {
|
|
3328
|
+
const soundscriptSourceFileName = canonicalSoundscriptSourceFileName(preparedProject, declaration.getSourceFile().fileName);
|
|
3329
|
+
if (!soundscriptSourceFileName) {
|
|
3330
|
+
continue;
|
|
3331
|
+
}
|
|
3332
|
+
for (const view of relatedViews) {
|
|
3333
|
+
const candidateFileNames = [
|
|
3334
|
+
view.preparedProgram.toProgramFileName(soundscriptSourceFileName),
|
|
3335
|
+
view.preparedProgram.toProjectedDeclarationFileName(soundscriptSourceFileName),
|
|
3336
|
+
];
|
|
3337
|
+
for (const candidateFileName of candidateFileNames) {
|
|
3338
|
+
const relatedSourceFile = view.program.getSourceFile(candidateFileName);
|
|
3339
|
+
if (!relatedSourceFile) {
|
|
3340
|
+
continue;
|
|
3341
|
+
}
|
|
3342
|
+
const moduleSymbol = getSourceFileModuleSymbol(view.analysisContext.checker, relatedSourceFile);
|
|
3343
|
+
if (!moduleSymbol) {
|
|
3344
|
+
continue;
|
|
3345
|
+
}
|
|
3346
|
+
const exportedSymbol = view.analysisContext.checker.getExportsOfModule(moduleSymbol).find((candidate) => candidate.getName() === symbolName);
|
|
3347
|
+
if (!exportedSymbol) {
|
|
3348
|
+
continue;
|
|
3349
|
+
}
|
|
3350
|
+
const exportedDeclarations = exportedSymbol.declarations ??
|
|
3351
|
+
(exportedSymbol.valueDeclaration ? [exportedSymbol.valueDeclaration] : []);
|
|
3352
|
+
for (const exportedDeclaration of exportedDeclarations) {
|
|
3353
|
+
if (exportedDeclaration.getSourceFile() !== relatedSourceFile) {
|
|
3354
|
+
continue;
|
|
3355
|
+
}
|
|
3356
|
+
const key = `${relatedSourceFile.fileName}:${exportedDeclaration.getStart(relatedSourceFile)}:${exportedDeclaration.getEnd()}`;
|
|
3357
|
+
if (seen.has(key)) {
|
|
3358
|
+
continue;
|
|
3359
|
+
}
|
|
3360
|
+
seen.add(key);
|
|
3361
|
+
matches.push({
|
|
3362
|
+
declaration: exportedDeclaration,
|
|
3363
|
+
view,
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
return matches;
|
|
3370
|
+
}
|
|
3371
|
+
function getTargetSymbolKeys(preparedProject, symbol, relatedViews = [], sourceFile, macroSourceMap) {
|
|
3372
|
+
const declarations = symbol.declarations ??
|
|
3373
|
+
(symbol.valueDeclaration ? [symbol.valueDeclaration] : []);
|
|
3374
|
+
const keys = new Set();
|
|
3375
|
+
for (const declaration of declarations) {
|
|
3376
|
+
const symbolName = symbol.getName();
|
|
3377
|
+
const sourceBackedLocation = createSourceBackedProjectedDefinitionLocation(preparedProject, declaration, symbolName) ??
|
|
3378
|
+
createSourceBackedDefinitionLocation(preparedProject, declaration, symbolName);
|
|
3379
|
+
if (sourceBackedLocation) {
|
|
3380
|
+
keys.add(createLocationKey(sourceBackedLocation));
|
|
3381
|
+
continue;
|
|
3382
|
+
}
|
|
3383
|
+
const targetNode = getDefinitionTargetNode(declaration);
|
|
3384
|
+
const location = createDefinitionLocation(preparedProject, targetNode, declaration.getSourceFile(), sourceFile && declaration.getSourceFile() === sourceFile ? macroSourceMap : undefined);
|
|
3385
|
+
if (location) {
|
|
3386
|
+
keys.add(createLocationKey(location));
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
for (const match of collectCrossViewDeclarations(preparedProject, symbol, relatedViews)) {
|
|
3390
|
+
const symbolName = symbol.getName();
|
|
3391
|
+
const sourceBackedLocation = createSourceBackedProjectedDefinitionLocation(match.view, match.declaration, symbolName) ??
|
|
3392
|
+
createSourceBackedDefinitionLocation(match.view, match.declaration, symbolName);
|
|
3393
|
+
if (sourceBackedLocation) {
|
|
3394
|
+
keys.add(createLocationKey(sourceBackedLocation));
|
|
3395
|
+
continue;
|
|
3396
|
+
}
|
|
3397
|
+
const targetNode = getDefinitionTargetNode(match.declaration);
|
|
3398
|
+
const location = createDefinitionLocation(match.view, targetNode, match.declaration.getSourceFile());
|
|
3399
|
+
if (location) {
|
|
3400
|
+
keys.add(createLocationKey(location));
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
return keys;
|
|
3404
|
+
}
|
|
3405
|
+
function isExportedStatement(statement) {
|
|
3406
|
+
return (ts.canHaveModifiers(statement) ? ts.getModifiers(statement) : undefined)
|
|
3407
|
+
?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
3408
|
+
}
|
|
3409
|
+
function findTopLevelNamedDeclarationNode(sourceFile, symbolName) {
|
|
3410
|
+
for (const statement of sourceFile.statements) {
|
|
3411
|
+
if (ts.isVariableStatement(statement)) {
|
|
3412
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
3413
|
+
if (ts.isIdentifier(declaration.name) && declaration.name.text === symbolName) {
|
|
3414
|
+
return declaration.name;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
if ((ts.isFunctionDeclaration(statement) ||
|
|
3420
|
+
ts.isClassDeclaration(statement) ||
|
|
3421
|
+
ts.isInterfaceDeclaration(statement) ||
|
|
3422
|
+
ts.isTypeAliasDeclaration(statement) ||
|
|
3423
|
+
ts.isEnumDeclaration(statement)) &&
|
|
3424
|
+
statement.name?.text === symbolName) {
|
|
3425
|
+
return statement.name;
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
return undefined;
|
|
3429
|
+
}
|
|
3430
|
+
function createSourceBackedProjectedDefinitionLocation(preparedProject, declaration, symbolName) {
|
|
3431
|
+
const declarationFileName = declaration.getSourceFile().fileName;
|
|
3432
|
+
if (!isProjectedSoundscriptDeclarationFile(declarationFileName)) {
|
|
3433
|
+
return null;
|
|
3434
|
+
}
|
|
3435
|
+
const sourceFileName = toProjectedDeclarationSourceFileName(preparedProject.preparedProgram.toSourceFileName(declarationFileName));
|
|
3436
|
+
const sourceText = ts.sys.readFile(sourceFileName);
|
|
3437
|
+
if (!sourceText) {
|
|
3438
|
+
return null;
|
|
3439
|
+
}
|
|
3440
|
+
const sourceFile = ts.createSourceFile(sourceFileName, sourceText, ts.ScriptTarget.Latest, true);
|
|
3441
|
+
const targetNode = findTopLevelNamedDeclarationNode(sourceFile, symbolName);
|
|
3442
|
+
if (!targetNode) {
|
|
3443
|
+
return null;
|
|
3444
|
+
}
|
|
3445
|
+
return createDefinitionLocationFromOriginalSource(sourceFileName, sourceText, targetNode);
|
|
3446
|
+
}
|
|
3447
|
+
function createSourceBackedDefinitionLocation(preparedProject, declaration, symbolName) {
|
|
3448
|
+
const declarationFileName = declaration.getSourceFile().fileName;
|
|
3449
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(declarationFileName);
|
|
3450
|
+
if (!isSoundscriptSourceFile(sourceFileName)) {
|
|
3451
|
+
return null;
|
|
3452
|
+
}
|
|
3453
|
+
const isTopLevelDeclaration = ts.isVariableDeclaration(declaration)
|
|
3454
|
+
? ts.isVariableStatement(declaration.parent.parent) &&
|
|
3455
|
+
ts.isSourceFile(declaration.parent.parent.parent)
|
|
3456
|
+
: ts.isClassDeclaration(declaration) ||
|
|
3457
|
+
ts.isFunctionDeclaration(declaration) ||
|
|
3458
|
+
ts.isInterfaceDeclaration(declaration) ||
|
|
3459
|
+
ts.isTypeAliasDeclaration(declaration) ||
|
|
3460
|
+
ts.isEnumDeclaration(declaration)
|
|
3461
|
+
? ts.isSourceFile(declaration.parent)
|
|
3462
|
+
: false;
|
|
3463
|
+
if (!isTopLevelDeclaration) {
|
|
3464
|
+
return null;
|
|
3465
|
+
}
|
|
3466
|
+
const sourceText = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName)
|
|
3467
|
+
?.originalText ?? ts.sys.readFile(sourceFileName);
|
|
3468
|
+
if (!sourceText) {
|
|
3469
|
+
return null;
|
|
3470
|
+
}
|
|
3471
|
+
const sourceFile = ts.createSourceFile(sourceFileName, sourceText, ts.ScriptTarget.Latest, true);
|
|
3472
|
+
const sourceBackedNode = findTopLevelNamedDeclarationNode(sourceFile, symbolName);
|
|
3473
|
+
if (!sourceBackedNode) {
|
|
3474
|
+
return null;
|
|
3475
|
+
}
|
|
3476
|
+
return createDefinitionLocationFromOriginalSource(sourceFileName, sourceText, sourceBackedNode);
|
|
3477
|
+
}
|
|
3478
|
+
function createDefinitionLocationFromOriginalSource(sourceFileName, sourceText, declarationNode) {
|
|
3479
|
+
return {
|
|
3480
|
+
uri: toFileUrl(sourceFileName).href,
|
|
3481
|
+
range: createRangeFromOffsets(declarationNode.getStart(), declarationNode.getEnd(), sourceText),
|
|
3482
|
+
};
|
|
3483
|
+
}
|
|
3484
|
+
function preparedFileForProgramSourceFile(preparedProject, sourceFile) {
|
|
3485
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
3486
|
+
const directPreparedFile = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(sourceFileName);
|
|
3487
|
+
const diagnosticPreparedFile = preparedProject.diagnosticPreparedFiles.get(sourceFileName);
|
|
3488
|
+
const directProgramSourceFile = preparedProject.preparedProgram.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(sourceFileName));
|
|
3489
|
+
const diagnosticProgramSourceFile = preparedProject.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(sourceFileName));
|
|
3490
|
+
if (sourceFile === directProgramSourceFile) {
|
|
3491
|
+
return directPreparedFile ?? diagnosticPreparedFile;
|
|
3492
|
+
}
|
|
3493
|
+
if (sourceFile === diagnosticProgramSourceFile) {
|
|
3494
|
+
return diagnosticPreparedFile ?? directPreparedFile;
|
|
3495
|
+
}
|
|
3496
|
+
return diagnosticPreparedFile ?? directPreparedFile;
|
|
3497
|
+
}
|
|
3498
|
+
function symbolMatchesTargetKeys(preparedProject, symbol, targetKeys, sourceFile, macroSourceMap) {
|
|
3499
|
+
for (const key of getTargetSymbolKeys(preparedProject, symbol, [], sourceFile, macroSourceMap)) {
|
|
3500
|
+
if (targetKeys.has(key)) {
|
|
3501
|
+
return true;
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
return false;
|
|
3505
|
+
}
|
|
3506
|
+
function getDefinitionTargetNode(declaration) {
|
|
3507
|
+
const namedDeclaration = declaration;
|
|
3508
|
+
return namedDeclaration.name ?? declaration;
|
|
3509
|
+
}
|
|
3510
|
+
function mapPatchedNodeRangeToSource(node, sourceFile, preparedFile, macroSourceMap) {
|
|
3511
|
+
const nodeStart = node.getStart(sourceFile);
|
|
3512
|
+
const nodeEnd = node.getEnd();
|
|
3513
|
+
const materializedLength = macroSourceMap.materializedRegion.text.length;
|
|
3514
|
+
const replacementLength = macroSourceMap.originalReplacementEnd -
|
|
3515
|
+
macroSourceMap.originalReplacementStart;
|
|
3516
|
+
const materializedEnd = macroSourceMap.rewrittenStart + materializedLength;
|
|
3517
|
+
if (nodeEnd <= macroSourceMap.rewrittenStart) {
|
|
3518
|
+
const mappedRange = mapProgramRangeToSource(preparedFile, nodeStart, nodeEnd);
|
|
3519
|
+
return { start: mappedRange.start, end: mappedRange.end };
|
|
3520
|
+
}
|
|
3521
|
+
if (nodeStart >= materializedEnd) {
|
|
3522
|
+
const delta = replacementLength - materializedLength;
|
|
3523
|
+
const mappedRange = mapProgramRangeToSource(preparedFile, nodeStart + delta, nodeEnd + delta);
|
|
3524
|
+
return { start: mappedRange.start, end: mappedRange.end };
|
|
3525
|
+
}
|
|
3526
|
+
if (nodeStart < macroSourceMap.rewrittenStart || nodeEnd > materializedEnd) {
|
|
3527
|
+
return {
|
|
3528
|
+
start: macroSourceMap.originalReplacementStart,
|
|
3529
|
+
end: macroSourceMap.originalReplacementEnd,
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
const materializedRange = mapMaterializedRangeToSource(macroSourceMap.materializedRegion, nodeStart - macroSourceMap.rewrittenStart, nodeEnd - macroSourceMap.rewrittenStart);
|
|
3533
|
+
if (!materializedRange || materializedRange.intersectsUnmapped) {
|
|
3534
|
+
return {
|
|
3535
|
+
start: macroSourceMap.originalReplacementStart,
|
|
3536
|
+
end: macroSourceMap.originalReplacementEnd,
|
|
3537
|
+
};
|
|
3538
|
+
}
|
|
3539
|
+
return {
|
|
3540
|
+
start: materializedRange.start,
|
|
3541
|
+
end: materializedRange.end,
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
function createDefinitionLocation(preparedProject, declarationNode, sourceFile, macroSourceMap) {
|
|
3545
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
3546
|
+
const preparedFile = preparedFileForProgramSourceFile(preparedProject, sourceFile);
|
|
3547
|
+
if (preparedFile) {
|
|
3548
|
+
const mappedRange = macroSourceMap &&
|
|
3549
|
+
sourceFile.fileName === preparedProject.preparedProgram.toProgramFileName(sourceFileName)
|
|
3550
|
+
? mapPatchedNodeRangeToSource(declarationNode, sourceFile, preparedFile, macroSourceMap)
|
|
3551
|
+
: (() => {
|
|
3552
|
+
const range = mapProgramRangeToSource(preparedFile, declarationNode.getStart(sourceFile), declarationNode.getEnd());
|
|
3553
|
+
return { start: range.start, end: range.end };
|
|
3554
|
+
})();
|
|
3555
|
+
if (!mappedRange) {
|
|
3556
|
+
return null;
|
|
3557
|
+
}
|
|
3558
|
+
return {
|
|
3559
|
+
uri: toFileUrl(sourceFileName).href,
|
|
3560
|
+
range: createRangeFromOffsets(mappedRange.start, mappedRange.end, preparedFile.originalText),
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
const start = sourceFile.getLineAndCharacterOfPosition(declarationNode.getStart(sourceFile));
|
|
3564
|
+
const end = sourceFile.getLineAndCharacterOfPosition(declarationNode.getEnd());
|
|
3565
|
+
return {
|
|
3566
|
+
uri: toFileUrl(sourceFileName).href,
|
|
3567
|
+
range: {
|
|
3568
|
+
start: { line: start.line, character: start.character },
|
|
3569
|
+
end: { line: end.line, character: end.character },
|
|
3570
|
+
},
|
|
3571
|
+
};
|
|
3572
|
+
}
|
|
3573
|
+
function definitionForNode(preparedProject, checker, node, sourceFile, relatedViews = [], macroSourceMap) {
|
|
3574
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
3575
|
+
if (!symbol) {
|
|
3576
|
+
return null;
|
|
3577
|
+
}
|
|
3578
|
+
const declarations = symbol.declarations ??
|
|
3579
|
+
(symbol.valueDeclaration ? [symbol.valueDeclaration] : []);
|
|
3580
|
+
if (declarations.length === 0) {
|
|
3581
|
+
return null;
|
|
3582
|
+
}
|
|
3583
|
+
const symbolName = symbol.getName();
|
|
3584
|
+
const uniqueLocations = new Map();
|
|
3585
|
+
for (const declaration of declarations) {
|
|
3586
|
+
const sourceBackedLocation = createSourceBackedProjectedDefinitionLocation(preparedProject, declaration, symbolName);
|
|
3587
|
+
if (sourceBackedLocation) {
|
|
3588
|
+
const key = `${sourceBackedLocation.uri}:${sourceBackedLocation.range.start.line}:${sourceBackedLocation.range.start.character}:${sourceBackedLocation.range.end.line}:${sourceBackedLocation.range.end.character}`;
|
|
3589
|
+
uniqueLocations.set(key, sourceBackedLocation);
|
|
3590
|
+
continue;
|
|
3591
|
+
}
|
|
3592
|
+
const sourceBackedDefinitionLocation = createSourceBackedDefinitionLocation(preparedProject, declaration, symbolName);
|
|
3593
|
+
if (sourceBackedDefinitionLocation) {
|
|
3594
|
+
const key = `${sourceBackedDefinitionLocation.uri}:${sourceBackedDefinitionLocation.range.start.line}:${sourceBackedDefinitionLocation.range.start.character}:${sourceBackedDefinitionLocation.range.end.line}:${sourceBackedDefinitionLocation.range.end.character}`;
|
|
3595
|
+
uniqueLocations.set(key, sourceBackedDefinitionLocation);
|
|
3596
|
+
continue;
|
|
3597
|
+
}
|
|
3598
|
+
const targetNode = getDefinitionTargetNode(declaration);
|
|
3599
|
+
const location = createDefinitionLocation(preparedProject, targetNode, declaration.getSourceFile(), declaration.getSourceFile() === sourceFile ? macroSourceMap : undefined);
|
|
3600
|
+
if (!location) {
|
|
3601
|
+
continue;
|
|
3602
|
+
}
|
|
3603
|
+
const key = `${location.uri}:${location.range.start.line}:${location.range.start.character}:${location.range.end.line}:${location.range.end.character}`;
|
|
3604
|
+
uniqueLocations.set(key, location);
|
|
3605
|
+
}
|
|
3606
|
+
for (const match of collectCrossViewDeclarations(preparedProject, symbol, relatedViews)) {
|
|
3607
|
+
const sourceBackedLocation = createSourceBackedProjectedDefinitionLocation(match.view, match.declaration, symbolName) ??
|
|
3608
|
+
createSourceBackedDefinitionLocation(match.view, match.declaration, symbolName);
|
|
3609
|
+
if (sourceBackedLocation) {
|
|
3610
|
+
const key = `${sourceBackedLocation.uri}:${sourceBackedLocation.range.start.line}:${sourceBackedLocation.range.start.character}:${sourceBackedLocation.range.end.line}:${sourceBackedLocation.range.end.character}`;
|
|
3611
|
+
uniqueLocations.set(key, sourceBackedLocation);
|
|
3612
|
+
continue;
|
|
3613
|
+
}
|
|
3614
|
+
const targetNode = getDefinitionTargetNode(match.declaration);
|
|
3615
|
+
const location = createDefinitionLocation(match.view, targetNode, match.declaration.getSourceFile());
|
|
3616
|
+
if (!location) {
|
|
3617
|
+
continue;
|
|
3618
|
+
}
|
|
3619
|
+
const key = `${location.uri}:${location.range.start.line}:${location.range.start.character}:${location.range.end.line}:${location.range.end.character}`;
|
|
3620
|
+
uniqueLocations.set(key, location);
|
|
3621
|
+
}
|
|
3622
|
+
const allLocations = [...uniqueLocations.values()];
|
|
3623
|
+
const sourceLocations = allLocations.filter((location) => isSoundscriptSourceFile(fromFileUrl(location.uri)));
|
|
3624
|
+
if (sourceLocations.length > 0) {
|
|
3625
|
+
return sourceLocations;
|
|
3626
|
+
}
|
|
3627
|
+
const filteredLocations = allLocations.filter((location) => !isProjectedSoundscriptDeclarationFile(fromFileUrl(location.uri)));
|
|
3628
|
+
return uniqueLocations.size > 0
|
|
3629
|
+
? (filteredLocations.length > 0 ? filteredLocations : allLocations)
|
|
3630
|
+
: null;
|
|
3631
|
+
}
|
|
3632
|
+
function definitionMacroInvocation(preparedProject, filePath, sourcePosition) {
|
|
3633
|
+
const macroBinding = findMacroBindingOccurrence(preparedProject, filePath, sourcePosition);
|
|
3634
|
+
if (macroBinding) {
|
|
3635
|
+
const declaration = macroBinding.occurrences.find((entry) => entry.bindingId === macroBinding.occurrence.bindingId && entry.kind === 'declaration');
|
|
3636
|
+
if (declaration) {
|
|
3637
|
+
const originalText = macroBinding.match.resolved.placeholder.preparedFile.originalText;
|
|
3638
|
+
return [createSourceSpanLocation(filePath, originalText, declaration.span)];
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
3642
|
+
const match = findResolvedMacroContainingPosition(filePath, sourcePosition, collected);
|
|
3643
|
+
if (!match) {
|
|
3644
|
+
return null;
|
|
3645
|
+
}
|
|
3646
|
+
const hookNode = resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, sourcePosition);
|
|
3647
|
+
if (hookNode && !('kind' in hookNode)) {
|
|
3648
|
+
return definitionForNode(preparedProject, hookNode.checker, hookNode.node, hookNode.sourceFile, [], {
|
|
3649
|
+
materializedRegion: hookNode.materializedRegion,
|
|
3650
|
+
originalReplacementEnd: hookNode.originalReplacementEnd,
|
|
3651
|
+
originalReplacementStart: hookNode.originalReplacementStart,
|
|
3652
|
+
rewrittenStart: hookNode.rewrittenStart,
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3655
|
+
const expressionNode = resolveExpressionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
3656
|
+
if (expressionNode && !('kind' in expressionNode)) {
|
|
3657
|
+
return definitionForNode(preparedProject, expressionNode.checker, expressionNode.node, expressionNode.sourceFile, [], {
|
|
3658
|
+
materializedRegion: expressionNode.materializedRegion,
|
|
3659
|
+
originalReplacementEnd: expressionNode.originalReplacementEnd,
|
|
3660
|
+
originalReplacementStart: expressionNode.originalReplacementStart,
|
|
3661
|
+
rewrittenStart: expressionNode.rewrittenStart,
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3664
|
+
const blockNode = resolveBlockNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
3665
|
+
if (blockNode && !('kind' in blockNode)) {
|
|
3666
|
+
return definitionForNode(preparedProject, blockNode.checker, blockNode.node, blockNode.sourceFile, [], {
|
|
3667
|
+
materializedRegion: blockNode.materializedRegion,
|
|
3668
|
+
originalReplacementEnd: blockNode.originalReplacementEnd,
|
|
3669
|
+
originalReplacementStart: blockNode.originalReplacementStart,
|
|
3670
|
+
rewrittenStart: blockNode.rewrittenStart,
|
|
3671
|
+
});
|
|
3672
|
+
}
|
|
3673
|
+
return null;
|
|
3674
|
+
}
|
|
3675
|
+
function resolveMacroNodeAtPosition(preparedProject, filePath, sourcePosition) {
|
|
3676
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
3677
|
+
const match = findResolvedMacroContainingPosition(filePath, sourcePosition, collected);
|
|
3678
|
+
if (!match) {
|
|
3679
|
+
return null;
|
|
3680
|
+
}
|
|
3681
|
+
const hookNode = resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, sourcePosition);
|
|
3682
|
+
if (hookNode && !('kind' in hookNode)) {
|
|
3683
|
+
return {
|
|
3684
|
+
checker: hookNode.checker,
|
|
3685
|
+
materializedRegion: hookNode.materializedRegion,
|
|
3686
|
+
node: hookNode.node,
|
|
3687
|
+
originalReplacementEnd: hookNode.originalReplacementEnd,
|
|
3688
|
+
originalReplacementStart: hookNode.originalReplacementStart,
|
|
3689
|
+
rewrittenStart: hookNode.rewrittenStart,
|
|
3690
|
+
sourceFile: hookNode.sourceFile,
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
const declarationNode = resolveDeclarationNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
3694
|
+
if (declarationNode) {
|
|
3695
|
+
return {
|
|
3696
|
+
checker: declarationNode.checker,
|
|
3697
|
+
materializedRegion: declarationNode.materializedRegion,
|
|
3698
|
+
node: declarationNode.node,
|
|
3699
|
+
originalReplacementEnd: declarationNode.originalReplacementEnd,
|
|
3700
|
+
originalReplacementStart: declarationNode.originalReplacementStart,
|
|
3701
|
+
rewrittenStart: declarationNode.rewrittenStart,
|
|
3702
|
+
sourceFile: declarationNode.sourceFile,
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
const expressionNode = resolveExpressionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
3706
|
+
if (expressionNode && !('kind' in expressionNode)) {
|
|
3707
|
+
return {
|
|
3708
|
+
checker: expressionNode.checker,
|
|
3709
|
+
materializedRegion: expressionNode.materializedRegion,
|
|
3710
|
+
node: expressionNode.node,
|
|
3711
|
+
originalReplacementEnd: expressionNode.originalReplacementEnd,
|
|
3712
|
+
originalReplacementStart: expressionNode.originalReplacementStart,
|
|
3713
|
+
rewrittenStart: expressionNode.rewrittenStart,
|
|
3714
|
+
sourceFile: expressionNode.sourceFile,
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
const blockNode = resolveBlockNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, sourcePosition);
|
|
3718
|
+
if (blockNode && !('kind' in blockNode)) {
|
|
3719
|
+
return {
|
|
3720
|
+
checker: blockNode.checker,
|
|
3721
|
+
materializedRegion: blockNode.materializedRegion,
|
|
3722
|
+
node: blockNode.node,
|
|
3723
|
+
originalReplacementEnd: blockNode.originalReplacementEnd,
|
|
3724
|
+
originalReplacementStart: blockNode.originalReplacementStart,
|
|
3725
|
+
rewrittenStart: blockNode.rewrittenStart,
|
|
3726
|
+
sourceFile: blockNode.sourceFile,
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
return null;
|
|
3730
|
+
}
|
|
3731
|
+
function resolveDeclarationNodeAtSourcePosition(preparedProgram, resolved, sourcePosition) {
|
|
3732
|
+
const declarationSpan = resolved.placeholder.invocation.declarationSpan;
|
|
3733
|
+
if (!declarationSpan ||
|
|
3734
|
+
sourcePosition < declarationSpan.start ||
|
|
3735
|
+
sourcePosition >= declarationSpan.end) {
|
|
3736
|
+
return null;
|
|
3737
|
+
}
|
|
3738
|
+
const originalText = resolved.placeholder.preparedFile.originalText;
|
|
3739
|
+
const materializedRegion = materializeRegionForAnalysis(resolved.placeholder.invocation.fileName, originalText, declarationSpan);
|
|
3740
|
+
return resolveNodeAtMaterializedRegion(preparedProgram, resolved, {
|
|
3741
|
+
...materializedRegion,
|
|
3742
|
+
hoverPosition: Math.max(0, Math.min(sourcePosition - declarationSpan.start, materializedRegion.text.length)),
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
function resolveRenameTarget(preparedProject, filePath, line, character) {
|
|
3746
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
3747
|
+
if (!lookup) {
|
|
3748
|
+
return null;
|
|
3749
|
+
}
|
|
3750
|
+
const sourcePosition = getPositionOfLineAndCharacter(lookup.preparedFile.originalText, line, character);
|
|
3751
|
+
const macroNode = resolveMacroNodeAtPosition(preparedProject, filePath, sourcePosition);
|
|
3752
|
+
if (macroNode && ts.isIdentifier(macroNode.node)) {
|
|
3753
|
+
const target = {
|
|
3754
|
+
checker: macroNode.checker,
|
|
3755
|
+
node: macroNode.node,
|
|
3756
|
+
preparedFile: lookup.preparedFile,
|
|
3757
|
+
sourceFile: macroNode.sourceFile,
|
|
3758
|
+
sourcePosition,
|
|
3759
|
+
};
|
|
3760
|
+
if (resolveSymbolAtNode(target.checker, target.node)) {
|
|
3761
|
+
return target;
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
3765
|
+
if (!mappedPosition.insideReplacement) {
|
|
3766
|
+
const node = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
3767
|
+
if (node && !ts.isSourceFile(node) && ts.isIdentifier(node)) {
|
|
3768
|
+
const target = {
|
|
3769
|
+
checker: lookup.checker,
|
|
3770
|
+
node,
|
|
3771
|
+
preparedFile: lookup.preparedFile,
|
|
3772
|
+
sourceFile: lookup.sourceFile,
|
|
3773
|
+
sourcePosition,
|
|
3774
|
+
};
|
|
3775
|
+
if (resolveSymbolAtNode(target.checker, target.node)) {
|
|
3776
|
+
return target;
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
return resolveExpandedRenameTarget(preparedProject, filePath, sourcePosition);
|
|
3781
|
+
}
|
|
3782
|
+
function createDirectSourceLookup(preparedProject, filePath) {
|
|
3783
|
+
const sourceFile = preparedProject.preparedProgram.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(filePath));
|
|
3784
|
+
if (!sourceFile) {
|
|
3785
|
+
return null;
|
|
3786
|
+
}
|
|
3787
|
+
const preparedFile = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(filePath);
|
|
3788
|
+
if (!preparedFile) {
|
|
3789
|
+
return null;
|
|
3790
|
+
}
|
|
3791
|
+
return {
|
|
3792
|
+
checker: preparedProject.preparedProgram.program.getTypeChecker(),
|
|
3793
|
+
preparedFile,
|
|
3794
|
+
sourceFile,
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
function createExpandedSourceLookup(preparedProject, filePath) {
|
|
3798
|
+
const sourceFile = preparedProject.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(filePath));
|
|
3799
|
+
if (!sourceFile) {
|
|
3800
|
+
return null;
|
|
3801
|
+
}
|
|
3802
|
+
const preparedFile = preparedProject.diagnosticPreparedFiles.get(filePath) ??
|
|
3803
|
+
preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(filePath);
|
|
3804
|
+
if (!preparedFile) {
|
|
3805
|
+
return null;
|
|
3806
|
+
}
|
|
3807
|
+
return {
|
|
3808
|
+
checker: preparedProject.program.getTypeChecker(),
|
|
3809
|
+
preparedFile,
|
|
3810
|
+
sourceFile,
|
|
3811
|
+
};
|
|
3812
|
+
}
|
|
3813
|
+
function findLookupNodeAtSourcePosition(lookup, sourcePosition) {
|
|
3814
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
3815
|
+
if (mappedPosition.insideReplacement) {
|
|
3816
|
+
return null;
|
|
3817
|
+
}
|
|
3818
|
+
const node = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
3819
|
+
return !node || ts.isSourceFile(node) ? null : node;
|
|
3820
|
+
}
|
|
3821
|
+
function resolveExpandedRenameTarget(preparedProject, filePath, sourcePosition) {
|
|
3822
|
+
const lookup = createExpandedSourceLookup(preparedProject, filePath);
|
|
3823
|
+
if (!lookup) {
|
|
3824
|
+
return null;
|
|
3825
|
+
}
|
|
3826
|
+
const node = findLookupNodeAtSourcePosition(lookup, sourcePosition);
|
|
3827
|
+
if (!node || !ts.isIdentifier(node)) {
|
|
3828
|
+
return null;
|
|
3829
|
+
}
|
|
3830
|
+
return resolveSymbolAtNode(lookup.checker, node)
|
|
3831
|
+
? {
|
|
3832
|
+
checker: lookup.checker,
|
|
3833
|
+
node,
|
|
3834
|
+
preparedFile: lookup.preparedFile,
|
|
3835
|
+
sourceFile: lookup.sourceFile,
|
|
3836
|
+
sourcePosition,
|
|
3837
|
+
}
|
|
3838
|
+
: null;
|
|
3839
|
+
}
|
|
3840
|
+
function completionMacroInvocation(preparedProject, filePath, sourcePosition, originalText) {
|
|
3841
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
3842
|
+
const match = findResolvedMacroContainingPosition(filePath, sourcePosition, collected) ??
|
|
3843
|
+
findResolvedMacroContainingPosition(filePath, Math.max(0, sourcePosition - 1), collected);
|
|
3844
|
+
if (!match) {
|
|
3845
|
+
return null;
|
|
3846
|
+
}
|
|
3847
|
+
const candidatePositions = [
|
|
3848
|
+
...new Set([
|
|
3849
|
+
sourcePosition,
|
|
3850
|
+
Math.max(match.resolved.placeholder.invocation.span.start, sourcePosition - 1),
|
|
3851
|
+
]),
|
|
3852
|
+
];
|
|
3853
|
+
for (const candidatePosition of candidatePositions) {
|
|
3854
|
+
const hookCompletion = completionHookForMacroInvocation(preparedProject, filePath, match, candidatePosition);
|
|
3855
|
+
if (hookCompletion) {
|
|
3856
|
+
return hookCompletion;
|
|
3857
|
+
}
|
|
3858
|
+
const hookNode = resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, candidatePosition, true);
|
|
3859
|
+
if (hookNode && !('kind' in hookNode)) {
|
|
3860
|
+
return completionItemsForNode(hookNode.checker, hookNode.node, originalText, sourcePosition);
|
|
3861
|
+
}
|
|
3862
|
+
const expressionNode = resolveExpressionCompletionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, candidatePosition);
|
|
3863
|
+
if (expressionNode && !('kind' in expressionNode)) {
|
|
3864
|
+
return completionItemsForNode(expressionNode.checker, expressionNode.node, originalText, sourcePosition);
|
|
3865
|
+
}
|
|
3866
|
+
const blockNode = resolveBlockCompletionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, candidatePosition);
|
|
3867
|
+
if (blockNode && !('kind' in blockNode)) {
|
|
3868
|
+
return completionItemsForNode(blockNode.checker, blockNode.node, originalText, sourcePosition);
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
return null;
|
|
3872
|
+
}
|
|
3873
|
+
function signatureHelpMacroInvocation(preparedProject, filePath, sourcePosition) {
|
|
3874
|
+
const collected = getCollectedResolvedMacroPlaceholders(preparedProject);
|
|
3875
|
+
const candidatePositions = [
|
|
3876
|
+
...new Set([
|
|
3877
|
+
sourcePosition,
|
|
3878
|
+
Math.max(0, sourcePosition - 1),
|
|
3879
|
+
]),
|
|
3880
|
+
];
|
|
3881
|
+
for (const candidatePosition of candidatePositions) {
|
|
3882
|
+
const match = findResolvedMacroContainingPosition(filePath, candidatePosition, collected);
|
|
3883
|
+
if (!match) {
|
|
3884
|
+
continue;
|
|
3885
|
+
}
|
|
3886
|
+
const hookNode = resolveHookedMacroAnalysisNodeAtPosition(preparedProject, filePath, match, candidatePosition, true);
|
|
3887
|
+
if (hookNode && !('kind' in hookNode)) {
|
|
3888
|
+
return signatureHelpForNode(hookNode.checker, hookNode.node, hookNode.rewrittenStart + hookNode.materializedRegion.hoverPosition);
|
|
3889
|
+
}
|
|
3890
|
+
const expressionNode = resolveExpressionCompletionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, candidatePosition);
|
|
3891
|
+
if (expressionNode && !('kind' in expressionNode)) {
|
|
3892
|
+
return signatureHelpForNode(expressionNode.checker, expressionNode.node, expressionNode.rewrittenStart + expressionNode.materializedRegion.hoverPosition);
|
|
3893
|
+
}
|
|
3894
|
+
const blockNode = resolveBlockCompletionNodeAtSourcePosition(preparedProject.preparedProgram, match.resolved, candidatePosition);
|
|
3895
|
+
if (blockNode && !('kind' in blockNode)) {
|
|
3896
|
+
return signatureHelpForNode(blockNode.checker, blockNode.node, blockNode.rewrittenStart + blockNode.materializedRegion.hoverPosition);
|
|
3897
|
+
}
|
|
3898
|
+
const artifacts = parseResolvedMacroSyntaxArtifacts(preparedProject, filePath, match);
|
|
3899
|
+
if (artifacts?.definition.signature) {
|
|
3900
|
+
return signatureHelpForMacroDefinition(artifacts.definition, match.resolved.placeholder.invocation, candidatePosition, artifacts.context);
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
return null;
|
|
3904
|
+
}
|
|
3905
|
+
function findSourceMacroInvocationContainingPosition(preparedFile, filePath, sourcePosition) {
|
|
3906
|
+
let bestMatch = null;
|
|
3907
|
+
for (const invocation of preparedFile.rewriteResult.macrosById.values()) {
|
|
3908
|
+
if (invocation.fileName !== filePath) {
|
|
3909
|
+
continue;
|
|
3910
|
+
}
|
|
3911
|
+
if (!containsPosition(invocation.span.start, invocation.span.end, sourcePosition) &&
|
|
3912
|
+
sourcePosition !== invocation.span.end) {
|
|
3913
|
+
continue;
|
|
3914
|
+
}
|
|
3915
|
+
if (!bestMatch ||
|
|
3916
|
+
(invocation.span.end - invocation.span.start) < (bestMatch.span.end - bestMatch.span.start)) {
|
|
3917
|
+
bestMatch = invocation;
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
return bestMatch;
|
|
3921
|
+
}
|
|
3922
|
+
function signatureHelpSourceMacroInvocation(preparedProject, preparedFile, filePath, sourcePosition) {
|
|
3923
|
+
const invocation = findSourceMacroInvocationContainingPosition(preparedFile, filePath, sourcePosition);
|
|
3924
|
+
if (!invocation) {
|
|
3925
|
+
return null;
|
|
3926
|
+
}
|
|
3927
|
+
const definition = getImportedMacroDefinitionsForFile(preparedProject, filePath).get(invocation.nameText);
|
|
3928
|
+
if (!definition?.signature) {
|
|
3929
|
+
return null;
|
|
3930
|
+
}
|
|
3931
|
+
return signatureHelpForMacroDefinition(definition, invocation, sourcePosition);
|
|
3932
|
+
}
|
|
3933
|
+
function findParsedMacroInvocationContainingPosition(fileName, originalText, sourcePosition) {
|
|
3934
|
+
const scanResult = scanMacroCandidates(fileName, originalText);
|
|
3935
|
+
for (const hash of scanResult.hashes) {
|
|
3936
|
+
if (hash.kind !== 'macro-start' || hash.span.start > sourcePosition) {
|
|
3937
|
+
continue;
|
|
3938
|
+
}
|
|
3939
|
+
const parsed = parseMacroInvocationAt(fileName, originalText, hash.span.start);
|
|
3940
|
+
if ('reason' in parsed) {
|
|
3941
|
+
continue;
|
|
3942
|
+
}
|
|
3943
|
+
if (containsPosition(parsed.span.start, parsed.span.end, sourcePosition) ||
|
|
3944
|
+
sourcePosition === parsed.span.end) {
|
|
3945
|
+
return parsed;
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
return null;
|
|
3949
|
+
}
|
|
3950
|
+
function signatureHelpDirectMacroInvocation(preparedProject, filePath, originalText, sourcePosition) {
|
|
3951
|
+
let invocation = findParsedMacroInvocationContainingPosition(filePath, originalText, sourcePosition);
|
|
3952
|
+
if (!invocation) {
|
|
3953
|
+
let probePosition = sourcePosition;
|
|
3954
|
+
while (probePosition > 0) {
|
|
3955
|
+
const precedingChar = originalText[probePosition - 1];
|
|
3956
|
+
if (precedingChar !== ' ' &&
|
|
3957
|
+
precedingChar !== '\t' &&
|
|
3958
|
+
precedingChar !== '\r' &&
|
|
3959
|
+
precedingChar !== '\n' &&
|
|
3960
|
+
precedingChar !== ';') {
|
|
3961
|
+
break;
|
|
3962
|
+
}
|
|
3963
|
+
probePosition -= 1;
|
|
3964
|
+
}
|
|
3965
|
+
if (probePosition !== sourcePosition) {
|
|
3966
|
+
invocation = findParsedMacroInvocationContainingPosition(filePath, originalText, probePosition);
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
if (!invocation) {
|
|
3970
|
+
return null;
|
|
3971
|
+
}
|
|
3972
|
+
const definition = getImportedMacroDefinitionsForFile(preparedProject, filePath).get(invocation.nameText) ?? null;
|
|
3973
|
+
if (!definition?.signature) {
|
|
3974
|
+
return null;
|
|
3975
|
+
}
|
|
3976
|
+
return signatureHelpForMacroDefinition(definition, invocation, sourcePosition);
|
|
3977
|
+
}
|
|
3978
|
+
function collectDirectMacroInvocationsInRegion(fileName, originalText, regionSpan) {
|
|
3979
|
+
const invocations = [];
|
|
3980
|
+
const scanResult = scanMacroCandidates(fileName, originalText);
|
|
3981
|
+
let cursor = regionSpan.start;
|
|
3982
|
+
for (const hash of scanResult.hashes) {
|
|
3983
|
+
if (hash.kind !== 'macro-start') {
|
|
3984
|
+
continue;
|
|
3985
|
+
}
|
|
3986
|
+
if (hash.span.start < cursor || hash.span.start >= regionSpan.end) {
|
|
3987
|
+
continue;
|
|
3988
|
+
}
|
|
3989
|
+
const parsed = parseMacroInvocationAt(fileName, originalText, hash.span.start);
|
|
3990
|
+
if ('reason' in parsed || parsed.span.end > regionSpan.end) {
|
|
3991
|
+
continue;
|
|
3992
|
+
}
|
|
3993
|
+
invocations.push(parsed);
|
|
3994
|
+
cursor = parsed.span.end;
|
|
3995
|
+
}
|
|
3996
|
+
return invocations;
|
|
3997
|
+
}
|
|
3998
|
+
function collectReferenceLocationForNode(preparedProject, node, sourceFile, uniqueLocations, includeDeclaration, declarationKeys, macroSourceMap) {
|
|
3999
|
+
const sourceFileName = preparedProject.preparedProgram.toSourceFileName(sourceFile.fileName);
|
|
4000
|
+
const preparedFile = preparedFileForProgramSourceFile(preparedProject, sourceFile);
|
|
4001
|
+
if (preparedFile) {
|
|
4002
|
+
const mappedRange = macroSourceMap
|
|
4003
|
+
? mapPatchedNodeRangeToSource(node, sourceFile, preparedFile, macroSourceMap)
|
|
4004
|
+
: mapProgramRangeToSource(preparedFile, node.getStart(sourceFile), node.getEnd());
|
|
4005
|
+
if (mappedRange &&
|
|
4006
|
+
preparedFile.originalText.slice(mappedRange.start, mappedRange.end) !==
|
|
4007
|
+
node.getText(sourceFile) &&
|
|
4008
|
+
(macroSourceMap ||
|
|
4009
|
+
('intersectsReplacement' in mappedRange && mappedRange.intersectsReplacement))) {
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
const location = createDefinitionLocation(preparedProject, node, sourceFile, macroSourceMap);
|
|
4014
|
+
if (!location) {
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
const key = createLocationKey(location);
|
|
4018
|
+
if (!includeDeclaration && declarationKeys.has(key)) {
|
|
4019
|
+
return;
|
|
4020
|
+
}
|
|
4021
|
+
uniqueLocations.set(key, location);
|
|
4022
|
+
}
|
|
4023
|
+
function collectReferencesInSourceFile(preparedProject, sourceFile, checker, targetKeys, uniqueLocations, includeDeclaration, declarationKeys, macroSourceMap, scanRange) {
|
|
4024
|
+
function visit(node) {
|
|
4025
|
+
if (scanRange && (node.getEnd() <= scanRange.start || node.getStart(sourceFile) >= scanRange.end)) {
|
|
4026
|
+
return;
|
|
4027
|
+
}
|
|
4028
|
+
if (ts.isIdentifier(node)) {
|
|
4029
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
4030
|
+
const matches = symbol
|
|
4031
|
+
? symbolMatchesTargetKeys(preparedProject, symbol, targetKeys, sourceFile, macroSourceMap)
|
|
4032
|
+
: false;
|
|
4033
|
+
if (symbol &&
|
|
4034
|
+
matches) {
|
|
4035
|
+
collectReferenceLocationForNode(preparedProject, node, sourceFile, uniqueLocations, includeDeclaration, declarationKeys, macroSourceMap);
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
ts.forEachChild(node, visit);
|
|
4039
|
+
}
|
|
4040
|
+
visit(sourceFile);
|
|
4041
|
+
}
|
|
4042
|
+
function collectDirectSymbolReferencesInSourceFile(preparedProject, sourceFile, checker, targetSymbol, uniqueLocations, includeDeclaration, declarationKeys, macroSourceMap) {
|
|
4043
|
+
function visit(node) {
|
|
4044
|
+
if (ts.isIdentifier(node)) {
|
|
4045
|
+
const symbol = resolveSymbolAtNode(checker, node);
|
|
4046
|
+
if (symbol === targetSymbol) {
|
|
4047
|
+
collectReferenceLocationForNode(preparedProject, node, sourceFile, uniqueLocations, includeDeclaration, declarationKeys, macroSourceMap);
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
ts.forEachChild(node, visit);
|
|
4051
|
+
}
|
|
4052
|
+
visit(sourceFile);
|
|
4053
|
+
}
|
|
4054
|
+
function collectReferencesInMacroRegion(preparedProject, resolved, regionSpan, targetKeys, uniqueLocations, includeDeclaration, declarationKeys) {
|
|
4055
|
+
const preparedFile = resolved.placeholder.preparedFile;
|
|
4056
|
+
const materializedRegion = materializeRegionForAnalysis(resolved.placeholder.fileName, preparedFile.originalText, regionSpan);
|
|
4057
|
+
const patchedRegion = createPatchedMacroRegion(preparedProject.preparedProgram, resolved, materializedRegion);
|
|
4058
|
+
if (patchedRegion) {
|
|
4059
|
+
collectReferencesInSourceFile(preparedProject, patchedRegion.sourceFile, patchedRegion.checker, targetKeys, uniqueLocations, includeDeclaration, declarationKeys, {
|
|
4060
|
+
materializedRegion: patchedRegion.materializedRegion,
|
|
4061
|
+
originalReplacementEnd: patchedRegion.originalReplacementEnd,
|
|
4062
|
+
originalReplacementStart: patchedRegion.originalReplacementStart,
|
|
4063
|
+
rewrittenStart: patchedRegion.rewrittenStart,
|
|
4064
|
+
}, {
|
|
4065
|
+
start: patchedRegion.rewrittenStart,
|
|
4066
|
+
end: patchedRegion.rewrittenStart + patchedRegion.materializedRegion.text.length,
|
|
4067
|
+
});
|
|
4068
|
+
}
|
|
4069
|
+
for (const nestedInvocation of collectDirectMacroInvocationsInRegion(resolved.placeholder.fileName, preparedFile.originalText, regionSpan)) {
|
|
4070
|
+
for (const argument of nestedInvocation.argumentSpans) {
|
|
4071
|
+
if (argument.kind === 'ExprArg') {
|
|
4072
|
+
collectReferencesInMacroRegion(preparedProject, resolved, argument.span, targetKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
const nestedBlockSpan = getInvocationBlockSpan(nestedInvocation);
|
|
4076
|
+
if (nestedBlockSpan) {
|
|
4077
|
+
collectReferencesInMacroRegion(preparedProject, resolved, nestedBlockSpan, targetKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
export function analyzeOpenDocument(uri, session) {
|
|
4082
|
+
return measureDocumentOperation('request.diagnostics', uri, () => {
|
|
4083
|
+
const filePath = fromFileUrl(uri);
|
|
4084
|
+
const analyzedResult = getAnalyzedProjectContext(filePath, session);
|
|
4085
|
+
if (!analyzedResult) {
|
|
4086
|
+
return {
|
|
4087
|
+
diagnostics: [createNoProjectDiagnostic(filePath)],
|
|
4088
|
+
filePath,
|
|
4089
|
+
uri,
|
|
4090
|
+
};
|
|
4091
|
+
}
|
|
4092
|
+
try {
|
|
4093
|
+
const diagnostics = [
|
|
4094
|
+
...analyzedResult.diagnostics,
|
|
4095
|
+
...(getFileLocalAnalyzedProjectContext(filePath, session)?.diagnostics ?? []),
|
|
4096
|
+
].filter((diagnostic) => diagnostic.filePath === filePath);
|
|
4097
|
+
const uniqueDiagnostics = new Map();
|
|
4098
|
+
for (const diagnostic of diagnostics) {
|
|
4099
|
+
const key = [
|
|
4100
|
+
diagnostic.code,
|
|
4101
|
+
diagnostic.filePath,
|
|
4102
|
+
diagnostic.line,
|
|
4103
|
+
diagnostic.column,
|
|
4104
|
+
diagnostic.endLine,
|
|
4105
|
+
diagnostic.endColumn,
|
|
4106
|
+
diagnostic.message,
|
|
4107
|
+
].join(':');
|
|
4108
|
+
uniqueDiagnostics.set(key, diagnostic);
|
|
4109
|
+
}
|
|
4110
|
+
return {
|
|
4111
|
+
diagnostics: [...uniqueDiagnostics.values()],
|
|
4112
|
+
filePath,
|
|
4113
|
+
uri,
|
|
4114
|
+
};
|
|
4115
|
+
}
|
|
4116
|
+
catch (error) {
|
|
4117
|
+
return {
|
|
4118
|
+
diagnostics: [createAnalysisFailureDiagnostic(filePath, error)],
|
|
4119
|
+
filePath,
|
|
4120
|
+
uri,
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4123
|
+
});
|
|
4124
|
+
}
|
|
4125
|
+
export function hoverOpenDocument(uri, line, character, session, capabilityMode = 'full') {
|
|
4126
|
+
return measureDocumentOperation('request.hover', uri, () => {
|
|
4127
|
+
const filePath = fromFileUrl(uri);
|
|
4128
|
+
try {
|
|
4129
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4130
|
+
if (!preparedProject) {
|
|
4131
|
+
return null;
|
|
4132
|
+
}
|
|
4133
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4134
|
+
if (!lookup) {
|
|
4135
|
+
return null;
|
|
4136
|
+
}
|
|
4137
|
+
const originalText = session.get(uri)?.text ?? lookup.preparedFile.originalText;
|
|
4138
|
+
const sourcePosition = getPositionOfLineAndCharacter(originalText, line, character);
|
|
4139
|
+
const annotationCommentHover = annotationHover(preparedProject, filePath, sourcePosition, originalText);
|
|
4140
|
+
if (annotationCommentHover) {
|
|
4141
|
+
return annotationCommentHover;
|
|
4142
|
+
}
|
|
4143
|
+
const macroHover = hoverMacroInvocation(preparedProject, filePath, sourcePosition, originalText);
|
|
4144
|
+
if (macroHover) {
|
|
4145
|
+
return macroHover;
|
|
4146
|
+
}
|
|
4147
|
+
const projectedImportHover = projectedForeignImportHover(preparedProject, filePath, sourcePosition, originalText);
|
|
4148
|
+
if (projectedImportHover) {
|
|
4149
|
+
return projectedImportHover;
|
|
4150
|
+
}
|
|
4151
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
4152
|
+
if (mappedPosition.insideReplacement) {
|
|
4153
|
+
return null;
|
|
4154
|
+
}
|
|
4155
|
+
const node = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
4156
|
+
if (!node || ts.isSourceFile(node)) {
|
|
4157
|
+
return null;
|
|
4158
|
+
}
|
|
4159
|
+
if (isSuppressedFallbackHoverNode(node)) {
|
|
4160
|
+
return null;
|
|
4161
|
+
}
|
|
4162
|
+
const importedMacroHover = importedMacroSummaryHoverForNode(preparedProject, filePath, node, lookup.preparedFile.originalText, sourcePosition);
|
|
4163
|
+
if (importedMacroHover) {
|
|
4164
|
+
return importedMacroHover;
|
|
4165
|
+
}
|
|
4166
|
+
const analysisBackedHover = getAnalysisBackedHoverDetails(preparedProject, filePath, node);
|
|
4167
|
+
const type = lookup.checker.getTypeAtLocation(node);
|
|
4168
|
+
const ordinaryTypeText = lookup.checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);
|
|
4169
|
+
const fallbackSymbol = resolveSymbolAtNode(lookup.checker, node);
|
|
4170
|
+
const fallbackSymbolDeclaration = fallbackSymbol?.valueDeclaration ??
|
|
4171
|
+
fallbackSymbol?.declarations?.[0];
|
|
4172
|
+
const fallbackValue = (fallbackSymbolDeclaration
|
|
4173
|
+
? formatSymbolHoverCode(lookup.checker, getDefinitionTargetNode(fallbackSymbolDeclaration))
|
|
4174
|
+
: null) ??
|
|
4175
|
+
formatSymbolHoverCode(lookup.checker, node) ??
|
|
4176
|
+
ordinaryTypeText;
|
|
4177
|
+
const value = analysisBackedHover?.code ??
|
|
4178
|
+
fallbackValue;
|
|
4179
|
+
if (value.length === 0) {
|
|
4180
|
+
return null;
|
|
4181
|
+
}
|
|
4182
|
+
return {
|
|
4183
|
+
contents: createMarkdownHoverContents(value),
|
|
4184
|
+
range: (() => {
|
|
4185
|
+
const mappedRange = mapProgramRangeToSource(lookup.preparedFile, node.getStart(lookup.sourceFile), node.getEnd());
|
|
4186
|
+
return createHoverRange(mappedRange.start, mappedRange.end, lookup.preparedFile.originalText);
|
|
4187
|
+
})(),
|
|
4188
|
+
};
|
|
4189
|
+
}
|
|
4190
|
+
catch {
|
|
4191
|
+
return null;
|
|
4192
|
+
}
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
export function definitionOpenDocument(uri, line, character, session) {
|
|
4196
|
+
return measureDocumentOperation('request.definition', uri, () => {
|
|
4197
|
+
const filePath = fromFileUrl(uri);
|
|
4198
|
+
try {
|
|
4199
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4200
|
+
if (!preparedProject) {
|
|
4201
|
+
return null;
|
|
4202
|
+
}
|
|
4203
|
+
const projectViews = getPreparedProjectViews(filePath, session);
|
|
4204
|
+
const primaryResult = definitionInPreparedProject(preparedProject, filePath, line, character, projectViews);
|
|
4205
|
+
if (primaryResult || !isSoundscriptSourceFile(filePath)) {
|
|
4206
|
+
return primaryResult;
|
|
4207
|
+
}
|
|
4208
|
+
const fullPreparedProject = getPreparedProjectContext(filePath, session, 'full');
|
|
4209
|
+
if (!fullPreparedProject || fullPreparedProject === preparedProject) {
|
|
4210
|
+
return primaryResult;
|
|
4211
|
+
}
|
|
4212
|
+
return definitionInPreparedProject(fullPreparedProject, filePath, line, character, projectViews);
|
|
4213
|
+
}
|
|
4214
|
+
catch {
|
|
4215
|
+
return null;
|
|
4216
|
+
}
|
|
4217
|
+
});
|
|
4218
|
+
}
|
|
4219
|
+
function definitionInPreparedProject(preparedProject, filePath, line, character, relatedViews) {
|
|
4220
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4221
|
+
if (!lookup) {
|
|
4222
|
+
return null;
|
|
4223
|
+
}
|
|
4224
|
+
const sourcePosition = getPositionOfLineAndCharacter(lookup.preparedFile.originalText, line, character);
|
|
4225
|
+
const macroDefinition = definitionMacroInvocation(preparedProject, filePath, sourcePosition);
|
|
4226
|
+
if (macroDefinition) {
|
|
4227
|
+
return macroDefinition;
|
|
4228
|
+
}
|
|
4229
|
+
const macroNode = resolveMacroNodeAtPosition(preparedProject, filePath, sourcePosition);
|
|
4230
|
+
if (macroNode) {
|
|
4231
|
+
return definitionForNode(preparedProject, macroNode.checker, macroNode.node, macroNode.sourceFile, relatedViews, {
|
|
4232
|
+
materializedRegion: macroNode.materializedRegion,
|
|
4233
|
+
originalReplacementEnd: macroNode.originalReplacementEnd,
|
|
4234
|
+
originalReplacementStart: macroNode.originalReplacementStart,
|
|
4235
|
+
rewrittenStart: macroNode.rewrittenStart,
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
4239
|
+
if (mappedPosition.insideReplacement) {
|
|
4240
|
+
return definitionInExpandedPreparedProject(preparedProject, filePath, sourcePosition, relatedViews);
|
|
4241
|
+
}
|
|
4242
|
+
const node = findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
4243
|
+
if (!node || ts.isSourceFile(node)) {
|
|
4244
|
+
return definitionInExpandedPreparedProject(preparedProject, filePath, sourcePosition, relatedViews);
|
|
4245
|
+
}
|
|
4246
|
+
const directDefinition = definitionForNode(preparedProject, lookup.checker, node, lookup.sourceFile, relatedViews);
|
|
4247
|
+
const selfRange = createTokenHoverRange(sourcePosition, lookup.preparedFile.originalText);
|
|
4248
|
+
const isOnlySelfDefinition = directDefinition?.length === 1 &&
|
|
4249
|
+
directDefinition[0]?.uri === toFileUrl(filePath).href &&
|
|
4250
|
+
rangesEqual(directDefinition[0].range, selfRange);
|
|
4251
|
+
return (!isOnlySelfDefinition ? directDefinition : null) ??
|
|
4252
|
+
definitionInExpandedPreparedProject(preparedProject, filePath, sourcePosition, relatedViews);
|
|
4253
|
+
}
|
|
4254
|
+
function definitionInExpandedPreparedProject(preparedProject, filePath, sourcePosition, relatedViews) {
|
|
4255
|
+
const lookup = createExpandedSourceLookup(preparedProject, filePath);
|
|
4256
|
+
if (!lookup) {
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
const node = findLookupNodeAtSourcePosition(lookup, sourcePosition);
|
|
4260
|
+
if (!node) {
|
|
4261
|
+
return null;
|
|
4262
|
+
}
|
|
4263
|
+
return definitionForNode(preparedProject, lookup.checker, node, lookup.sourceFile, relatedViews);
|
|
4264
|
+
}
|
|
4265
|
+
export function documentSymbolsOpenDocument(uri, session) {
|
|
4266
|
+
return measureDocumentOperation('request.documentSymbols', uri, () => {
|
|
4267
|
+
const filePath = fromFileUrl(uri);
|
|
4268
|
+
try {
|
|
4269
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4270
|
+
if (!preparedProject) {
|
|
4271
|
+
return null;
|
|
4272
|
+
}
|
|
4273
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4274
|
+
if (!lookup) {
|
|
4275
|
+
return null;
|
|
4276
|
+
}
|
|
4277
|
+
return topLevelDocumentSymbols(preparedProject, lookup.sourceFile);
|
|
4278
|
+
}
|
|
4279
|
+
catch {
|
|
4280
|
+
return null;
|
|
4281
|
+
}
|
|
4282
|
+
});
|
|
4283
|
+
}
|
|
4284
|
+
export function semanticTokensLegend() {
|
|
4285
|
+
return {
|
|
4286
|
+
tokenTypes: [...SEMANTIC_TOKEN_TYPES],
|
|
4287
|
+
tokenModifiers: [...SEMANTIC_TOKEN_MODIFIERS],
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
export function semanticTokensOpenDocument(uri, session) {
|
|
4291
|
+
return measureDocumentOperation('request.semanticTokens', uri, () => {
|
|
4292
|
+
const filePath = fromFileUrl(uri);
|
|
4293
|
+
try {
|
|
4294
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4295
|
+
if (!preparedProject) {
|
|
4296
|
+
return { data: [] };
|
|
4297
|
+
}
|
|
4298
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4299
|
+
if (!lookup) {
|
|
4300
|
+
return { data: [] };
|
|
4301
|
+
}
|
|
4302
|
+
return collectSemanticTokensFromSourceFile(preparedProject, lookup.sourceFile, lookup.checker);
|
|
4303
|
+
}
|
|
4304
|
+
catch {
|
|
4305
|
+
return { data: [] };
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
export function formatOpenDocument(uri, options, session) {
|
|
4310
|
+
const document = session.get(uri);
|
|
4311
|
+
if (!document) {
|
|
4312
|
+
return null;
|
|
4313
|
+
}
|
|
4314
|
+
const filePath = fromFileUrl(uri);
|
|
4315
|
+
if (!isSoundscriptSourceFile(filePath) && document.languageId !== 'soundscript') {
|
|
4316
|
+
return null;
|
|
4317
|
+
}
|
|
4318
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4319
|
+
const sourceFile = preparedProject
|
|
4320
|
+
? preparedProject.preparedProgram.program.getSourceFile(preparedProject.preparedProgram.toProgramFileName(filePath))
|
|
4321
|
+
: undefined;
|
|
4322
|
+
const macroDefinitions = sourceFile
|
|
4323
|
+
? preparedProject?.macroEnvironment.definitionsForFile(sourceFile)
|
|
4324
|
+
: undefined;
|
|
4325
|
+
const importedMacroSiteKindsBySpecifier = sourceFile
|
|
4326
|
+
? preparedProject?.macroEnvironment.siteKindsBySpecifierForFile(sourceFile)
|
|
4327
|
+
: undefined;
|
|
4328
|
+
const formattedText = formatSoundscriptText(filePath, document.text, {
|
|
4329
|
+
indentText: options.insertSpaces ? ' '.repeat(Math.max(options.tabSize, 1)) : '\t',
|
|
4330
|
+
importedMacroSiteKindsBySpecifier,
|
|
4331
|
+
macroDefinitions,
|
|
4332
|
+
newLine: document.text.includes('\r\n') ? '\r\n' : '\n',
|
|
4333
|
+
});
|
|
4334
|
+
if (formattedText === document.text) {
|
|
4335
|
+
return [];
|
|
4336
|
+
}
|
|
4337
|
+
const end = getLineAndCharacterOfPosition(document.text, document.text.length);
|
|
4338
|
+
return [{
|
|
4339
|
+
newText: formattedText,
|
|
4340
|
+
range: {
|
|
4341
|
+
start: { line: 0, character: 0 },
|
|
4342
|
+
end: { line: end.line, character: end.character },
|
|
4343
|
+
},
|
|
4344
|
+
}];
|
|
4345
|
+
}
|
|
4346
|
+
export function showExpandedSourceOpenDocument(uri, stage, session) {
|
|
4347
|
+
const filePath = fromFileUrl(uri);
|
|
4348
|
+
const snapshot = getMacroDebugSnapshotForFile(filePath, session);
|
|
4349
|
+
if (!snapshot) {
|
|
4350
|
+
return null;
|
|
4351
|
+
}
|
|
4352
|
+
return {
|
|
4353
|
+
filePath,
|
|
4354
|
+
stage,
|
|
4355
|
+
text: readMacroDebugStageText(snapshot, stage),
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4358
|
+
export function showMacroTraceOpenDocument(uri, session) {
|
|
4359
|
+
const filePath = fromFileUrl(uri);
|
|
4360
|
+
const snapshot = getMacroDebugSnapshotForFile(filePath, session);
|
|
4361
|
+
if (!snapshot) {
|
|
4362
|
+
return null;
|
|
4363
|
+
}
|
|
4364
|
+
return {
|
|
4365
|
+
filePath,
|
|
4366
|
+
traces: snapshot.traces,
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
export function codeActionsOpenDocument(uri, diagnostics = [], session) {
|
|
4370
|
+
const filePath = fromFileUrl(uri);
|
|
4371
|
+
const text = readCodeActionDocumentText(uri, session);
|
|
4372
|
+
const diagnosticCodes = new Set(diagnostics
|
|
4373
|
+
.map((diagnostic) => diagnostic.code)
|
|
4374
|
+
.filter((code) => code !== undefined));
|
|
4375
|
+
const actions = [];
|
|
4376
|
+
if (diagnosticCodes.has('SOUNDSCRIPT_NO_PROJECT')) {
|
|
4377
|
+
const projectPath = suggestedProjectPathForFile(filePath);
|
|
4378
|
+
actions.push({
|
|
4379
|
+
title: 'Create tsconfig.json for soundscript',
|
|
4380
|
+
kind: 'quickfix',
|
|
4381
|
+
edit: {
|
|
4382
|
+
changes: {
|
|
4383
|
+
[toFileUrl(projectPath).href]: [{
|
|
4384
|
+
newText: createStarterTsconfigText(filePath),
|
|
4385
|
+
range: {
|
|
4386
|
+
start: { line: 0, character: 0 },
|
|
4387
|
+
end: { line: 0, character: 0 },
|
|
4388
|
+
},
|
|
4389
|
+
}],
|
|
4390
|
+
},
|
|
4391
|
+
},
|
|
4392
|
+
});
|
|
4393
|
+
}
|
|
4394
|
+
if (text) {
|
|
4395
|
+
for (const diagnostic of diagnostics) {
|
|
4396
|
+
const interopAction = createAddInteropCodeAction(uri, filePath, diagnostic, text, session);
|
|
4397
|
+
if (interopAction) {
|
|
4398
|
+
actions.push(interopAction);
|
|
4399
|
+
}
|
|
4400
|
+
const externAction = createAddExternCodeAction(uri, diagnostic, text);
|
|
4401
|
+
if (externAction) {
|
|
4402
|
+
actions.push(externAction);
|
|
4403
|
+
}
|
|
4404
|
+
const unsupportedFeatureAction = createUnsupportedFeatureRewriteCodeAction(uri, filePath, diagnostic, text);
|
|
4405
|
+
if (unsupportedFeatureAction) {
|
|
4406
|
+
actions.push(unsupportedFeatureAction);
|
|
4407
|
+
}
|
|
4408
|
+
const explicitNullishConditionAction = createExplicitNullishConditionCodeAction(uri, filePath, diagnostic, text, session);
|
|
4409
|
+
if (explicitNullishConditionAction) {
|
|
4410
|
+
actions.push(explicitNullishConditionAction);
|
|
4411
|
+
}
|
|
4412
|
+
const explicitBooleanLogicalOperatorAction = createExplicitBooleanLogicalOperatorCodeAction(uri, filePath, diagnostic, text, session);
|
|
4413
|
+
if (explicitBooleanLogicalOperatorAction) {
|
|
4414
|
+
actions.push(explicitBooleanLogicalOperatorAction);
|
|
4415
|
+
}
|
|
4416
|
+
const proofEscapeHatchAction = createProofEscapeHatchRewriteCodeAction(uri, filePath, diagnostic, text);
|
|
4417
|
+
if (proofEscapeHatchAction) {
|
|
4418
|
+
actions.push(proofEscapeHatchAction);
|
|
4419
|
+
}
|
|
4420
|
+
const anyTypeAction = createAnyTypeRewriteCodeAction(uri, filePath, diagnostic, text);
|
|
4421
|
+
if (anyTypeAction) {
|
|
4422
|
+
actions.push(anyTypeAction);
|
|
4423
|
+
}
|
|
4424
|
+
const ambientExportAction = createRemoveAmbientExportCodeAction(uri, filePath, diagnostic, text);
|
|
4425
|
+
if (ambientExportAction) {
|
|
4426
|
+
actions.push(ambientExportAction);
|
|
4427
|
+
}
|
|
4428
|
+
const throwNonErrorAction = createThrowNonErrorCodeAction(uri, filePath, diagnostic, text);
|
|
4429
|
+
if (throwNonErrorAction) {
|
|
4430
|
+
actions.push(throwNonErrorAction);
|
|
4431
|
+
}
|
|
4432
|
+
const receiverSensitiveAction = createReceiverSensitiveBindCodeAction(uri, filePath, diagnostic, text);
|
|
4433
|
+
if (receiverSensitiveAction) {
|
|
4434
|
+
actions.push(receiverSensitiveAction);
|
|
4435
|
+
}
|
|
4436
|
+
const invalidAnnotationTargetAction = createRemoveInvalidAnnotationTargetCodeAction(uri, diagnostic, text);
|
|
4437
|
+
if (invalidAnnotationTargetAction) {
|
|
4438
|
+
actions.push(invalidAnnotationTargetAction);
|
|
4439
|
+
}
|
|
4440
|
+
const pragmaAction = createRemoveTypeScriptPragmaCodeAction(uri, diagnostic, text);
|
|
4441
|
+
if (pragmaAction) {
|
|
4442
|
+
actions.push(pragmaAction);
|
|
4443
|
+
}
|
|
4444
|
+
const malformedAction = createRemoveMalformedAnnotationCodeAction(uri, diagnostic, text);
|
|
4445
|
+
if (malformedAction) {
|
|
4446
|
+
actions.push(malformedAction);
|
|
4447
|
+
}
|
|
4448
|
+
const duplicateAction = createRemoveDuplicateAnnotationsCodeAction(uri, diagnostic, text);
|
|
4449
|
+
if (duplicateAction) {
|
|
4450
|
+
actions.push(duplicateAction);
|
|
4451
|
+
}
|
|
4452
|
+
const unknownAnnotationAction = createRemoveUnknownAnnotationCodeAction(uri, diagnostic, text);
|
|
4453
|
+
if (unknownAnnotationAction) {
|
|
4454
|
+
actions.push(unknownAnnotationAction);
|
|
4455
|
+
}
|
|
4456
|
+
const annotationArgumentsAction = createRemoveUnsupportedAnnotationArgumentsCodeAction(uri, diagnostic, text);
|
|
4457
|
+
if (annotationArgumentsAction) {
|
|
4458
|
+
actions.push(annotationArgumentsAction);
|
|
4459
|
+
}
|
|
4460
|
+
const varianceContractAction = createVarianceContractRewriteCodeAction(uri, diagnostic, text);
|
|
4461
|
+
if (varianceContractAction) {
|
|
4462
|
+
actions.push(varianceContractAction);
|
|
4463
|
+
}
|
|
4464
|
+
const aliasAnnotationAction = createAliasReservedAnnotationMacroCodeAction(uri, filePath, diagnostic, text);
|
|
4465
|
+
if (aliasAnnotationAction) {
|
|
4466
|
+
actions.push(aliasAnnotationAction);
|
|
4467
|
+
}
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
return actions.length > 0 ? actions : null;
|
|
4471
|
+
}
|
|
4472
|
+
export function completeOpenDocument(uri, line, character, session) {
|
|
4473
|
+
return measureDocumentOperation('request.completion', uri, () => {
|
|
4474
|
+
const filePath = fromFileUrl(uri);
|
|
4475
|
+
try {
|
|
4476
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4477
|
+
if (!preparedProject) {
|
|
4478
|
+
return null;
|
|
4479
|
+
}
|
|
4480
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4481
|
+
if (!lookup) {
|
|
4482
|
+
return null;
|
|
4483
|
+
}
|
|
4484
|
+
const sourcePosition = getPositionOfLineAndCharacter(lookup.preparedFile.originalText, line, character);
|
|
4485
|
+
const macroCompletion = completionMacroInvocation(preparedProject, filePath, sourcePosition, lookup.preparedFile.originalText);
|
|
4486
|
+
if (macroCompletion) {
|
|
4487
|
+
return macroCompletion;
|
|
4488
|
+
}
|
|
4489
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
4490
|
+
if (mappedPosition.insideReplacement) {
|
|
4491
|
+
return null;
|
|
4492
|
+
}
|
|
4493
|
+
const lookupPosition = Math.max(0, Math.min(mappedPosition.position, Math.max(lookup.sourceFile.end - 1, 0)));
|
|
4494
|
+
const node = findCompletionNode(lookup.sourceFile, lookupPosition);
|
|
4495
|
+
if (!node) {
|
|
4496
|
+
return null;
|
|
4497
|
+
}
|
|
4498
|
+
return completionItemsForNode(lookup.checker, node, lookup.preparedFile.originalText, sourcePosition);
|
|
4499
|
+
}
|
|
4500
|
+
catch {
|
|
4501
|
+
return null;
|
|
4502
|
+
}
|
|
4503
|
+
});
|
|
4504
|
+
}
|
|
4505
|
+
export function signatureHelpOpenDocument(uri, line, character, session) {
|
|
4506
|
+
return measureDocumentOperation('request.signatureHelp', uri, () => {
|
|
4507
|
+
const filePath = fromFileUrl(uri);
|
|
4508
|
+
try {
|
|
4509
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4510
|
+
if (!preparedProject) {
|
|
4511
|
+
return null;
|
|
4512
|
+
}
|
|
4513
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4514
|
+
if (!lookup) {
|
|
4515
|
+
return null;
|
|
4516
|
+
}
|
|
4517
|
+
const sourcePosition = getPositionOfLineAndCharacter(lookup.preparedFile.originalText, line, character);
|
|
4518
|
+
const macroSignatureHelp = signatureHelpMacroInvocation(preparedProject, filePath, sourcePosition);
|
|
4519
|
+
if (macroSignatureHelp) {
|
|
4520
|
+
return macroSignatureHelp;
|
|
4521
|
+
}
|
|
4522
|
+
const directMacroSignatureHelp = signatureHelpDirectMacroInvocation(preparedProject, filePath, lookup.preparedFile.originalText, sourcePosition);
|
|
4523
|
+
if (directMacroSignatureHelp) {
|
|
4524
|
+
return directMacroSignatureHelp;
|
|
4525
|
+
}
|
|
4526
|
+
const sourceMacroSignatureHelp = signatureHelpSourceMacroInvocation(preparedProject, lookup.preparedFile, filePath, sourcePosition);
|
|
4527
|
+
if (sourceMacroSignatureHelp) {
|
|
4528
|
+
return sourceMacroSignatureHelp;
|
|
4529
|
+
}
|
|
4530
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
4531
|
+
if (mappedPosition.insideReplacement) {
|
|
4532
|
+
return null;
|
|
4533
|
+
}
|
|
4534
|
+
const lookupPosition = Math.max(0, Math.min(mappedPosition.position, Math.max(lookup.sourceFile.end - 1, 0)));
|
|
4535
|
+
const node = findCompletionNode(lookup.sourceFile, lookupPosition);
|
|
4536
|
+
if (!node) {
|
|
4537
|
+
return null;
|
|
4538
|
+
}
|
|
4539
|
+
return signatureHelpForNode(lookup.checker, node, mappedPosition.position);
|
|
4540
|
+
}
|
|
4541
|
+
catch {
|
|
4542
|
+
return null;
|
|
4543
|
+
}
|
|
4544
|
+
});
|
|
4545
|
+
}
|
|
4546
|
+
export function referencesOpenDocument(uri, line, character, session, includeDeclaration) {
|
|
4547
|
+
return measureDocumentOperation('request.references', uri, () => {
|
|
4548
|
+
const filePath = fromFileUrl(uri);
|
|
4549
|
+
try {
|
|
4550
|
+
const preparedProject = getPreparedProjectContext(filePath, session, 'full');
|
|
4551
|
+
if (!preparedProject) {
|
|
4552
|
+
return null;
|
|
4553
|
+
}
|
|
4554
|
+
const lookup = createDirectSourceLookup(preparedProject, filePath);
|
|
4555
|
+
if (!lookup) {
|
|
4556
|
+
return null;
|
|
4557
|
+
}
|
|
4558
|
+
const sourcePosition = getPositionOfLineAndCharacter(lookup.preparedFile.originalText, line, character);
|
|
4559
|
+
const macroBinding = findMacroBindingOccurrence(preparedProject, filePath, sourcePosition);
|
|
4560
|
+
if (macroBinding) {
|
|
4561
|
+
const locations = macroBinding.occurrences
|
|
4562
|
+
.filter((occurrence) => includeDeclaration || occurrence.kind !== 'declaration')
|
|
4563
|
+
.filter((occurrence) => occurrence.bindingId === macroBinding.occurrence.bindingId)
|
|
4564
|
+
.map((occurrence) => createSourceSpanLocation(filePath, lookup.preparedFile.originalText, occurrence.span));
|
|
4565
|
+
return locations.length > 0 ? locations : null;
|
|
4566
|
+
}
|
|
4567
|
+
const macroNode = resolveMacroNodeAtPosition(preparedProject, filePath, sourcePosition);
|
|
4568
|
+
const mappedPosition = mapSourcePositionToProgram(lookup.preparedFile, sourcePosition);
|
|
4569
|
+
const mainNode = mappedPosition.insideReplacement
|
|
4570
|
+
? undefined
|
|
4571
|
+
: findDeepestNodeContainingPosition(lookup.sourceFile, mappedPosition.position);
|
|
4572
|
+
const primaryResolvedTarget = macroNode
|
|
4573
|
+
? {
|
|
4574
|
+
checker: macroNode.checker,
|
|
4575
|
+
node: macroNode.node,
|
|
4576
|
+
sourceFile: macroNode.sourceFile,
|
|
4577
|
+
macroSourceMap: {
|
|
4578
|
+
materializedRegion: macroNode.materializedRegion,
|
|
4579
|
+
originalReplacementEnd: macroNode.originalReplacementEnd,
|
|
4580
|
+
originalReplacementStart: macroNode.originalReplacementStart,
|
|
4581
|
+
rewrittenStart: macroNode.rewrittenStart,
|
|
4582
|
+
},
|
|
4583
|
+
}
|
|
4584
|
+
: mainNode && !ts.isSourceFile(mainNode)
|
|
4585
|
+
? {
|
|
4586
|
+
checker: lookup.checker,
|
|
4587
|
+
node: mainNode,
|
|
4588
|
+
sourceFile: lookup.sourceFile,
|
|
4589
|
+
macroSourceMap: undefined,
|
|
4590
|
+
}
|
|
4591
|
+
: null;
|
|
4592
|
+
const resolvedTargets = [];
|
|
4593
|
+
if (primaryResolvedTarget) {
|
|
4594
|
+
const symbol = resolveSymbolAtNode(primaryResolvedTarget.checker, primaryResolvedTarget.node);
|
|
4595
|
+
if (symbol) {
|
|
4596
|
+
resolvedTargets.push({
|
|
4597
|
+
...primaryResolvedTarget,
|
|
4598
|
+
symbol,
|
|
4599
|
+
});
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4602
|
+
if (resolvedTargets.length === 0) {
|
|
4603
|
+
const expandedTarget = resolveExpandedRenameTarget(preparedProject, filePath, sourcePosition);
|
|
4604
|
+
if (expandedTarget) {
|
|
4605
|
+
const symbol = resolveSymbolAtNode(expandedTarget.checker, expandedTarget.node);
|
|
4606
|
+
if (symbol) {
|
|
4607
|
+
resolvedTargets.push({
|
|
4608
|
+
...expandedTarget,
|
|
4609
|
+
macroSourceMap: undefined,
|
|
4610
|
+
symbol,
|
|
4611
|
+
});
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
if (resolvedTargets.length === 0) {
|
|
4616
|
+
return null;
|
|
4617
|
+
}
|
|
4618
|
+
const projectViews = getPreparedProjectViews(filePath, session);
|
|
4619
|
+
const collectReferencesForResolvedTarget = (resolvedTarget) => {
|
|
4620
|
+
const definitionLocations = resolvedTarget.macroSourceMap
|
|
4621
|
+
? definitionForNode(preparedProject, resolvedTarget.checker, resolvedTarget.node, resolvedTarget.sourceFile, projectViews, resolvedTarget.macroSourceMap)
|
|
4622
|
+
: null;
|
|
4623
|
+
const declarationKeys = definitionLocations && definitionLocations.length > 0
|
|
4624
|
+
? new Set(definitionLocations.map(createLocationKey))
|
|
4625
|
+
: getTargetSymbolKeys(preparedProject, resolvedTarget.symbol, projectViews, resolvedTarget.sourceFile, resolvedTarget.macroSourceMap);
|
|
4626
|
+
if (declarationKeys.size === 0) {
|
|
4627
|
+
return null;
|
|
4628
|
+
}
|
|
4629
|
+
const uniqueLocations = new Map();
|
|
4630
|
+
for (const view of projectViews) {
|
|
4631
|
+
const programsToScan = [
|
|
4632
|
+
{
|
|
4633
|
+
checker: view.preparedProgram.program.getTypeChecker(),
|
|
4634
|
+
program: view.preparedProgram.program,
|
|
4635
|
+
},
|
|
4636
|
+
{
|
|
4637
|
+
checker: view.analysisContext.checker,
|
|
4638
|
+
program: view.program,
|
|
4639
|
+
},
|
|
4640
|
+
];
|
|
4641
|
+
for (const { program, checker } of programsToScan) {
|
|
4642
|
+
for (const currentSourceFile of program.getSourceFiles()) {
|
|
4643
|
+
if (currentSourceFile.isDeclarationFile) {
|
|
4644
|
+
continue;
|
|
4645
|
+
}
|
|
4646
|
+
collectReferencesInSourceFile(view, currentSourceFile, checker, declarationKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
for (const collected of getCollectedResolvedMacroPlaceholders(view)) {
|
|
4650
|
+
for (const argument of collected.resolved.placeholder.invocation.argumentSpans) {
|
|
4651
|
+
if (argument.kind === 'ExprArg') {
|
|
4652
|
+
collectReferencesInMacroRegion(view, collected.resolved, argument.span, declarationKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
const blockSpan = getInvocationBlockSpan(collected.resolved.placeholder.invocation);
|
|
4656
|
+
if (blockSpan) {
|
|
4657
|
+
collectReferencesInMacroRegion(view, collected.resolved, blockSpan, declarationKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4658
|
+
}
|
|
4659
|
+
const declarationSpan = collected.resolved.placeholder.invocation.declarationSpan;
|
|
4660
|
+
if (declarationSpan && !isAugmentDeclarationMacroInvocation(view, collected.resolved)) {
|
|
4661
|
+
collectReferencesInMacroRegion(view, collected.resolved, declarationSpan, declarationKeys, uniqueLocations, includeDeclaration, declarationKeys);
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
if (uniqueLocations.size === 0 && resolvedTarget.macroSourceMap) {
|
|
4666
|
+
collectDirectSymbolReferencesInSourceFile(preparedProject, resolvedTarget.sourceFile, resolvedTarget.checker, resolvedTarget.symbol, uniqueLocations, includeDeclaration, declarationKeys, resolvedTarget.macroSourceMap);
|
|
4667
|
+
}
|
|
4668
|
+
return uniqueLocations.size === 0
|
|
4669
|
+
? null
|
|
4670
|
+
: [...uniqueLocations.values()].sort((left, right) => left.uri.localeCompare(right.uri) ||
|
|
4671
|
+
left.range.start.line - right.range.start.line ||
|
|
4672
|
+
left.range.start.character - right.range.start.character ||
|
|
4673
|
+
left.range.end.line - right.range.end.line ||
|
|
4674
|
+
left.range.end.character - right.range.end.character);
|
|
4675
|
+
};
|
|
4676
|
+
let bestReferences = null;
|
|
4677
|
+
for (const resolvedTarget of resolvedTargets) {
|
|
4678
|
+
const candidateReferences = collectReferencesForResolvedTarget(resolvedTarget);
|
|
4679
|
+
if (!candidateReferences) {
|
|
4680
|
+
continue;
|
|
4681
|
+
}
|
|
4682
|
+
if (!bestReferences || candidateReferences.length > bestReferences.length) {
|
|
4683
|
+
bestReferences = candidateReferences;
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
return bestReferences;
|
|
4687
|
+
}
|
|
4688
|
+
catch {
|
|
4689
|
+
return null;
|
|
4690
|
+
}
|
|
4691
|
+
});
|
|
4692
|
+
}
|
|
4693
|
+
export function highlightOpenDocument(uri, line, character, session) {
|
|
4694
|
+
return measureDocumentOperation('request.documentHighlight', uri, () => {
|
|
4695
|
+
const references = referencesOpenDocument(uri, line, character, session, true);
|
|
4696
|
+
if (!references) {
|
|
4697
|
+
return null;
|
|
4698
|
+
}
|
|
4699
|
+
return references
|
|
4700
|
+
.filter((reference) => reference.uri === uri)
|
|
4701
|
+
.map((reference, index) => ({
|
|
4702
|
+
kind: index === 0 ? 3 : 2,
|
|
4703
|
+
range: reference.range,
|
|
4704
|
+
}));
|
|
4705
|
+
});
|
|
4706
|
+
}
|
|
4707
|
+
export function prepareRenameOpenDocument(uri, line, character, session) {
|
|
4708
|
+
return measureDocumentOperation('request.prepareRename', uri, () => {
|
|
4709
|
+
const filePath = fromFileUrl(uri);
|
|
4710
|
+
try {
|
|
4711
|
+
const preparedProject = getPreparedProjectContext(filePath, session);
|
|
4712
|
+
if (!preparedProject) {
|
|
4713
|
+
return null;
|
|
4714
|
+
}
|
|
4715
|
+
const preparedFile = preparedProject.preparedProgram.preparedHost.getPreparedSourceFile(filePath);
|
|
4716
|
+
if (!preparedFile) {
|
|
4717
|
+
return null;
|
|
4718
|
+
}
|
|
4719
|
+
const sourcePosition = getPositionOfLineAndCharacter(preparedFile.originalText, line, character);
|
|
4720
|
+
const macroBinding = findMacroBindingOccurrence(preparedProject, filePath, sourcePosition);
|
|
4721
|
+
if (macroBinding) {
|
|
4722
|
+
return {
|
|
4723
|
+
placeholder: macroBinding.occurrence.name,
|
|
4724
|
+
range: createRangeFromOffsets(macroBinding.occurrence.span.start, macroBinding.occurrence.span.end, preparedFile.originalText),
|
|
4725
|
+
};
|
|
4726
|
+
}
|
|
4727
|
+
const target = resolveRenameTarget(preparedProject, filePath, line, character);
|
|
4728
|
+
if (!target) {
|
|
4729
|
+
return null;
|
|
4730
|
+
}
|
|
4731
|
+
const symbol = resolveSymbolAtNode(target.checker, target.node);
|
|
4732
|
+
if (!symbol) {
|
|
4733
|
+
return null;
|
|
4734
|
+
}
|
|
4735
|
+
const range = createTokenHoverRange(target.sourcePosition, target.preparedFile.originalText);
|
|
4736
|
+
const placeholder = target.preparedFile.originalText.slice(getPositionOfLineAndCharacter(target.preparedFile.originalText, range.start.line, range.start.character), getPositionOfLineAndCharacter(target.preparedFile.originalText, range.end.line, range.end.character));
|
|
4737
|
+
if (placeholder.length === 0) {
|
|
4738
|
+
return null;
|
|
4739
|
+
}
|
|
4740
|
+
return {
|
|
4741
|
+
placeholder,
|
|
4742
|
+
range,
|
|
4743
|
+
};
|
|
4744
|
+
}
|
|
4745
|
+
catch {
|
|
4746
|
+
return null;
|
|
4747
|
+
}
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
export function renameOpenDocument(uri, line, character, newName, session) {
|
|
4751
|
+
return measureDocumentOperation('request.rename', uri, () => {
|
|
4752
|
+
const preparedRename = prepareRenameOpenDocument(uri, line, character, session);
|
|
4753
|
+
if (!preparedRename) {
|
|
4754
|
+
return null;
|
|
4755
|
+
}
|
|
4756
|
+
const references = referencesOpenDocument(uri, line, character, session, true);
|
|
4757
|
+
if (!references) {
|
|
4758
|
+
return null;
|
|
4759
|
+
}
|
|
4760
|
+
const changes = {};
|
|
4761
|
+
for (const reference of references) {
|
|
4762
|
+
const edits = changes[reference.uri] ?? [];
|
|
4763
|
+
edits.push({
|
|
4764
|
+
newText: newName,
|
|
4765
|
+
range: reference.range,
|
|
4766
|
+
});
|
|
4767
|
+
changes[reference.uri] = edits;
|
|
4768
|
+
}
|
|
4769
|
+
return { changes };
|
|
4770
|
+
});
|
|
4771
|
+
}
|