mt-lang 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module LSP
|
|
5
|
+
class Server
|
|
6
|
+
module ServerHover
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
KEYWORD_HOVER_INFO = {
|
|
10
|
+
'size_of' => {
|
|
11
|
+
signature: 'size_of(expr) -> int',
|
|
12
|
+
docs: '`size_of(expr)` evaluates the compile-time size (in bytes) of the expression\'s type.',
|
|
13
|
+
},
|
|
14
|
+
'align_of' => {
|
|
15
|
+
signature: 'align_of(expr) -> int',
|
|
16
|
+
docs: '`align_of(expr)` evaluates the compile-time alignment (in bytes) of the expression\'s type.',
|
|
17
|
+
},
|
|
18
|
+
'offset_of' => {
|
|
19
|
+
signature: 'offset_of(Type, field) -> int',
|
|
20
|
+
docs: '`offset_of(Type, field)` evaluates the compile-time byte offset of `field` within `Type`.',
|
|
21
|
+
},
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
def handle_hover(params)
|
|
25
|
+
stages = new_perf_stages
|
|
26
|
+
total_start = stages ? monotonic_time : nil
|
|
27
|
+
uri = params['textDocument']['uri']
|
|
28
|
+
lsp_line = params['position']['line']
|
|
29
|
+
lsp_char = params['position']['character']
|
|
30
|
+
token_kind = 'none'
|
|
31
|
+
result_state = 'miss'
|
|
32
|
+
|
|
33
|
+
context = measure_perf_stage(stages, 'context') { token_context_at(uri, lsp_line, lsp_char) }
|
|
34
|
+
token = context&.fetch(:token, nil)
|
|
35
|
+
token_kind = token&.type || :none
|
|
36
|
+
unless token&.type == :identifier
|
|
37
|
+
info = builtin_keyword_hover_info(token)
|
|
38
|
+
return info if info
|
|
39
|
+
|
|
40
|
+
result_state = 'not-identifier'
|
|
41
|
+
return nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
info = resolve_hover_info(uri, lsp_line, lsp_char, token: token, tokens: context[:tokens], token_index: context[:token_index], stages: stages)
|
|
45
|
+
return nil unless info
|
|
46
|
+
|
|
47
|
+
result = measure_perf_stage(stages, 'render') do
|
|
48
|
+
{
|
|
49
|
+
contents: {
|
|
50
|
+
kind: 'markdown',
|
|
51
|
+
value: render_hover_markdown(info)
|
|
52
|
+
},
|
|
53
|
+
range: token_to_range(token)
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
result_state = 'hit'
|
|
57
|
+
result
|
|
58
|
+
rescue StandardError => e
|
|
59
|
+
result_state = 'error'
|
|
60
|
+
warn "Error in hover handler: #{e.message}"
|
|
61
|
+
nil
|
|
62
|
+
ensure
|
|
63
|
+
log_request_stage_breakdown('textDocument/hover', total_start, uri: uri, stages: stages, summary: "token=#{token_kind} result=#{result_state}")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def resolve_hover_info(uri, lsp_line, lsp_char, token: nil, tokens: nil, token_index: nil, stages: nil)
|
|
67
|
+
if token.nil?
|
|
68
|
+
context = measure_perf_stage(stages, 'context') { token_context_at(uri, lsp_line, lsp_char) }
|
|
69
|
+
return nil unless context
|
|
70
|
+
|
|
71
|
+
token = context[:token]
|
|
72
|
+
tokens = context[:tokens]
|
|
73
|
+
token_index = context[:token_index]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return nil unless token&.type == :identifier
|
|
77
|
+
|
|
78
|
+
tokens ||= @workspace.get_tokens(uri) || []
|
|
79
|
+
token_index = tokens.index(token) if token_index.nil?
|
|
80
|
+
if token_index
|
|
81
|
+
module_info = module_declaration_info_at(tokens, token_index)
|
|
82
|
+
if module_info
|
|
83
|
+
location = module_definition_location(uri, module_info[:module_name])
|
|
84
|
+
return {
|
|
85
|
+
signature: "module #{module_info[:module_name]}",
|
|
86
|
+
docs: nil,
|
|
87
|
+
source: hover_source_label_from_location(location),
|
|
88
|
+
source_uri: hover_source_uri_from_location(location),
|
|
89
|
+
source_line: hover_source_line_from_location(location),
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if token_index
|
|
95
|
+
import_info = import_path_info_at(tokens, token_index)
|
|
96
|
+
if import_info
|
|
97
|
+
location = module_definition_location(uri, import_info[:module_name])
|
|
98
|
+
return {
|
|
99
|
+
signature: "module #{import_info[:module_name]}",
|
|
100
|
+
docs: nil,
|
|
101
|
+
source: hover_source_label_from_location(location),
|
|
102
|
+
source_uri: hover_source_uri_from_location(location),
|
|
103
|
+
source_line: hover_source_line_from_location(location),
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
facts = measure_perf_stage(stages, 'facts') do
|
|
109
|
+
@workspace.get_facts(uri, allow_last_good_fallback: allow_hover_last_good_fallback?(uri))
|
|
110
|
+
end
|
|
111
|
+
return nil unless facts
|
|
112
|
+
|
|
113
|
+
if token_index && field_declaration_token?(tokens, token_index)
|
|
114
|
+
return resolve_field_declaration_hover_info(uri, facts, tokens, token_index)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if token_index && named_argument_label_token?(tokens, token_index)
|
|
118
|
+
return resolve_named_argument_label_hover_info(uri, facts, tokens, token_index)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if token_index && (member_hover = resolve_member_access_hover_info(uri, facts, tokens, token_index))
|
|
122
|
+
return member_hover
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if token_index && (enum_member_hover = resolve_enum_member_hover_info(uri, facts, tokens, token_index))
|
|
126
|
+
return enum_member_hover
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
name = token.lexeme
|
|
130
|
+
signature = nil
|
|
131
|
+
docs = nil
|
|
132
|
+
source_location = nil
|
|
133
|
+
|
|
134
|
+
if (binding = method_binding_at_token(facts, token))
|
|
135
|
+
signature = method_signature(binding)
|
|
136
|
+
source_location = module_member_binding_location(uri, facts.module_name, name, binding)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
unless signature
|
|
140
|
+
if token_index && (for_binding_info = resolve_for_binding_hover_info(tokens, token_index))
|
|
141
|
+
signature = for_binding_info[:signature]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
unless signature
|
|
146
|
+
if token_index && match_arm_binding_token?(tokens, token_index)
|
|
147
|
+
if (local_binding = resolve_as_binding_declaration_hover_binding(facts, name, lsp_line + 1, lsp_char + 1))
|
|
148
|
+
signature = value_hover_signature(local_binding)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
unless signature
|
|
154
|
+
if (local_binding = resolve_local_hover_binding(facts, name, lsp_line + 1, lsp_char + 1))
|
|
155
|
+
signature = value_hover_signature(local_binding)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
unless signature
|
|
160
|
+
if (binding = facts.functions[name])
|
|
161
|
+
params_str = format_params(binding.type.params)
|
|
162
|
+
signature = "function #{name}(#{params_str}) -> #{binding.type.return_type}"
|
|
163
|
+
elsif (binding = facts.interfaces[name])
|
|
164
|
+
signature = interface_signature(binding)
|
|
165
|
+
source_location = module_member_definition_location(uri, binding.module_name, name)
|
|
166
|
+
elsif facts.types.key?(name)
|
|
167
|
+
type = facts.types[name]
|
|
168
|
+
signature = type_hover_signature(name, type)
|
|
169
|
+
elsif (binding = facts.values[name])
|
|
170
|
+
signature = value_hover_signature(binding)
|
|
171
|
+
elsif (import_binding = facts.imports[name])
|
|
172
|
+
signature = "module #{import_binding.name}"
|
|
173
|
+
source_location = module_definition_location(uri, import_binding.name)
|
|
174
|
+
else
|
|
175
|
+
dot_receiver = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
|
|
176
|
+
dot_receiver_path = @workspace.find_dot_receiver_path(uri, lsp_line, lsp_char)
|
|
177
|
+
if dot_receiver && (module_binding = facts.imports[dot_receiver])
|
|
178
|
+
if (fn = module_binding.functions[name])
|
|
179
|
+
params_str = format_params(fn.type.params)
|
|
180
|
+
signature = "function #{name}(#{params_str}) -> #{fn.type.return_type}"
|
|
181
|
+
source_location = module_member_binding_location(uri, module_binding.name, name, fn)
|
|
182
|
+
elsif (val = module_binding.values[name])
|
|
183
|
+
signature = value_hover_signature(val)
|
|
184
|
+
elsif module_binding.types.key?(name)
|
|
185
|
+
signature = "type #{name}"
|
|
186
|
+
elsif (binding = module_binding.interfaces[name])
|
|
187
|
+
signature = interface_signature(binding)
|
|
188
|
+
source_location = module_member_definition_location(uri, module_binding.name, name)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
if signature
|
|
192
|
+
source_location ||= module_member_definition_location(uri, module_binding.name, name)
|
|
193
|
+
source_location ||= module_definition_location(uri, module_binding.name)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
unless signature
|
|
198
|
+
if (type_method = resolve_static_type_receiver_method(facts, dot_receiver, dot_receiver_path, name))
|
|
199
|
+
signature = method_signature(type_method[:binding])
|
|
200
|
+
source_location = module_member_binding_location(uri, type_method[:module_name], name, type_method[:binding])
|
|
201
|
+
source_location ||= module_member_definition_location(uri, type_method[:module_name], name)
|
|
202
|
+
source_location ||= module_definition_location(uri, type_method[:module_name])
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
unless signature
|
|
207
|
+
if token_index && (builtin_info = builtin_hover_info(name, tokens, token_index))
|
|
208
|
+
signature = builtin_info[:signature]
|
|
209
|
+
docs = builtin_info[:docs]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
unless signature
|
|
214
|
+
local_def = @workspace.find_definition_token_global(
|
|
215
|
+
name,
|
|
216
|
+
preferred_uri: uri,
|
|
217
|
+
before_line: lsp_line + 1,
|
|
218
|
+
before_char: lsp_char + 1,
|
|
219
|
+
)
|
|
220
|
+
if local_def
|
|
221
|
+
signature = resolve_lexical_local_hover_signature(local_def[:uri], name, local_def[:token])
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
return nil unless signature
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
definition_entry = if source_location
|
|
230
|
+
measure_perf_stage(stages, 'definition_entry') { hover_definition_entry_from_location(source_location) }
|
|
231
|
+
else
|
|
232
|
+
measure_perf_stage(stages, 'global_definition') do
|
|
233
|
+
@workspace.find_definition_token_global(
|
|
234
|
+
name,
|
|
235
|
+
preferred_uri: uri,
|
|
236
|
+
before_line: lsp_line + 1,
|
|
237
|
+
before_char: lsp_char + 1,
|
|
238
|
+
)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
source_uri = hover_source_uri_for_definition(definition_entry) || hover_source_uri_from_location(source_location)
|
|
243
|
+
source_line = hover_source_line_for_definition(definition_entry) || hover_source_line_from_location(source_location)
|
|
244
|
+
|
|
245
|
+
{
|
|
246
|
+
signature: signature,
|
|
247
|
+
docs: docs || hover_doc_comment_for_definition(definition_entry),
|
|
248
|
+
doc_comment: hover_doc_comment_data_for_definition(definition_entry),
|
|
249
|
+
source: hover_source_label_for_definition(definition_entry) || hover_source_label_from_location(source_location),
|
|
250
|
+
source_uri: source_uri,
|
|
251
|
+
source_line: source_line,
|
|
252
|
+
}
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def token_context_at(uri, lsp_line, lsp_char)
|
|
256
|
+
tokens = @workspace.get_tokens(uri) || []
|
|
257
|
+
interpolation_context = fstring_interpolation_token_context(tokens, lsp_line, lsp_char)
|
|
258
|
+
return interpolation_context if interpolation_context
|
|
259
|
+
|
|
260
|
+
token = @workspace.find_token_at(uri, lsp_line, lsp_char)
|
|
261
|
+
return nil unless token
|
|
262
|
+
|
|
263
|
+
return nil if embedded_heredoc_or_format_heredoc_token?(token)
|
|
264
|
+
|
|
265
|
+
{
|
|
266
|
+
token: token,
|
|
267
|
+
tokens: tokens,
|
|
268
|
+
token_index: tokens.index(token),
|
|
269
|
+
}
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def embedded_heredoc_or_format_heredoc_token?(token)
|
|
273
|
+
return false unless [:string, :cstring, :fstring].include?(token.type)
|
|
274
|
+
|
|
275
|
+
tag = token.lexeme[/\A(?:f|c)?<<-([A-Za-z_][A-Za-z0-9_]*)[ \t]*\n/, 1]
|
|
276
|
+
return false if tag.nil?
|
|
277
|
+
|
|
278
|
+
%w[GLSL VERT FRAG COMP JSON JSONC SQL HTML].include?(tag)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def fstring_interpolation_token_context(tokens, lsp_line, lsp_char)
|
|
282
|
+
target_line = lsp_line + 1
|
|
283
|
+
target_char = lsp_char + 1
|
|
284
|
+
|
|
285
|
+
fstring_token = tokens.find do |token|
|
|
286
|
+
next false unless token.type == :fstring
|
|
287
|
+
|
|
288
|
+
token_contains_position?(token, target_line, target_char)
|
|
289
|
+
end
|
|
290
|
+
return nil unless fstring_token
|
|
291
|
+
|
|
292
|
+
Array(fstring_token.literal).each do |part|
|
|
293
|
+
next unless part[:kind] == :expr
|
|
294
|
+
next unless part[:line] == target_line
|
|
295
|
+
|
|
296
|
+
expression_tokens = interpolation_expression_tokens(part)
|
|
297
|
+
token = expression_tokens.find { |candidate| token_contains_position?(candidate, target_line, target_char) }
|
|
298
|
+
next unless token
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
token: token,
|
|
302
|
+
tokens: expression_tokens,
|
|
303
|
+
token_index: expression_tokens.index(token),
|
|
304
|
+
}
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
nil
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def interpolation_expression_tokens(part)
|
|
311
|
+
source = part[:source]
|
|
312
|
+
return [] if source.nil? || source.strip.empty?
|
|
313
|
+
|
|
314
|
+
MilkTea::Lexer.new(source).lex
|
|
315
|
+
.reject { |token| [:newline, :indent, :dedent, :eof].include?(token.type) }
|
|
316
|
+
.map do |token|
|
|
317
|
+
adjusted_line = part[:line] + token.line - 1
|
|
318
|
+
adjusted_column = token.line == 1 ? (part[:column] + token.column - 1) : token.column
|
|
319
|
+
|
|
320
|
+
token.with(
|
|
321
|
+
line: adjusted_line,
|
|
322
|
+
column: adjusted_column,
|
|
323
|
+
start_offset: nil,
|
|
324
|
+
end_offset: nil,
|
|
325
|
+
leading_trivia: [].freeze,
|
|
326
|
+
trailing_trivia: [].freeze,
|
|
327
|
+
)
|
|
328
|
+
end
|
|
329
|
+
rescue MilkTea::LexError
|
|
330
|
+
[]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def token_contains_position?(token, target_line, target_char)
|
|
334
|
+
segments = token.lexeme.split("\n", -1)
|
|
335
|
+
end_line = token.line + segments.length - 1
|
|
336
|
+
return false if target_line < token.line || target_line > end_line
|
|
337
|
+
|
|
338
|
+
if segments.length == 1
|
|
339
|
+
return token.column <= target_char && target_char < (token.column + segments.first.length)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
if target_line == token.line
|
|
343
|
+
return token.column <= target_char && target_char <= (token.column + segments.first.length - 1)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
target_char <= segments.fetch(target_line - token.line).length
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def render_hover_markdown(info)
|
|
350
|
+
lines = []
|
|
351
|
+
|
|
352
|
+
signature = info[:signature].to_s
|
|
353
|
+
shortened, modules = shorten_qualified_types(signature)
|
|
354
|
+
|
|
355
|
+
lines << "```milk-tea"
|
|
356
|
+
lines << shortened
|
|
357
|
+
lines << "```"
|
|
358
|
+
|
|
359
|
+
unless modules.empty?
|
|
360
|
+
prefix = 'From'
|
|
361
|
+
lines << "*#{prefix} #{modules.join(', ')}*"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
rendered_doc_comment = false
|
|
365
|
+
if info[:doc_comment].is_a?(Hash)
|
|
366
|
+
rendered_doc_comment = append_structured_doc_comment_markdown(lines, info[:doc_comment])
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
docs = info[:docs].to_s.strip
|
|
370
|
+
unless rendered_doc_comment || docs.empty?
|
|
371
|
+
lines << ""
|
|
372
|
+
lines << docs
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
source_uri = info[:source_uri]
|
|
376
|
+
source_line = info[:source_line]
|
|
377
|
+
source_label = info[:source].to_s.strip
|
|
378
|
+
unless source_label.empty?
|
|
379
|
+
lines << ""
|
|
380
|
+
if source_uri && source_line
|
|
381
|
+
link_uri = "#{source_uri}#L#{source_line}"
|
|
382
|
+
lines << "Defined at: [#{source_label}](#{link_uri})"
|
|
383
|
+
else
|
|
384
|
+
lines << "Defined at: #{source_label}"
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
lines.join("\n")
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def shorten_qualified_types(signature)
|
|
392
|
+
modules = []
|
|
393
|
+
shortened = signature.gsub(/\b([a-z_][\w]*(?:\.[a-z_][\w]*)*\.)([A-Z]\w*(?:\[[^\]]*\])?(?:\s*\|\s*(?:[a-z_][\w]*(?:\.[a-z_][\w]*)*\.)?[A-Z]\w*(?:\[[^\]]*\])?)*)\b/) do
|
|
394
|
+
prefix = $1
|
|
395
|
+
mod_name = prefix.delete_suffix('.')
|
|
396
|
+
modules << mod_name unless modules.include?(mod_name)
|
|
397
|
+
$2
|
|
398
|
+
end
|
|
399
|
+
[shortened, modules]
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def append_structured_doc_comment_markdown(lines, doc_comment)
|
|
403
|
+
body = doc_comment[:body_markdown].to_s.strip
|
|
404
|
+
tags = doc_comment.fetch(:tags, {})
|
|
405
|
+
params = Array(tags[:params]).reject { |entry| entry[:name].to_s.strip.empty? }
|
|
406
|
+
returns = tags[:returns]
|
|
407
|
+
throws = Array(tags[:throws]).reject { |entry| entry[:text].to_s.strip.empty? }
|
|
408
|
+
see_also = Array(tags[:see]).reject { |entry| entry[:text].to_s.strip.empty? }
|
|
409
|
+
|
|
410
|
+
wrote = false
|
|
411
|
+
unless body.empty?
|
|
412
|
+
lines << ""
|
|
413
|
+
lines << body
|
|
414
|
+
wrote = true
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
unless params.empty?
|
|
418
|
+
lines << ""
|
|
419
|
+
lines << "**Parameters**"
|
|
420
|
+
params.each do |entry|
|
|
421
|
+
description = entry[:text].to_s.strip
|
|
422
|
+
if description.empty?
|
|
423
|
+
lines << "- `#{entry[:name]}`"
|
|
424
|
+
else
|
|
425
|
+
lines << "- `#{entry[:name]}`: #{description}"
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
wrote = true
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
if returns
|
|
432
|
+
description = returns[:text].to_s.strip
|
|
433
|
+
unless description.empty?
|
|
434
|
+
lines << ""
|
|
435
|
+
lines << "**Returns**"
|
|
436
|
+
lines << description
|
|
437
|
+
wrote = true
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
unless throws.empty?
|
|
442
|
+
lines << ""
|
|
443
|
+
lines << "**Throws**"
|
|
444
|
+
throws.each do |entry|
|
|
445
|
+
lines << "- #{entry[:text]}"
|
|
446
|
+
end
|
|
447
|
+
wrote = true
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
unless see_also.empty?
|
|
451
|
+
lines << ""
|
|
452
|
+
lines << "**See Also**"
|
|
453
|
+
see_also.each do |entry|
|
|
454
|
+
lines << "- #{entry[:text]}"
|
|
455
|
+
end
|
|
456
|
+
wrote = true
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
wrote
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def hover_doc_comment_for_definition(definition_entry)
|
|
463
|
+
return nil unless definition_entry
|
|
464
|
+
|
|
465
|
+
@workspace.doc_comment_for_definition(definition_entry[:uri], definition_entry[:token])
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def hover_doc_comment_data_for_definition(definition_entry)
|
|
469
|
+
return nil unless definition_entry
|
|
470
|
+
|
|
471
|
+
@workspace.doc_comment_data_for_definition(definition_entry[:uri], definition_entry[:token])
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def signature_help_doc_comment_for_call(uri, name, lsp_line, lsp_char)
|
|
475
|
+
definition_entry = @workspace.find_definition_token_global(
|
|
476
|
+
name,
|
|
477
|
+
preferred_uri: uri,
|
|
478
|
+
before_line: lsp_line + 1,
|
|
479
|
+
before_char: lsp_char + 1,
|
|
480
|
+
)
|
|
481
|
+
return nil unless definition_entry
|
|
482
|
+
|
|
483
|
+
@workspace.doc_comment_data_for_definition(definition_entry[:uri], definition_entry[:token])
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def signature_help_markdown_for_doc_comment(doc_comment)
|
|
487
|
+
return '' unless doc_comment.is_a?(Hash)
|
|
488
|
+
|
|
489
|
+
lines = []
|
|
490
|
+
body = doc_comment[:body_markdown].to_s.strip
|
|
491
|
+
lines << body unless body.empty?
|
|
492
|
+
|
|
493
|
+
returns = doc_tag_return_description(doc_comment)
|
|
494
|
+
unless returns.empty?
|
|
495
|
+
lines << '' unless lines.empty?
|
|
496
|
+
lines << "**Returns**"
|
|
497
|
+
lines << returns
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
lines.join("\n")
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def doc_tag_param_descriptions(doc_comment)
|
|
504
|
+
return {} unless doc_comment.is_a?(Hash)
|
|
505
|
+
|
|
506
|
+
Array(doc_comment.dig(:tags, :params)).each_with_object({}) do |entry, docs|
|
|
507
|
+
name = entry[:name].to_s
|
|
508
|
+
next if name.empty?
|
|
509
|
+
|
|
510
|
+
text = entry[:text].to_s.strip
|
|
511
|
+
next if text.empty?
|
|
512
|
+
|
|
513
|
+
docs[name] = text
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def doc_tag_return_description(doc_comment)
|
|
518
|
+
return '' unless doc_comment.is_a?(Hash)
|
|
519
|
+
|
|
520
|
+
doc_comment.dig(:tags, :returns, :text).to_s.strip
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def completion_function_documentation(uri, name, cache:)
|
|
524
|
+
key = [uri, name]
|
|
525
|
+
return cache[key] if cache.key?(key)
|
|
526
|
+
|
|
527
|
+
definition_entry = @workspace.find_definition_token_global(name, preferred_uri: uri)
|
|
528
|
+
unless definition_entry
|
|
529
|
+
cache[key] = ''
|
|
530
|
+
return ''
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
doc_comment = @workspace.doc_comment_data_for_definition(definition_entry[:uri], definition_entry[:token])
|
|
534
|
+
cache[key] = signature_help_markdown_for_doc_comment(doc_comment)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def hover_source_label_for_definition(definition_entry)
|
|
538
|
+
return nil unless definition_entry
|
|
539
|
+
|
|
540
|
+
hover_source_label(definition_entry[:uri], definition_entry[:token].line)
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
def hover_source_uri_for_definition(definition_entry)
|
|
544
|
+
return nil unless definition_entry
|
|
545
|
+
|
|
546
|
+
definition_entry[:uri]
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def hover_source_line_for_definition(definition_entry)
|
|
550
|
+
return nil unless definition_entry
|
|
551
|
+
|
|
552
|
+
definition_entry[:token].line
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def hover_source_label_from_location(location)
|
|
556
|
+
return nil unless location
|
|
557
|
+
|
|
558
|
+
line = location.dig(:range, :start, :line)
|
|
559
|
+
hover_source_label(location[:uri], (line || 0) + 1)
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def hover_source_uri_from_location(location)
|
|
563
|
+
return nil unless location
|
|
564
|
+
|
|
565
|
+
location[:uri]
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def hover_source_line_from_location(location)
|
|
569
|
+
return nil unless location
|
|
570
|
+
|
|
571
|
+
line = location.dig(:range, :start, :line)
|
|
572
|
+
(line || 0) + 1
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def hover_source_label(uri, line)
|
|
576
|
+
path = uri_to_path(uri)
|
|
577
|
+
return nil unless path
|
|
578
|
+
|
|
579
|
+
display = path
|
|
580
|
+
if @root_uri
|
|
581
|
+
root_path = uri_to_path(@root_uri)
|
|
582
|
+
if root_path
|
|
583
|
+
begin
|
|
584
|
+
relative = Pathname.new(path).relative_path_from(Pathname.new(root_path)).to_s
|
|
585
|
+
display = relative unless relative.start_with?('..')
|
|
586
|
+
rescue StandardError
|
|
587
|
+
display = path
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
"#{display}:#{line}"
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def hover_definition_entry_from_location(location)
|
|
596
|
+
return nil unless location
|
|
597
|
+
|
|
598
|
+
start = location.dig(:range, :start)
|
|
599
|
+
return nil unless start
|
|
600
|
+
|
|
601
|
+
token = @workspace.find_token_at(location[:uri], start[:line], start[:character])
|
|
602
|
+
return nil unless token
|
|
603
|
+
|
|
604
|
+
{ uri: location[:uri], token: token }
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def format_params(params)
|
|
608
|
+
params.map { |p| "#{p.name}: #{p.type}" }.join(', ')
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def interface_signature(binding)
|
|
612
|
+
method_lines = binding.methods.values.map do |method|
|
|
613
|
+
" #{interface_method_signature(method)}"
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
(["interface #{binding.name}"] + method_lines).join("\n")
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def interface_method_signature(binding)
|
|
620
|
+
keyword = binding.kind == :editable ? 'editable function' : 'function'
|
|
621
|
+
keyword = "async #{keyword}" if binding.async
|
|
622
|
+
"#{keyword} #{binding.name}(#{format_params(binding.params)}) -> #{binding.return_type}"
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
def resolve_dot_receiver_value_type(facts, receiver_name, line, char)
|
|
626
|
+
local_type = resolve_local_hover_type(facts, receiver_name, line, char)
|
|
627
|
+
return local_type if local_type
|
|
628
|
+
|
|
629
|
+
facts.values[receiver_name]&.type
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def resolve_member_access_hover_info(current_uri, facts, tokens, token_index)
|
|
633
|
+
chain = member_access_chain_at(tokens, token_index)
|
|
634
|
+
return nil unless chain
|
|
635
|
+
|
|
636
|
+
hovered_segment = chain[:segments].find { |segment| segment[:token_index] == token_index }
|
|
637
|
+
return nil unless hovered_segment && hovered_segment[:position].positive?
|
|
638
|
+
|
|
639
|
+
current_type = resolve_dot_receiver_value_type(
|
|
640
|
+
facts,
|
|
641
|
+
chain[:segments].first[:name],
|
|
642
|
+
chain[:line],
|
|
643
|
+
chain[:char],
|
|
644
|
+
)
|
|
645
|
+
unless current_type
|
|
646
|
+
first_name = chain[:segments].first[:name]
|
|
647
|
+
current_type = facts.types[first_name]
|
|
648
|
+
end
|
|
649
|
+
return nil unless current_type
|
|
650
|
+
|
|
651
|
+
chain[:segments][1..hovered_segment[:position]].each do |segment|
|
|
652
|
+
field_receiver_type = project_field_receiver_type_for_completion(current_type)
|
|
653
|
+
if field_receiver_type.respond_to?(:field) && (field_type = field_receiver_type.field(segment[:name]))
|
|
654
|
+
source_location = field_definition_location(current_uri, field_receiver_type, segment[:name])
|
|
655
|
+
|
|
656
|
+
if segment[:token_index] == token_index
|
|
657
|
+
return {
|
|
658
|
+
signature: field_hover_signature(segment[:name], field_type),
|
|
659
|
+
docs: nil,
|
|
660
|
+
source: hover_source_label_from_location(source_location),
|
|
661
|
+
source_uri: hover_source_uri_from_location(source_location),
|
|
662
|
+
source_line: hover_source_line_from_location(source_location),
|
|
663
|
+
}
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
current_type = field_type
|
|
667
|
+
next
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
if current_type.respond_to?(:nested_types) && (nested = current_type.nested_types[segment[:name]])
|
|
671
|
+
if segment[:token_index] == token_index
|
|
672
|
+
return {
|
|
673
|
+
signature: type_hover_signature(segment[:name], nested),
|
|
674
|
+
docs: nil,
|
|
675
|
+
}
|
|
676
|
+
end
|
|
677
|
+
current_type = nested
|
|
678
|
+
next
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
next unless segment[:token_index] == token_index
|
|
682
|
+
|
|
683
|
+
method_receiver_type = project_method_receiver_type_for_completion(current_type)
|
|
684
|
+
method_info = member_method_info_for_receiver_type(facts, method_receiver_type, segment[:name])
|
|
685
|
+
return nil unless method_info
|
|
686
|
+
|
|
687
|
+
source_location = module_member_binding_location(current_uri, method_info[:module_name], segment[:name], method_info[:binding])
|
|
688
|
+
source_location ||= module_member_definition_location(current_uri, method_info[:module_name], segment[:name])
|
|
689
|
+
|
|
690
|
+
return {
|
|
691
|
+
signature: method_signature(method_info[:binding]),
|
|
692
|
+
docs: nil,
|
|
693
|
+
source: hover_source_label_from_location(source_location),
|
|
694
|
+
source_uri: hover_source_uri_from_location(source_location),
|
|
695
|
+
source_line: hover_source_line_from_location(source_location),
|
|
696
|
+
}
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
nil
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def member_method_info_for_receiver_type(facts, receiver_type, method_name)
|
|
703
|
+
return nil unless receiver_type
|
|
704
|
+
|
|
705
|
+
dispatch_receiver_type = method_dispatch_receiver_type_for_completion(receiver_type)
|
|
706
|
+
|
|
707
|
+
if (binding = find_method_entry(facts.methods, receiver_type, method_name))
|
|
708
|
+
return {
|
|
709
|
+
binding: binding,
|
|
710
|
+
module_name: facts.module_name,
|
|
711
|
+
}
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
if dispatch_receiver_type != receiver_type && (binding = find_method_entry(facts.methods, dispatch_receiver_type, method_name))
|
|
715
|
+
return {
|
|
716
|
+
binding: binding,
|
|
717
|
+
module_name: facts.module_name,
|
|
718
|
+
}
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
facts.imports.each_value do |module_binding|
|
|
722
|
+
binding = find_method_entry(module_binding.methods, receiver_type, method_name)
|
|
723
|
+
if binding.nil? && dispatch_receiver_type != receiver_type
|
|
724
|
+
binding = find_method_entry(module_binding.methods, dispatch_receiver_type, method_name)
|
|
725
|
+
end
|
|
726
|
+
next unless binding
|
|
727
|
+
|
|
728
|
+
return {
|
|
729
|
+
binding: binding,
|
|
730
|
+
module_name: module_binding.name,
|
|
731
|
+
}
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
nil
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def find_method_entry(methods_table, receiver_type, method_name)
|
|
738
|
+
methods_table.fetch(receiver_type, {})[method_name] || methods_table.fetch(receiver_type, {})["static:#{method_name}"]
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def resolve_enum_member_hover_info(current_uri, facts, tokens, token_index)
|
|
742
|
+
member_info = resolve_enum_member_access_info(current_uri, facts, tokens, token_index)
|
|
743
|
+
return nil unless member_info
|
|
744
|
+
|
|
745
|
+
signature = "#{member_info[:member_name]}: #{member_info[:receiver_label]}"
|
|
746
|
+
signature += " = #{member_info[:value_text]}" if member_info[:value_text]
|
|
747
|
+
|
|
748
|
+
{
|
|
749
|
+
signature: signature,
|
|
750
|
+
docs: nil,
|
|
751
|
+
source: hover_source_label_from_location(member_info[:location]),
|
|
752
|
+
source_uri: hover_source_uri_from_location(member_info[:location]),
|
|
753
|
+
source_line: hover_source_line_from_location(member_info[:location]),
|
|
754
|
+
}
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
def resolve_enum_member_definition_location(current_uri, facts, tokens, token_index)
|
|
758
|
+
resolve_enum_member_access_info(current_uri, facts, tokens, token_index)&.fetch(:location, nil)
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def resolve_enum_member_access_info(current_uri, facts, tokens, token_index)
|
|
762
|
+
return nil unless type_name_member_access?(tokens, token_index, facts)
|
|
763
|
+
|
|
764
|
+
token = tokens[token_index]
|
|
765
|
+
token_end_char = token.column - 1 + token.lexeme.length
|
|
766
|
+
receiver_name = @workspace.find_dot_receiver(current_uri, token.line - 1, token_end_char)
|
|
767
|
+
receiver_path = @workspace.find_dot_receiver_path(current_uri, token.line - 1, token_end_char)
|
|
768
|
+
receiver_info = resolve_type_receiver_info(facts, receiver_name, receiver_path)
|
|
769
|
+
return nil unless receiver_info
|
|
770
|
+
|
|
771
|
+
receiver_type = receiver_info[:type]
|
|
772
|
+
return nil unless receiver_type.is_a?(Types::EnumBase)
|
|
773
|
+
return nil unless receiver_type.member(token.lexeme)
|
|
774
|
+
|
|
775
|
+
owner_module_name = receiver_type.respond_to?(:module_name) ? receiver_type.module_name : receiver_info[:module_name]
|
|
776
|
+
|
|
777
|
+
{
|
|
778
|
+
receiver_label: receiver_info[:label],
|
|
779
|
+
member_name: token.lexeme,
|
|
780
|
+
value_text: enum_member_value_text(current_uri, owner_module_name, receiver_type.name, token.lexeme),
|
|
781
|
+
location: enum_member_definition_location(current_uri, owner_module_name, receiver_type.name, token.lexeme),
|
|
782
|
+
}
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
def resolve_field_declaration_hover_info(current_uri, facts, tokens, token_index)
|
|
786
|
+
receiver_info = field_declaration_receiver_info(facts, tokens, token_index)
|
|
787
|
+
return nil unless receiver_info
|
|
788
|
+
|
|
789
|
+
field_name = tokens[token_index].lexeme
|
|
790
|
+
receiver_type = project_field_receiver_type_for_completion(receiver_info[:type])
|
|
791
|
+
return nil unless receiver_type.respond_to?(:field)
|
|
792
|
+
|
|
793
|
+
field_type = receiver_type.field(field_name)
|
|
794
|
+
return nil unless field_type
|
|
795
|
+
|
|
796
|
+
source_location = field_definition_location(current_uri, receiver_type, field_name)
|
|
797
|
+
|
|
798
|
+
{
|
|
799
|
+
signature: field_hover_signature(field_name, field_type),
|
|
800
|
+
docs: nil,
|
|
801
|
+
source: hover_source_label_from_location(source_location),
|
|
802
|
+
source_uri: hover_source_uri_from_location(source_location),
|
|
803
|
+
source_line: hover_source_line_from_location(source_location),
|
|
804
|
+
}
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
def resolve_named_argument_label_hover_info(current_uri, facts, tokens, token_index)
|
|
808
|
+
receiver_info = named_argument_label_receiver_info(facts, tokens, token_index)
|
|
809
|
+
|
|
810
|
+
field_name = tokens[token_index].lexeme
|
|
811
|
+
if receiver_info
|
|
812
|
+
receiver_type = project_field_receiver_type_for_completion(receiver_info[:type])
|
|
813
|
+
if receiver_type.respond_to?(:field)
|
|
814
|
+
field_type = receiver_type.field(field_name)
|
|
815
|
+
if field_type
|
|
816
|
+
source_location = field_definition_location(current_uri, receiver_type, field_name)
|
|
817
|
+
return {
|
|
818
|
+
signature: field_hover_signature(field_name, field_type),
|
|
819
|
+
docs: nil,
|
|
820
|
+
source: hover_source_label_from_location(source_location),
|
|
821
|
+
source_uri: hover_source_uri_from_location(source_location),
|
|
822
|
+
source_line: hover_source_line_from_location(source_location),
|
|
823
|
+
}
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
param_info = named_argument_parameter_info(facts, tokens, token_index)
|
|
829
|
+
return nil unless param_info
|
|
830
|
+
|
|
831
|
+
{
|
|
832
|
+
signature: param_info[:signature],
|
|
833
|
+
docs: param_info[:docs],
|
|
834
|
+
source: param_info[:source],
|
|
835
|
+
source_uri: param_info[:source_uri],
|
|
836
|
+
source_line: param_info[:source_line],
|
|
837
|
+
}
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
def named_argument_parameter_info(facts, tokens, token_index)
|
|
841
|
+
receiver_name = named_argument_callee_name(tokens, token_index)
|
|
842
|
+
return nil unless receiver_name
|
|
843
|
+
|
|
844
|
+
field_name = tokens[token_index].lexeme
|
|
845
|
+
|
|
846
|
+
if facts.functions.key?(receiver_name)
|
|
847
|
+
func = facts.functions[receiver_name]
|
|
848
|
+
func.type.params.each do |param|
|
|
849
|
+
next unless param.name == field_name
|
|
850
|
+
return named_argument_param_result(field_name, param, func.ast)
|
|
851
|
+
end
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
facts.methods.each_value do |methods|
|
|
855
|
+
method = methods[receiver_name]
|
|
856
|
+
next unless method
|
|
857
|
+
method.type.params.each do |param|
|
|
858
|
+
next unless param.name == field_name
|
|
859
|
+
return named_argument_param_result(field_name, param, method.ast)
|
|
860
|
+
end
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
nil
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def named_argument_param_result(field_name, param, callable_ast)
|
|
867
|
+
param_ast = callable_ast.params.find { |p| p.name == field_name }
|
|
868
|
+
source_location = param_ast ? ast_name_range(field_name, param_ast.line, param_ast.column) : nil
|
|
869
|
+
doc_tags = callable_ast.respond_to?(:doc_comment) ? param_doc_for_name(callable_ast, field_name) : nil
|
|
870
|
+
|
|
871
|
+
{
|
|
872
|
+
signature: "#{field_name}: #{param.type}",
|
|
873
|
+
docs: doc_tags,
|
|
874
|
+
source: hover_source_label_from_location(source_location),
|
|
875
|
+
source_uri: hover_source_uri_from_location(source_location),
|
|
876
|
+
source_line: hover_source_line_from_location(source_location),
|
|
877
|
+
}
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
def param_doc_for_name(callable_ast, param_name)
|
|
881
|
+
return nil unless callable_ast.respond_to?(:doc_comment)
|
|
882
|
+
return nil unless callable_ast.doc_comment
|
|
883
|
+
|
|
884
|
+
lines = callable_ast.doc_comment.lines
|
|
885
|
+
param_lines = []
|
|
886
|
+
in_param = false
|
|
887
|
+
lines.each do |line|
|
|
888
|
+
if line =~ /@param\s+#{Regexp.escape(param_name)}\b/
|
|
889
|
+
in_param = true
|
|
890
|
+
param_lines << line.sub(/^\s*##\s*@param\s+#{Regexp.escape(param_name)}\s*/, "")
|
|
891
|
+
next
|
|
892
|
+
end
|
|
893
|
+
if in_param
|
|
894
|
+
if line =~ /@param\b/
|
|
895
|
+
break
|
|
896
|
+
end
|
|
897
|
+
param_lines << line.sub(/^\s*##\s*/, "")
|
|
898
|
+
end
|
|
899
|
+
end
|
|
900
|
+
param_lines.join(" ").strip.empty? ? nil : param_lines.join(" ").strip
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
def named_argument_callee_name(tokens, token_index)
|
|
904
|
+
opener_index = parameter_list_opener_index(tokens, token_index)
|
|
905
|
+
return nil unless opener_index
|
|
906
|
+
|
|
907
|
+
head_index = previous_non_trivia_token_index(tokens, opener_index)
|
|
908
|
+
return nil unless head_index
|
|
909
|
+
return nil unless tokens[head_index].type == :identifier
|
|
910
|
+
|
|
911
|
+
tokens[head_index].lexeme
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
def field_declaration_receiver_info(facts, tokens, token_index)
|
|
915
|
+
token = tokens[token_index]
|
|
916
|
+
return nil unless token
|
|
917
|
+
|
|
918
|
+
i = token_index - 1
|
|
919
|
+
while i >= 0
|
|
920
|
+
current = tokens[i]
|
|
921
|
+
i -= 1
|
|
922
|
+
|
|
923
|
+
next if [:newline, :indent, :dedent, :eof].include?(current.type)
|
|
924
|
+
next if current.line == token.line
|
|
925
|
+
next if current.column >= token.column
|
|
926
|
+
|
|
927
|
+
header_line_tokens = non_trivia_tokens_on_line(tokens, current.line)
|
|
928
|
+
header = header_line_tokens.first
|
|
929
|
+
return nil unless [:struct, :union].include?(header&.type)
|
|
930
|
+
|
|
931
|
+
type_token = header_line_tokens[1]
|
|
932
|
+
return nil unless type_token&.type == :identifier
|
|
933
|
+
|
|
934
|
+
return resolve_type_receiver_info(facts, type_token.lexeme, type_token.lexeme)
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
nil
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
def named_argument_label_receiver_info(facts, tokens, token_index)
|
|
941
|
+
opener_index = parameter_list_opener_index(tokens, token_index)
|
|
942
|
+
return nil unless opener_index
|
|
943
|
+
|
|
944
|
+
head_index = previous_non_trivia_token_index(tokens, opener_index)
|
|
945
|
+
return nil unless head_index
|
|
946
|
+
|
|
947
|
+
if tokens[head_index].type == :rbracket
|
|
948
|
+
lbracket_index = matching_opener_index(tokens, head_index)
|
|
949
|
+
return nil unless lbracket_index
|
|
950
|
+
|
|
951
|
+
head_index = previous_non_trivia_token_index(tokens, lbracket_index)
|
|
952
|
+
return nil unless head_index
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
head = tokens[head_index]
|
|
956
|
+
return nil unless head.type == :identifier
|
|
957
|
+
|
|
958
|
+
receiver_name = head.lexeme
|
|
959
|
+
receiver_path = receiver_name
|
|
960
|
+
|
|
961
|
+
dot_index = previous_non_trivia_token_index(tokens, head_index)
|
|
962
|
+
if dot_index && tokens[dot_index].type == :dot
|
|
963
|
+
module_index = previous_non_trivia_token_index(tokens, dot_index)
|
|
964
|
+
return nil unless module_index && tokens[module_index].type == :identifier
|
|
965
|
+
|
|
966
|
+
receiver_path = "#{tokens[module_index].lexeme}.#{receiver_name}"
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
resolve_type_receiver_info(facts, receiver_name, receiver_path)
|
|
970
|
+
end
|
|
971
|
+
|
|
972
|
+
def member_access_chain_at(tokens, token_index)
|
|
973
|
+
token = tokens[token_index]
|
|
974
|
+
return nil unless token&.type == :identifier
|
|
975
|
+
|
|
976
|
+
indices = [token_index]
|
|
977
|
+
current_index = token_index
|
|
978
|
+
|
|
979
|
+
loop do
|
|
980
|
+
dot_index = previous_non_trivia_token_index(tokens, current_index)
|
|
981
|
+
break unless dot_index && tokens[dot_index].type == :dot && tokens[dot_index].line == token.line
|
|
982
|
+
|
|
983
|
+
receiver_index = previous_non_trivia_token_index(tokens, dot_index)
|
|
984
|
+
break unless receiver_index && tokens[receiver_index].type == :identifier && tokens[receiver_index].line == token.line
|
|
985
|
+
|
|
986
|
+
indices.unshift(receiver_index)
|
|
987
|
+
current_index = receiver_index
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
current_index = token_index
|
|
991
|
+
loop do
|
|
992
|
+
dot_index = next_non_trivia_token_index(tokens, current_index + 1)
|
|
993
|
+
break unless dot_index && tokens[dot_index].type == :dot && tokens[dot_index].line == token.line
|
|
994
|
+
|
|
995
|
+
member_index = next_non_trivia_token_index(tokens, dot_index + 1)
|
|
996
|
+
break unless member_index && tokens[member_index].type == :identifier && tokens[member_index].line == token.line
|
|
997
|
+
|
|
998
|
+
indices << member_index
|
|
999
|
+
current_index = member_index
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
return nil if indices.length < 2
|
|
1003
|
+
|
|
1004
|
+
{
|
|
1005
|
+
line: token.line,
|
|
1006
|
+
char: token.column + token.lexeme.length,
|
|
1007
|
+
segments: indices.each_with_index.map do |index, position|
|
|
1008
|
+
{
|
|
1009
|
+
name: tokens[index].lexeme,
|
|
1010
|
+
token_index: index,
|
|
1011
|
+
position: position,
|
|
1012
|
+
}
|
|
1013
|
+
end,
|
|
1014
|
+
}
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
def method_binding_at_token(facts, token)
|
|
1018
|
+
facts.methods.each_value do |methods|
|
|
1019
|
+
methods.each_value do |binding|
|
|
1020
|
+
next unless binding.name == token.lexeme
|
|
1021
|
+
next unless binding.ast.is_a?(AST::MethodDef)
|
|
1022
|
+
next unless binding.ast.line == token.line
|
|
1023
|
+
next unless binding.ast.respond_to?(:column) && binding.ast.column == token.column
|
|
1024
|
+
|
|
1025
|
+
return binding
|
|
1026
|
+
end
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
facts.interfaces.each_value do |interface_binding|
|
|
1030
|
+
interface_binding.methods.each_value do |method_binding|
|
|
1031
|
+
next unless method_binding.name == token.lexeme
|
|
1032
|
+
next unless method_binding.ast.respond_to?(:line) && method_binding.ast.line == token.line
|
|
1033
|
+
next unless method_binding.ast.respond_to?(:column) && method_binding.ast.column == token.column
|
|
1034
|
+
|
|
1035
|
+
return method_binding
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
nil
|
|
1040
|
+
end
|
|
1041
|
+
|
|
1042
|
+
def method_signature(binding)
|
|
1043
|
+
async_prefix = (binding.respond_to?(:async) && binding.async) || (binding.ast.respond_to?(:async) && binding.ast.async) ? 'async ' : ''
|
|
1044
|
+
if binding.respond_to?(:type) && binding.type
|
|
1045
|
+
params_str = format_params(binding.type.params)
|
|
1046
|
+
keyword = case binding.ast.kind
|
|
1047
|
+
when :editable
|
|
1048
|
+
"editable function"
|
|
1049
|
+
when :static
|
|
1050
|
+
"static function"
|
|
1051
|
+
else
|
|
1052
|
+
"function"
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
"#{async_prefix}#{keyword} #{binding.name}(#{params_str}) -> #{binding.type.return_type}"
|
|
1056
|
+
else
|
|
1057
|
+
params_str = binding.params.map { |p| "#{p.name}: #{p.type}" }.join(', ')
|
|
1058
|
+
keyword = case binding.kind
|
|
1059
|
+
when :editable
|
|
1060
|
+
"editable function"
|
|
1061
|
+
when :static
|
|
1062
|
+
"static function"
|
|
1063
|
+
else
|
|
1064
|
+
"function"
|
|
1065
|
+
end
|
|
1066
|
+
|
|
1067
|
+
"#{async_prefix}#{keyword} #{binding.name}(#{params_str}) -> #{binding.return_type}"
|
|
1068
|
+
end
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
def type_hover_signature(name, type)
|
|
1072
|
+
rendered_type = type.to_s
|
|
1073
|
+
return "type #{name}" if rendered_type == name
|
|
1074
|
+
|
|
1075
|
+
"type #{name} = #{rendered_type}"
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1078
|
+
def field_hover_signature(name, type)
|
|
1079
|
+
"field #{name}: #{type}"
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
def builtin_hover_info(name, tokens, token_index)
|
|
1083
|
+
specialization_info = builtin_value_specialization_info(name, tokens, token_index)
|
|
1084
|
+
return specialization_info if specialization_info
|
|
1085
|
+
|
|
1086
|
+
specialized_call_info = builtin_specialized_call_hover_info(name, tokens, token_index)
|
|
1087
|
+
return specialized_call_info if specialized_call_info
|
|
1088
|
+
|
|
1089
|
+
type_constructor_info = builtin_type_constructor_hover_info(name, tokens, token_index)
|
|
1090
|
+
return type_constructor_info if type_constructor_info
|
|
1091
|
+
|
|
1092
|
+
builtin_call_hover_info(name, tokens, token_index)
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
def builtin_in_member_access_context?(tokens, token_index)
|
|
1096
|
+
prev_index = previous_non_trivia_token_index(tokens, token_index)
|
|
1097
|
+
prev_index && tokens[prev_index].type == :dot
|
|
1098
|
+
end
|
|
1099
|
+
|
|
1100
|
+
def builtin_value_specialization_info(name, tokens, token_index)
|
|
1101
|
+
return nil unless %w[zero default reinterpret].include?(name)
|
|
1102
|
+
|
|
1103
|
+
return nil if builtin_in_member_access_context?(tokens, token_index)
|
|
1104
|
+
|
|
1105
|
+
lbracket_index = next_non_trivia_token_index(tokens, token_index + 1)
|
|
1106
|
+
return nil unless lbracket_index && tokens[lbracket_index].type == :lbracket
|
|
1107
|
+
|
|
1108
|
+
rbracket_index = matching_closer_index(tokens, lbracket_index, :lbracket, :rbracket)
|
|
1109
|
+
return nil unless rbracket_index
|
|
1110
|
+
|
|
1111
|
+
specialization = render_builtin_specialization(tokens[token_index..rbracket_index])
|
|
1112
|
+
target_type = render_builtin_specialization(tokens[(lbracket_index + 1)...rbracket_index])
|
|
1113
|
+
return nil if target_type.empty?
|
|
1114
|
+
|
|
1115
|
+
docs = case name
|
|
1116
|
+
when 'zero'
|
|
1117
|
+
'`zero[T]` returns the raw zero-initialized value for `T`. It is a value form, not a callable.'
|
|
1118
|
+
when 'default'
|
|
1119
|
+
'`default[T]` returns the semantic default value for `T` and requires an accessible zero-argument associated function `T.default()` that returns `T`. It is a value form, not a callable.'
|
|
1120
|
+
when 'reinterpret'
|
|
1121
|
+
'`reinterpret[T](value)` bit-casts a value to `T`; it requires `unsafe` and compatible concrete sized types.'
|
|
1122
|
+
end
|
|
1123
|
+
|
|
1124
|
+
{
|
|
1125
|
+
signature: if name == 'reinterpret'
|
|
1126
|
+
"builtin #{specialization}(value) -> #{target_type}"
|
|
1127
|
+
else
|
|
1128
|
+
"builtin #{specialization} -> #{target_type}"
|
|
1129
|
+
end,
|
|
1130
|
+
docs: docs,
|
|
1131
|
+
}
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
def builtin_type_constructor_hover_info(name, tokens, token_index)
|
|
1135
|
+
return nil unless %w[array span Option Result SoA].include?(name)
|
|
1136
|
+
|
|
1137
|
+
lbracket_index = next_non_trivia_token_index(tokens, token_index + 1)
|
|
1138
|
+
return nil unless lbracket_index && tokens[lbracket_index].type == :lbracket
|
|
1139
|
+
|
|
1140
|
+
rbracket_index = matching_closer_index(tokens, lbracket_index, :lbracket, :rbracket)
|
|
1141
|
+
return nil unless rbracket_index
|
|
1142
|
+
|
|
1143
|
+
specialization = render_builtin_specialization(tokens[token_index..rbracket_index])
|
|
1144
|
+
after_bracket_index = next_non_trivia_token_index(tokens, rbracket_index + 1)
|
|
1145
|
+
|
|
1146
|
+
if after_bracket_index && tokens[after_bracket_index].type == :lparen
|
|
1147
|
+
docs = case name
|
|
1148
|
+
when 'array'
|
|
1149
|
+
'`array[T, N](...)` constructs a fixed-length array value of type `array[T, N]`.'
|
|
1150
|
+
when 'span'
|
|
1151
|
+
'`span[T](data = ..., len = ...)` constructs a span view over contiguous `T` storage.'
|
|
1152
|
+
when 'SoA'
|
|
1153
|
+
'`SoA[T, N](...)` constructs a Struct-of-Arrays value with `N` elements of type `T`. Fields are stored in separate contiguous arrays.'
|
|
1154
|
+
when 'Option'
|
|
1155
|
+
'`Option[T]` is a built-in optional type with arms `some(value: T)` and `none`.'
|
|
1156
|
+
when 'Result'
|
|
1157
|
+
'`Result[T, E]` is a built-in result type with arms `success(value: T)` and `failure(error: E)`.'
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
return {
|
|
1161
|
+
signature: case name
|
|
1162
|
+
when 'array'
|
|
1163
|
+
"builtin #{specialization}(...) -> #{specialization}"
|
|
1164
|
+
when 'span'
|
|
1165
|
+
"builtin #{specialization}(data = ..., len = ...) -> #{specialization}"
|
|
1166
|
+
when 'SoA'
|
|
1167
|
+
"builtin #{specialization}(...) -> #{specialization}"
|
|
1168
|
+
when 'Option'
|
|
1169
|
+
"builtin #{specialization}(some: value = ...) / #{specialization}(none:)"
|
|
1170
|
+
when 'Result'
|
|
1171
|
+
"builtin #{specialization}(success: value = ...) / #{specialization}(failure: error = ...)"
|
|
1172
|
+
end,
|
|
1173
|
+
docs: docs,
|
|
1174
|
+
}
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1177
|
+
docs = case name
|
|
1178
|
+
when 'array'
|
|
1179
|
+
'`array[T, N]` is the built-in fixed-length array type.'
|
|
1180
|
+
when 'span'
|
|
1181
|
+
'`span[T]` is the built-in non-owning contiguous view type.'
|
|
1182
|
+
when 'SoA'
|
|
1183
|
+
'`SoA[T, N]` is the built-in Struct-of-Arrays type. Each struct field is stored in a separate contiguous array of `N` elements, improving SIMD/cache behavior for parallel field access.'
|
|
1184
|
+
when 'Option'
|
|
1185
|
+
'`Option[T]` is the built-in optional value type with `some(value = ...)` and `none` arms.'
|
|
1186
|
+
when 'Result'
|
|
1187
|
+
'`Result[T, E]` is the built-in success/failure type with `success(value = ...)` and `failure(error = ...)` arms.'
|
|
1188
|
+
end
|
|
1189
|
+
|
|
1190
|
+
{
|
|
1191
|
+
signature: "builtin type #{specialization}",
|
|
1192
|
+
docs: docs,
|
|
1193
|
+
}
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
def builtin_specialized_call_hover_info(name, tokens, token_index)
|
|
1197
|
+
return nil unless BUILTIN_ASSOCIATED_HOOK_NAMES.include?(name) || name == 'attribute_arg'
|
|
1198
|
+
|
|
1199
|
+
return nil if builtin_in_member_access_context?(tokens, token_index)
|
|
1200
|
+
|
|
1201
|
+
lbracket_index = next_non_trivia_token_index(tokens, token_index + 1)
|
|
1202
|
+
return nil unless lbracket_index && tokens[lbracket_index].type == :lbracket
|
|
1203
|
+
|
|
1204
|
+
rbracket_index = matching_closer_index(tokens, lbracket_index, :lbracket, :rbracket)
|
|
1205
|
+
return nil unless rbracket_index
|
|
1206
|
+
|
|
1207
|
+
after_bracket_index = next_non_trivia_token_index(tokens, rbracket_index + 1)
|
|
1208
|
+
return nil unless after_bracket_index && tokens[after_bracket_index].type == :lparen
|
|
1209
|
+
|
|
1210
|
+
specialization = render_builtin_specialization(tokens[token_index..rbracket_index])
|
|
1211
|
+
if name == 'attribute_arg'
|
|
1212
|
+
target_type = render_builtin_specialization(tokens[(lbracket_index + 1)...rbracket_index])
|
|
1213
|
+
return nil if target_type.empty?
|
|
1214
|
+
|
|
1215
|
+
return {
|
|
1216
|
+
signature: "builtin #{specialization}(attribute, param_name) -> #{target_type}",
|
|
1217
|
+
docs: '`attribute_arg[T](attribute, param_name)` returns the compile-time argument value for the named attribute parameter; `T` must exactly match the declared parameter type.'
|
|
1218
|
+
}
|
|
1219
|
+
end
|
|
1220
|
+
|
|
1221
|
+
docs = case name
|
|
1222
|
+
when 'hash'
|
|
1223
|
+
'`hash[T](value)` lowers to `T.hash(value: const_ptr[T]) -> uint` after borrowing safe lvalues or forwarding existing refs and pointers.'
|
|
1224
|
+
when 'equal'
|
|
1225
|
+
'`equal[T](left, right)` lowers to `T.equal(left: const_ptr[T], right: const_ptr[T]) -> bool` after borrowing safe lvalues or forwarding existing refs and pointers.'
|
|
1226
|
+
when 'order'
|
|
1227
|
+
'`order[T](left, right)` lowers to `T.order(left: const_ptr[T], right: const_ptr[T]) -> int` after borrowing safe lvalues or forwarding existing refs and pointers.'
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
signature = case name
|
|
1231
|
+
when 'hash'
|
|
1232
|
+
"builtin #{specialization}(value) -> uint"
|
|
1233
|
+
when 'equal'
|
|
1234
|
+
"builtin #{specialization}(left, right) -> bool"
|
|
1235
|
+
when 'order'
|
|
1236
|
+
"builtin #{specialization}(left, right) -> int"
|
|
1237
|
+
end
|
|
1238
|
+
|
|
1239
|
+
{
|
|
1240
|
+
signature: signature,
|
|
1241
|
+
docs: docs,
|
|
1242
|
+
}
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
def builtin_call_hover_info(name, tokens, token_index)
|
|
1246
|
+
info = BUILTIN_CALL_HOVER_INFO[name]
|
|
1247
|
+
return nil unless info
|
|
1248
|
+
|
|
1249
|
+
return nil if builtin_in_member_access_context?(tokens, token_index)
|
|
1250
|
+
|
|
1251
|
+
next_index = next_non_trivia_token_index(tokens, token_index + 1)
|
|
1252
|
+
return nil unless next_index
|
|
1253
|
+
next_type = tokens[next_index].type
|
|
1254
|
+
return nil unless next_type == :lparen || next_type == :lbracket
|
|
1255
|
+
|
|
1256
|
+
info
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
def builtin_keyword_hover_info(token)
|
|
1260
|
+
return nil unless token
|
|
1261
|
+
|
|
1262
|
+
info = KEYWORD_HOVER_INFO[token.lexeme]
|
|
1263
|
+
return nil unless info
|
|
1264
|
+
return nil unless [:size_of, :align_of, :offset_of].include?(token.type)
|
|
1265
|
+
|
|
1266
|
+
info
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
def render_builtin_specialization(tokens)
|
|
1270
|
+
Array(tokens).map(&:lexeme).join.gsub(',', ', ')
|
|
1271
|
+
end
|
|
1272
|
+
|
|
1273
|
+
def value_hover_signature(binding)
|
|
1274
|
+
case binding.kind
|
|
1275
|
+
when :const
|
|
1276
|
+
"const #{binding.name}: #{binding.type} (immutable)"
|
|
1277
|
+
when :var
|
|
1278
|
+
"var #{binding.name}: #{binding.type} (mutable)"
|
|
1279
|
+
when :let
|
|
1280
|
+
"let #{binding.name}: #{binding.type} (immutable)"
|
|
1281
|
+
when :param
|
|
1282
|
+
"parameter #{binding.name}: #{binding.type} (immutable)"
|
|
1283
|
+
when :local
|
|
1284
|
+
suffix = binding.mutable ? 'mutable' : 'immutable'
|
|
1285
|
+
"local #{binding.name}: #{binding.type} (#{suffix})"
|
|
1286
|
+
else
|
|
1287
|
+
"#{binding.name}: #{binding.type}"
|
|
1288
|
+
end
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1291
|
+
def resolve_local_hover_binding(facts, name, line, char)
|
|
1292
|
+
declared_binding = declared_generic_local_hover_binding(facts, name, line)
|
|
1293
|
+
return declared_binding if declared_binding
|
|
1294
|
+
|
|
1295
|
+
frame = enclosing_completion_frame(facts, line)
|
|
1296
|
+
return nil unless frame
|
|
1297
|
+
|
|
1298
|
+
snapshot = latest_completion_snapshot(frame, line, char)
|
|
1299
|
+
binding = snapshot&.bindings&.dig(name)
|
|
1300
|
+
return binding if binding
|
|
1301
|
+
|
|
1302
|
+
future_snapshot = same_line_future_completion_snapshot(frame, line, char)
|
|
1303
|
+
future_snapshot&.bindings&.dig(name)
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
def resolve_as_binding_declaration_hover_binding(facts, name, line, char)
|
|
1307
|
+
frame = enclosing_completion_frame(facts, line)
|
|
1308
|
+
return nil unless frame
|
|
1309
|
+
|
|
1310
|
+
Array(frame.snapshots).each do |snapshot|
|
|
1311
|
+
next if snapshot.line < line
|
|
1312
|
+
next if snapshot.line == line && snapshot.column <= char
|
|
1313
|
+
|
|
1314
|
+
binding = snapshot.bindings[name]
|
|
1315
|
+
return binding if binding
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
nil
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
def resolve_local_hover_type(facts, name, line, char)
|
|
1322
|
+
resolve_local_hover_binding(facts, name, line, char)&.type
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
def declared_generic_local_hover_binding(facts, name, line)
|
|
1326
|
+
binding = generic_function_binding_for_line(facts, line)
|
|
1327
|
+
return nil unless binding
|
|
1328
|
+
|
|
1329
|
+
binding.body_params.find { |param| param.name == name }
|
|
1330
|
+
end
|
|
1331
|
+
|
|
1332
|
+
def enclosing_completion_frame(facts, line)
|
|
1333
|
+
frames = Array(facts.local_completion_frames)
|
|
1334
|
+
containing = frames.select { |frame| frame.start_line && frame.end_line && frame.start_line <= line && line <= frame.end_line }
|
|
1335
|
+
containing.min_by { |frame| frame.end_line - frame.start_line }
|
|
1336
|
+
end
|
|
1337
|
+
|
|
1338
|
+
def latest_completion_snapshot(frame, line, char)
|
|
1339
|
+
snapshots = Array(frame.snapshots)
|
|
1340
|
+
snapshots.reverse_each do |snapshot|
|
|
1341
|
+
next if snapshot.line > line
|
|
1342
|
+
next if snapshot.line == line && snapshot.column > char
|
|
1343
|
+
|
|
1344
|
+
return snapshot
|
|
1345
|
+
end
|
|
1346
|
+
nil
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
def same_line_future_completion_snapshot(frame, line, char)
|
|
1350
|
+
snapshots = Array(frame.snapshots)
|
|
1351
|
+
snapshots.each do |snapshot|
|
|
1352
|
+
next unless snapshot.line == line
|
|
1353
|
+
next if snapshot.column <= char
|
|
1354
|
+
|
|
1355
|
+
return snapshot
|
|
1356
|
+
end
|
|
1357
|
+
nil
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
def resolve_for_binding_hover_info(tokens, token_index)
|
|
1361
|
+
tok = tokens[token_index]
|
|
1362
|
+
return nil unless tok&.type == :identifier
|
|
1363
|
+
|
|
1364
|
+
binding_info = for_binding_context_at(tokens, token_index)
|
|
1365
|
+
return nil unless binding_info
|
|
1366
|
+
|
|
1367
|
+
binding_name = binding_info[:name]
|
|
1368
|
+
iterable_text = binding_info[:iterable_text] || "collection"
|
|
1369
|
+
|
|
1370
|
+
{
|
|
1371
|
+
signature: "for binding #{binding_name} (iterating over #{iterable_text})",
|
|
1372
|
+
docs: nil,
|
|
1373
|
+
}
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
def for_binding_context_at(tokens, token_index)
|
|
1377
|
+
return nil unless token_index
|
|
1378
|
+
|
|
1379
|
+
i = token_index - 1
|
|
1380
|
+
passed_in = false
|
|
1381
|
+
|
|
1382
|
+
while i >= 0
|
|
1383
|
+
current = tokens[i]
|
|
1384
|
+
break if current.type == :newline && current.line != tokens[token_index].line
|
|
1385
|
+
|
|
1386
|
+
if current.type == :identifier
|
|
1387
|
+
case current.lexeme
|
|
1388
|
+
when "in"
|
|
1389
|
+
passed_in = true
|
|
1390
|
+
when "for"
|
|
1391
|
+
if passed_in
|
|
1392
|
+
ie = token_index + 1
|
|
1393
|
+
while ie < tokens.length
|
|
1394
|
+
nt = tokens[ie]
|
|
1395
|
+
break if nt.type == :newline || nt.type == :colon
|
|
1396
|
+
|
|
1397
|
+
ie += 1
|
|
1398
|
+
end
|
|
1399
|
+
iterable_text = tokens[(token_index + 1)...ie].map(&:lexeme).join(" ")
|
|
1400
|
+
iterable_text = iterable_text.gsub(/\s+/, " ").strip
|
|
1401
|
+
return {
|
|
1402
|
+
name: tokens[token_index].lexeme,
|
|
1403
|
+
iterable_text: iterable_text.empty? ? nil : iterable_text,
|
|
1404
|
+
}
|
|
1405
|
+
end
|
|
1406
|
+
end
|
|
1407
|
+
end
|
|
1408
|
+
|
|
1409
|
+
i -= 1
|
|
1410
|
+
end
|
|
1411
|
+
|
|
1412
|
+
nil
|
|
1413
|
+
end
|
|
1414
|
+
|
|
1415
|
+
def resolve_lexical_local_hover_signature(definition_uri, name, definition_token)
|
|
1416
|
+
kind = nil
|
|
1417
|
+
type_str = nil
|
|
1418
|
+
|
|
1419
|
+
tokens = @workspace.get_tokens(definition_uri)
|
|
1420
|
+
if tokens
|
|
1421
|
+
def_index = tokens.index(definition_token)
|
|
1422
|
+
if def_index
|
|
1423
|
+
prev = previous_non_trivia_token_index(tokens, def_index)
|
|
1424
|
+
if prev && tokens[prev].type == :identifier
|
|
1425
|
+
case tokens[prev].lexeme
|
|
1426
|
+
when "let" then kind = :let
|
|
1427
|
+
when "var" then kind = :var
|
|
1428
|
+
when "const" then kind = :const
|
|
1429
|
+
end
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1432
|
+
if kind
|
|
1433
|
+
next_idx = next_non_trivia_token_index(tokens, def_index + 1)
|
|
1434
|
+
if next_idx && tokens[next_idx].type == :colon
|
|
1435
|
+
type_start = next_non_trivia_token_index(tokens, next_idx + 1)
|
|
1436
|
+
if type_start && tokens[type_start].type == :identifier
|
|
1437
|
+
type_str = tokens[type_start].lexeme
|
|
1438
|
+
type_end = type_start
|
|
1439
|
+
loop do
|
|
1440
|
+
next_tok_idx = next_non_trivia_token_index(tokens, type_end + 1)
|
|
1441
|
+
break unless next_tok_idx && %i[identifier lbracket rbracket integer comma].include?(tokens[next_tok_idx].type)
|
|
1442
|
+
|
|
1443
|
+
type_str += tokens[next_tok_idx].lexeme
|
|
1444
|
+
type_end = next_tok_idx
|
|
1445
|
+
break if tokens[type_end].type == :equal
|
|
1446
|
+
end
|
|
1447
|
+
type_str = type_str.sub(/\s*=.*/, "").strip
|
|
1448
|
+
end
|
|
1449
|
+
end
|
|
1450
|
+
end
|
|
1451
|
+
end
|
|
1452
|
+
end
|
|
1453
|
+
|
|
1454
|
+
kind ||= :let
|
|
1455
|
+
mutability = kind == :var ? "mutable" : "immutable"
|
|
1456
|
+
if type_str && !type_str.empty?
|
|
1457
|
+
"#{kind} #{name}: #{type_str} (#{mutability})"
|
|
1458
|
+
else
|
|
1459
|
+
"#{kind} #{name} (#{mutability})"
|
|
1460
|
+
end
|
|
1461
|
+
end
|
|
1462
|
+
end
|
|
1463
|
+
end
|
|
1464
|
+
end
|
|
1465
|
+
end
|