mt-lang 0.3.8 → 0.3.10
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 +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- metadata +2 -2
|
@@ -18,8 +18,6 @@ module MilkTea
|
|
|
18
18
|
ptr ref span array dyn Option Result Task SoA str_buffer const_ptr simd
|
|
19
19
|
].freeze
|
|
20
20
|
|
|
21
|
-
private
|
|
22
|
-
|
|
23
21
|
def completion_data(name)
|
|
24
22
|
{ uri: @current_completion_uri || '', name: }
|
|
25
23
|
end
|
|
@@ -39,1082 +37,1082 @@ module MilkTea
|
|
|
39
37
|
result.empty? ? nil : result
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
|
|
62
|
-
|
|
63
|
-
unless facts
|
|
64
|
-
branch = 'no-facts'
|
|
65
|
-
return { isIncomplete: false, items: [] }
|
|
66
|
-
end
|
|
40
|
+
def handle_completion(params)
|
|
41
|
+
stages = new_perf_stages
|
|
42
|
+
total_start = stages ? monotonic_time : nil
|
|
43
|
+
uri = params['textDocument']['uri']
|
|
44
|
+
@current_completion_uri = uri
|
|
45
|
+
lsp_line = params['position']['line']
|
|
46
|
+
lsp_char = params['position']['character']
|
|
47
|
+
branch = 'none'
|
|
48
|
+
item_count = 0
|
|
49
|
+
|
|
50
|
+
prefix = measure_perf_stage(stages, 'prefix') { current_word_prefix(uri, lsp_line, lsp_char) }
|
|
51
|
+
|
|
52
|
+
import_items = measure_perf_stage(stages, 'import_context') { import_completions(uri, lsp_line, lsp_char) }
|
|
53
|
+
if import_items
|
|
54
|
+
branch = 'import'
|
|
55
|
+
item_count = import_items.length
|
|
56
|
+
return { isIncomplete: false, items: import_items }
|
|
57
|
+
end
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
attr_items = attribute_completions(facts, uri, lsp_line, lsp_char)
|
|
70
|
-
if attr_items
|
|
71
|
-
branch = 'attribute'
|
|
72
|
-
item_count = attr_items.length
|
|
73
|
-
return { isIncomplete: false, items: attr_items }
|
|
74
|
-
end
|
|
59
|
+
facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
|
|
75
60
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
item_count = fmt_items.length
|
|
81
|
-
return { isIncomplete: false, items: fmt_items }
|
|
82
|
-
end
|
|
61
|
+
unless facts
|
|
62
|
+
branch = 'no-facts'
|
|
63
|
+
return { isIncomplete: false, items: [] }
|
|
64
|
+
end
|
|
83
65
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
66
|
+
# Attribute context: complete inside @[...]
|
|
67
|
+
attr_items = attribute_completions(facts, uri, lsp_line, lsp_char)
|
|
68
|
+
if attr_items
|
|
69
|
+
branch = 'attribute'
|
|
70
|
+
item_count = attr_items.length
|
|
71
|
+
return { isIncomplete: false, items: attr_items }
|
|
72
|
+
end
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
74
|
+
# Format string interpolation: complete inside f"... #{ }
|
|
75
|
+
fmt_items = format_string_completions(facts, uri, lsp_line, lsp_char)
|
|
76
|
+
if fmt_items
|
|
77
|
+
branch = 'format-string'
|
|
78
|
+
item_count = fmt_items.length
|
|
79
|
+
return { isIncomplete: false, items: fmt_items }
|
|
80
|
+
end
|
|
99
81
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
end
|
|
107
|
-
if dot_recv
|
|
108
|
-
# Module member access: rl.init_window, rl.RAYWHITE, etc.
|
|
109
|
-
if (module_binding = facts.imports[dot_recv])
|
|
110
|
-
branch = 'module'
|
|
111
|
-
items = measure_perf_stage(stages, 'build') do
|
|
112
|
-
result = []
|
|
113
|
-
module_binding.functions.each do |fname, binding|
|
|
114
|
-
next unless prefix.empty? || fname.start_with?(prefix)
|
|
115
|
-
params_str = format_params(binding.type.params)
|
|
116
|
-
item = {
|
|
117
|
-
label: fname,
|
|
118
|
-
kind: 12, # Function
|
|
119
|
-
detail: "function #{fname}(#{params_str}) -> #{binding.type.return_type}",
|
|
120
|
-
insertText: fname,
|
|
121
|
-
sortText: "0_#{fname}",
|
|
122
|
-
data: completion_data(fname),
|
|
123
|
-
}
|
|
124
|
-
if (ld = label_details_for_params(params_str, module_binding.name))
|
|
125
|
-
item[:labelDetails] = ld
|
|
126
|
-
end
|
|
127
|
-
if (snippet = snippet_for_callable(fname, binding.type.params))
|
|
128
|
-
item[:insertText] = snippet
|
|
129
|
-
item[:insertTextFormat] = 2
|
|
130
|
-
end
|
|
131
|
-
result << item
|
|
132
|
-
end
|
|
133
|
-
module_binding.values.each do |vname, binding|
|
|
134
|
-
next unless prefix.empty? || vname.start_with?(prefix)
|
|
135
|
-
result << {
|
|
136
|
-
label: vname,
|
|
137
|
-
kind: 13, # Variable
|
|
138
|
-
detail: "#{vname}: #{binding.type}",
|
|
139
|
-
insertText: vname,
|
|
140
|
-
sortText: "1_#{vname}",
|
|
141
|
-
data: completion_data(vname),
|
|
142
|
-
}
|
|
143
|
-
end
|
|
144
|
-
module_binding.types.each do |tname, _type|
|
|
145
|
-
next unless prefix.empty? || tname.start_with?(prefix)
|
|
146
|
-
result << {
|
|
147
|
-
label: tname,
|
|
148
|
-
kind: 5, # Class
|
|
149
|
-
detail: "type #{tname}",
|
|
150
|
-
insertText: tname,
|
|
151
|
-
sortText: "2_#{tname}",
|
|
152
|
-
data: completion_data(tname),
|
|
153
|
-
}
|
|
154
|
-
end
|
|
155
|
-
result
|
|
156
|
-
end
|
|
157
|
-
item_count = items.length
|
|
158
|
-
return { isIncomplete: false, items: items }
|
|
82
|
+
# Named argument completions: inside function/struct call e.g. Point(x: 1, |)
|
|
83
|
+
named_items = named_argument_completions(facts, uri, lsp_line, lsp_char)
|
|
84
|
+
if named_items
|
|
85
|
+
branch = 'named-arg'
|
|
86
|
+
item_count = named_items.length
|
|
87
|
+
return { isIncomplete: false, items: named_items }
|
|
159
88
|
end
|
|
160
|
-
if (type_receiver = measure_perf_stage(stages, 'type_receiver') { resolve_type_receiver_info(facts, dot_recv, dot_recv_path) })
|
|
161
|
-
receiver_label = type_receiver[:label]
|
|
162
|
-
type = type_receiver[:type]
|
|
163
89
|
|
|
90
|
+
# Specialization context: inside name[...]
|
|
91
|
+
spec_items = specialization_completions(facts, uri, lsp_line, lsp_char)
|
|
92
|
+
if spec_items
|
|
93
|
+
branch = 'specialization'
|
|
94
|
+
item_count = spec_items.length
|
|
95
|
+
return { isIncomplete: false, items: spec_items }
|
|
96
|
+
end
|
|
164
97
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
98
|
+
# When user is typing after '.', return module members or method completions.
|
|
99
|
+
dot_recv = nil
|
|
100
|
+
dot_recv_path = nil
|
|
101
|
+
measure_perf_stage(stages, 'receiver_context') do
|
|
102
|
+
dot_recv = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
|
|
103
|
+
dot_recv_path = @workspace.find_dot_receiver_path(uri, lsp_line, lsp_char)
|
|
104
|
+
end
|
|
105
|
+
if dot_recv
|
|
106
|
+
# Module member access: rl.init_window, rl.RAYWHITE, etc.
|
|
107
|
+
if (module_binding = facts.imports[dot_recv])
|
|
108
|
+
branch = 'module'
|
|
168
109
|
items = measure_perf_stage(stages, 'build') do
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
110
|
+
result = []
|
|
111
|
+
module_binding.functions.each do |fname, binding|
|
|
112
|
+
next unless prefix.empty? || fname.start_with?(prefix)
|
|
113
|
+
params_str = format_params(binding.type.params)
|
|
114
|
+
item = {
|
|
115
|
+
label: fname,
|
|
116
|
+
kind: 12, # Function
|
|
117
|
+
detail: "function #{fname}(#{params_str}) -> #{binding.type.return_type}",
|
|
118
|
+
insertText: fname,
|
|
119
|
+
sortText: "0_#{fname}",
|
|
120
|
+
data: completion_data(fname),
|
|
121
|
+
}
|
|
122
|
+
if (ld = label_details_for_params(params_str, module_binding.name))
|
|
123
|
+
item[:labelDetails] = ld
|
|
124
|
+
end
|
|
125
|
+
if (snippet = snippet_for_callable(fname, binding.type.params))
|
|
126
|
+
item[:insertText] = snippet
|
|
127
|
+
item[:insertTextFormat] = 2
|
|
128
|
+
end
|
|
129
|
+
result << item
|
|
130
|
+
end
|
|
131
|
+
module_binding.values.each do |vname, binding|
|
|
132
|
+
next unless prefix.empty? || vname.start_with?(prefix)
|
|
133
|
+
result << {
|
|
134
|
+
label: vname,
|
|
135
|
+
kind: 13, # Variable
|
|
136
|
+
detail: "#{vname}: #{binding.type}",
|
|
137
|
+
insertText: vname,
|
|
138
|
+
sortText: "1_#{vname}",
|
|
139
|
+
data: completion_data(vname),
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
module_binding.types.each do |tname, _type|
|
|
143
|
+
next unless prefix.empty? || tname.start_with?(prefix)
|
|
144
|
+
result << {
|
|
145
|
+
label: tname,
|
|
146
|
+
kind: 5, # Class
|
|
147
|
+
detail: "type #{tname}",
|
|
148
|
+
insertText: tname,
|
|
149
|
+
sortText: "2_#{tname}",
|
|
150
|
+
data: completion_data(tname),
|
|
178
151
|
}
|
|
179
152
|
end
|
|
153
|
+
result
|
|
180
154
|
end
|
|
181
155
|
item_count = items.length
|
|
182
156
|
return { isIncomplete: false, items: items }
|
|
183
157
|
end
|
|
158
|
+
if (type_receiver = measure_perf_stage(stages, 'type_receiver') { resolve_type_receiver_info(facts, dot_recv, dot_recv_path) })
|
|
159
|
+
receiver_label = type_receiver[:label]
|
|
160
|
+
type = type_receiver[:type]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Enum/Flags member access: Color.RED, KeyboardKey.A, etc.
|
|
164
|
+
if type.is_a?(Types::EnumBase)
|
|
165
|
+
branch = 'enum-members'
|
|
166
|
+
items = measure_perf_stage(stages, 'build') do
|
|
167
|
+
type.members.filter_map do |mname|
|
|
168
|
+
next if !prefix.empty? && !mname.start_with?(prefix)
|
|
169
|
+
{
|
|
170
|
+
label: mname,
|
|
171
|
+
kind: 22, # EnumMember
|
|
172
|
+
detail: "#{receiver_label}.#{mname}",
|
|
173
|
+
insertText: mname,
|
|
174
|
+
sortText: "0_#{mname}",
|
|
175
|
+
data: completion_data(mname),
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
item_count = items.length
|
|
180
|
+
return { isIncomplete: false, items: items }
|
|
181
|
+
end
|
|
184
182
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
183
|
+
# Variant arm access: Option.none, Result.success, etc.
|
|
184
|
+
if type.is_a?(Types::Variant)
|
|
185
|
+
branch = 'variant-arms'
|
|
186
|
+
items = measure_perf_stage(stages, 'build') do
|
|
187
|
+
type.arm_names.filter_map do |aname|
|
|
188
|
+
next if !prefix.empty? && !aname.start_with?(prefix)
|
|
189
|
+
{
|
|
190
|
+
label: aname,
|
|
191
|
+
kind: 22, # EnumMember
|
|
192
|
+
detail: "#{receiver_label}.#{aname}",
|
|
193
|
+
insertText: aname,
|
|
194
|
+
sortText: "0_#{aname}",
|
|
195
|
+
data: completion_data(aname),
|
|
196
|
+
}
|
|
197
|
+
end
|
|
199
198
|
end
|
|
199
|
+
item_count = items.length
|
|
200
|
+
return { isIncomplete: false, items: items }
|
|
200
201
|
end
|
|
201
|
-
item_count = items.length
|
|
202
|
-
return { isIncomplete: false, items: items }
|
|
203
|
-
end
|
|
204
202
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
# Nested struct type members: ShapeGroup.CircleData, etc.
|
|
204
|
+
if type.is_a?(Types::Struct) && type.respond_to?(:nested_types) && type.nested_types.any?
|
|
205
|
+
branch = 'nested-types'
|
|
206
|
+
items = measure_perf_stage(stages, 'build') do
|
|
207
|
+
type.nested_types.filter_map do |nt_name, _nt_type|
|
|
208
|
+
next if !prefix.empty? && !nt_name.start_with?(prefix)
|
|
209
|
+
{
|
|
210
|
+
label: nt_name,
|
|
211
|
+
kind: 5, # Class/type
|
|
212
|
+
detail: "#{receiver_label}.#{nt_name}",
|
|
213
|
+
insertText: nt_name,
|
|
214
|
+
sortText: "1_#{nt_name}",
|
|
215
|
+
data: completion_data(nt_name),
|
|
216
|
+
}
|
|
217
|
+
end
|
|
219
218
|
end
|
|
219
|
+
item_count = items.length
|
|
220
|
+
return { isIncomplete: false, items: items } unless items.empty?
|
|
220
221
|
end
|
|
221
|
-
item_count = items.length
|
|
222
|
-
return { isIncomplete: false, items: items } unless items.empty?
|
|
223
|
-
end
|
|
224
222
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
items = measure_perf_stage(stages, 'build') { completion_items_for_type_receiver(facts, type, prefix) }
|
|
224
|
+
unless items.empty?
|
|
225
|
+
branch = 'type-receiver'
|
|
226
|
+
item_count = items.length
|
|
227
|
+
return { isIncomplete: false, items: items }
|
|
228
|
+
end
|
|
230
229
|
end
|
|
231
|
-
end
|
|
232
230
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
231
|
+
if dot_recv_path && dot_recv_path.include?('.')
|
|
232
|
+
segments = dot_recv_path.split('.', 2)
|
|
233
|
+
mod_alias = segments.first
|
|
234
|
+
value_name = segments[1]
|
|
235
|
+
if mod_alias && value_name && (mod_binding = facts.imports[mod_alias])
|
|
236
|
+
if (val_binding = mod_binding.values[value_name])
|
|
237
|
+
receiver_type = measure_perf_stage(stages, 'imported_value_receiver') { val_binding.type }
|
|
238
|
+
items = measure_perf_stage(stages, 'build') { completion_items_for_value_receiver(facts, receiver_type, prefix) }
|
|
239
|
+
unless items.empty?
|
|
240
|
+
branch = 'imported-value-receiver'
|
|
241
|
+
item_count = items.length
|
|
242
|
+
return { isIncomplete: false, items: items }
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
else
|
|
246
|
+
chain_items = measure_perf_stage(stages, 'value_chain') { value_chain_completions(facts, dot_recv_path, lsp_line, lsp_char, prefix) }
|
|
247
|
+
if chain_items
|
|
248
|
+
branch = 'value-chain'
|
|
249
|
+
item_count = chain_items.length
|
|
250
|
+
return { isIncomplete: false, items: chain_items }
|
|
245
251
|
end
|
|
246
|
-
end
|
|
247
|
-
else
|
|
248
|
-
chain_items = measure_perf_stage(stages, 'value_chain') { value_chain_completions(facts, dot_recv_path, lsp_line, lsp_char, prefix) }
|
|
249
|
-
if chain_items
|
|
250
|
-
branch = 'value-chain'
|
|
251
|
-
item_count = chain_items.length
|
|
252
|
-
return { isIncomplete: false, items: chain_items }
|
|
253
252
|
end
|
|
254
253
|
end
|
|
255
|
-
end
|
|
256
254
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
255
|
+
if (receiver_type = measure_perf_stage(stages, 'value_receiver') { resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1) })
|
|
256
|
+
items = measure_perf_stage(stages, 'build') { completion_items_for_value_receiver(facts, receiver_type, prefix) }
|
|
257
|
+
unless items.empty?
|
|
258
|
+
branch = 'value-receiver'
|
|
259
|
+
item_count = items.length
|
|
260
|
+
return { isIncomplete: false, items: items }
|
|
261
|
+
end
|
|
263
262
|
end
|
|
264
|
-
end
|
|
265
263
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
264
|
+
# Method completions on a non-module receiver.
|
|
265
|
+
branch = 'method-fallback'
|
|
266
|
+
method_items = measure_perf_stage(stages, 'build') do
|
|
267
|
+
result = []
|
|
268
|
+
facts.methods.each do |_recv_type, methods|
|
|
269
|
+
methods.each do |mname, binding|
|
|
270
|
+
next unless prefix.empty? || mname.start_with?(prefix)
|
|
271
|
+
|
|
272
|
+
params_str = format_params(binding.type.params)
|
|
273
|
+
item = {
|
|
274
|
+
label: mname,
|
|
275
|
+
kind: 6, # Method
|
|
276
|
+
detail: "#{mname}(#{params_str}) -> #{binding.type.return_type}",
|
|
277
|
+
insertText: mname,
|
|
278
|
+
sortText: "0_#{mname}",
|
|
279
|
+
data: completion_data(mname),
|
|
280
|
+
}
|
|
281
|
+
if (ld = label_details_for_params(params_str, nil))
|
|
282
|
+
item[:labelDetails] = ld
|
|
283
|
+
end
|
|
284
|
+
if (snippet = snippet_for_callable(mname, binding.type.params))
|
|
285
|
+
item[:insertText] = snippet
|
|
286
|
+
item[:insertTextFormat] = 2
|
|
287
|
+
end
|
|
288
|
+
result << item
|
|
289
289
|
end
|
|
290
|
-
result << item
|
|
291
290
|
end
|
|
291
|
+
result
|
|
292
292
|
end
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
item_count = method_items.length
|
|
294
|
+
truncated = item_count > MAX_COMPLETION_ITEMS
|
|
295
|
+
if truncated
|
|
296
|
+
method_items = method_items.first(MAX_COMPLETION_ITEMS)
|
|
297
|
+
item_count = MAX_COMPLETION_ITEMS
|
|
298
|
+
end
|
|
299
|
+
return { isIncomplete: truncated, items: method_items }
|
|
300
300
|
end
|
|
301
|
-
return { isIncomplete: truncated, items: method_items }
|
|
302
|
-
end
|
|
303
301
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
302
|
+
branch = 'global'
|
|
303
|
+
items = measure_perf_stage(stages, 'build') do
|
|
304
|
+
result = []
|
|
305
|
+
function_docs_cache = {}
|
|
308
306
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
307
|
+
# Functions
|
|
308
|
+
facts.functions.each do |name, binding|
|
|
309
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
312
310
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
311
|
+
params_str = format_params(binding.type.params)
|
|
312
|
+
entry = {
|
|
313
|
+
label: name,
|
|
314
|
+
kind: 12, # Function
|
|
315
|
+
detail: "function #{name}(#{params_str}) -> #{binding.type.return_type}",
|
|
316
|
+
insertText: name,
|
|
317
|
+
sortText: "0_#{name}",
|
|
318
|
+
data: completion_data(name),
|
|
319
|
+
}
|
|
322
320
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
if (ld = label_details_for_params(params_str, nil))
|
|
322
|
+
entry[:labelDetails] = ld
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
if (snippet = snippet_for_callable(name, binding.type.params))
|
|
326
|
+
entry[:insertText] = snippet
|
|
327
|
+
entry[:insertTextFormat] = 2
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
docs = completion_function_documentation(uri, name, cache: function_docs_cache)
|
|
331
|
+
unless docs.empty?
|
|
332
|
+
entry[:documentation] = {
|
|
333
|
+
kind: 'markdown',
|
|
334
|
+
value: docs,
|
|
335
|
+
}
|
|
336
|
+
end
|
|
326
337
|
|
|
327
|
-
|
|
328
|
-
entry[:insertText] = snippet
|
|
329
|
-
entry[:insertTextFormat] = 2
|
|
338
|
+
result << entry
|
|
330
339
|
end
|
|
331
340
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
341
|
+
# Types
|
|
342
|
+
builtin_names = SemanticAnalyzer::INSTALLABLE_BUILTIN_TYPE_NAMES
|
|
343
|
+
facts.types.each do |name, type|
|
|
344
|
+
next if builtin_names.include?(name)
|
|
345
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
346
|
+
|
|
347
|
+
kind = case type
|
|
348
|
+
when Types::StructInstance then 22 # Struct
|
|
349
|
+
when Types::EnumBase, Types::Variant then 13 # Enum
|
|
350
|
+
else 7 # Class/type
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
result << {
|
|
354
|
+
label: name,
|
|
355
|
+
kind: kind,
|
|
356
|
+
detail: "type #{name}",
|
|
357
|
+
insertText: name,
|
|
358
|
+
sortText: "1_#{name}",
|
|
359
|
+
data: completion_data(name),
|
|
337
360
|
}
|
|
338
361
|
end
|
|
339
362
|
|
|
340
|
-
|
|
341
|
-
|
|
363
|
+
# Imported modules
|
|
364
|
+
facts.imports.each do |name, module_binding|
|
|
365
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
342
366
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
367
|
+
result << {
|
|
368
|
+
label: name,
|
|
369
|
+
kind: 2, # Module
|
|
370
|
+
detail: "module #{module_binding.name}",
|
|
371
|
+
insertText: name,
|
|
372
|
+
sortText: "2_#{name}",
|
|
373
|
+
data: completion_data(name),
|
|
374
|
+
}
|
|
375
|
+
end
|
|
348
376
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
else 7 # Class/type
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
result << {
|
|
356
|
-
label: name,
|
|
357
|
-
kind: kind,
|
|
358
|
-
detail: "type #{name}",
|
|
359
|
-
insertText: name,
|
|
360
|
-
sortText: "1_#{name}",
|
|
361
|
-
data: completion_data(name),
|
|
362
|
-
}
|
|
363
|
-
end
|
|
377
|
+
# Values
|
|
378
|
+
facts.values.each do |name, binding|
|
|
379
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
364
380
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
381
|
+
result << {
|
|
382
|
+
label: name,
|
|
383
|
+
kind: 13, # Variable
|
|
384
|
+
detail: "#{name}: #{binding.type}",
|
|
385
|
+
insertText: name,
|
|
386
|
+
sortText: "3_#{name}",
|
|
387
|
+
data: completion_data(name),
|
|
388
|
+
}
|
|
389
|
+
end
|
|
368
390
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
391
|
+
# Locals (variables and parameters visible in current scope)
|
|
392
|
+
frame = enclosing_completion_frame(facts, lsp_line + 1)
|
|
393
|
+
if frame
|
|
394
|
+
snapshot = latest_completion_snapshot(frame, lsp_line + 1, lsp_char + 1)
|
|
395
|
+
if snapshot
|
|
396
|
+
snapshot.bindings.each do |name, binding|
|
|
397
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
398
|
+
next unless binding.respond_to?(:name) && binding.respond_to?(:type)
|
|
399
|
+
|
|
400
|
+
detail_label = case binding.respond_to?(:kind) && binding.kind
|
|
401
|
+
when :param then "parameter #{name}: #{binding.type}"
|
|
402
|
+
when :local then "local #{name}: #{binding.type}"
|
|
403
|
+
else "#{name}: #{binding.type}"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
result << {
|
|
407
|
+
label: name,
|
|
408
|
+
kind: 13, # Variable
|
|
409
|
+
detail: detail_label,
|
|
410
|
+
insertText: name,
|
|
411
|
+
sortText: "3_#{name}",
|
|
412
|
+
data: completion_data(name),
|
|
413
|
+
}
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
378
417
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
418
|
+
TYPE_CONSTRUCTOR_KEYWORDS.each do |tc|
|
|
419
|
+
next unless prefix.empty? || tc.start_with?(prefix)
|
|
382
420
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
421
|
+
result << {
|
|
422
|
+
label: tc,
|
|
423
|
+
kind: 14, # Keyword
|
|
424
|
+
detail: "type constructor #{tc}",
|
|
425
|
+
insertText: tc,
|
|
426
|
+
sortText: "8_#{tc}",
|
|
427
|
+
data: completion_data(tc),
|
|
428
|
+
}
|
|
429
|
+
end
|
|
392
430
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if frame
|
|
396
|
-
snapshot = latest_completion_snapshot(frame, lsp_line + 1, lsp_char + 1)
|
|
397
|
-
if snapshot
|
|
398
|
-
snapshot.bindings.each do |name, binding|
|
|
399
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
400
|
-
next unless binding.respond_to?(:name) && binding.respond_to?(:type)
|
|
431
|
+
COMPLETION_KEYWORDS.each do |kw|
|
|
432
|
+
next unless prefix.empty? || kw.start_with?(prefix)
|
|
401
433
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
kind: 13, # Variable
|
|
411
|
-
detail: detail_label,
|
|
412
|
-
insertText: name,
|
|
413
|
-
sortText: "3_#{name}",
|
|
414
|
-
data: completion_data(name),
|
|
415
|
-
}
|
|
416
|
-
end
|
|
434
|
+
result << {
|
|
435
|
+
label: kw,
|
|
436
|
+
kind: 14, # Keyword
|
|
437
|
+
detail: "keyword #{kw}",
|
|
438
|
+
insertText: kw,
|
|
439
|
+
sortText: "9_#{kw}",
|
|
440
|
+
data: completion_data(kw),
|
|
441
|
+
}
|
|
417
442
|
end
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
TYPE_CONSTRUCTOR_KEYWORDS.each do |tc|
|
|
421
|
-
next unless prefix.empty? || tc.start_with?(prefix)
|
|
422
443
|
|
|
423
|
-
result
|
|
424
|
-
label: tc,
|
|
425
|
-
kind: 14, # Keyword
|
|
426
|
-
detail: "type constructor #{tc}",
|
|
427
|
-
insertText: tc,
|
|
428
|
-
sortText: "8_#{tc}",
|
|
429
|
-
data: completion_data(tc),
|
|
430
|
-
}
|
|
444
|
+
result
|
|
431
445
|
end
|
|
432
446
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
kind: 14, # Keyword
|
|
439
|
-
detail: "keyword #{kw}",
|
|
440
|
-
insertText: kw,
|
|
441
|
-
sortText: "9_#{kw}",
|
|
442
|
-
data: completion_data(kw),
|
|
443
|
-
}
|
|
447
|
+
item_count = items.length
|
|
448
|
+
truncated = item_count > MAX_COMPLETION_ITEMS
|
|
449
|
+
if truncated
|
|
450
|
+
items = items.first(MAX_COMPLETION_ITEMS)
|
|
451
|
+
item_count = MAX_COMPLETION_ITEMS
|
|
444
452
|
end
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
items = items.first(MAX_COMPLETION_ITEMS)
|
|
453
|
-
item_count = MAX_COMPLETION_ITEMS
|
|
453
|
+
{ isIncomplete: truncated, items: items }
|
|
454
|
+
rescue StandardError => e
|
|
455
|
+
branch = 'error'
|
|
456
|
+
warn "Error in completion handler: #{e.message}"
|
|
457
|
+
{ isIncomplete: false, items: [] }
|
|
458
|
+
ensure
|
|
459
|
+
log_request_stage_breakdown('textDocument/completion', total_start, uri: uri, stages: stages, summary: "branch=#{branch} items=#{item_count}")
|
|
454
460
|
end
|
|
455
|
-
{ isIncomplete: truncated, items: items }
|
|
456
|
-
rescue StandardError => e
|
|
457
|
-
branch = 'error'
|
|
458
|
-
warn "Error in completion handler: #{e.message}"
|
|
459
|
-
{ isIncomplete: false, items: [] }
|
|
460
|
-
ensure
|
|
461
|
-
log_request_stage_breakdown('textDocument/completion', total_start, uri: uri, stages: stages, summary: "branch=#{branch} items=#{item_count}")
|
|
462
|
-
end
|
|
463
461
|
|
|
464
|
-
|
|
465
|
-
|
|
462
|
+
def value_chain_completions(facts, dot_recv_path, lsp_line, lsp_char, prefix)
|
|
463
|
+
return nil unless dot_recv_path&.include?('.')
|
|
466
464
|
|
|
467
|
-
|
|
468
|
-
|
|
465
|
+
chain_segments = dot_recv_path.split('.')
|
|
466
|
+
return nil if chain_segments.length < 2
|
|
469
467
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
468
|
+
first_seg = chain_segments.first
|
|
469
|
+
first_type = resolve_dot_receiver_value_type(facts, first_seg, lsp_line + 1, lsp_char + 1)
|
|
470
|
+
return nil unless first_type
|
|
473
471
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
472
|
+
current_type = first_type
|
|
473
|
+
chain_segments[1..].each do |seg|
|
|
474
|
+
field_owner = project_field_receiver_type_for_completion(current_type, facts)
|
|
475
|
+
if field_owner.respond_to?(:field) && (field_type = field_owner.field(seg))
|
|
476
|
+
current_type = field_type
|
|
477
|
+
else
|
|
478
|
+
return nil
|
|
479
|
+
end
|
|
481
480
|
end
|
|
481
|
+
|
|
482
|
+
items = completion_items_for_value_receiver(facts, current_type, prefix)
|
|
483
|
+
items.empty? ? nil : items
|
|
482
484
|
end
|
|
483
485
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
486
|
+
def completion_items_for_type_receiver(facts, receiver_type, prefix)
|
|
487
|
+
methods_for_receiver_type(facts, receiver_type).filter_map do |mname, binding|
|
|
488
|
+
next unless binding.ast.is_a?(AST::MethodDef) && binding.ast.kind == :static
|
|
487
489
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
item[:insertText] = snippet
|
|
509
|
-
item[:insertTextFormat] = 2
|
|
490
|
+
display_name = mname.delete_prefix("static:")
|
|
491
|
+
next unless prefix.empty? || display_name.start_with?(prefix)
|
|
492
|
+
|
|
493
|
+
params_str = format_params(binding.type.params)
|
|
494
|
+
item = {
|
|
495
|
+
label: display_name,
|
|
496
|
+
kind: 6, # Method
|
|
497
|
+
detail: "#{display_name}(#{params_str}) -> #{binding.type.return_type}",
|
|
498
|
+
insertText: display_name,
|
|
499
|
+
sortText: "0_#{display_name}",
|
|
500
|
+
data: completion_data(display_name),
|
|
501
|
+
}
|
|
502
|
+
if (ld = label_details_for_params(params_str, nil))
|
|
503
|
+
item[:labelDetails] = ld
|
|
504
|
+
end
|
|
505
|
+
if (snippet = snippet_for_callable(display_name, binding.type.params))
|
|
506
|
+
item[:insertText] = snippet
|
|
507
|
+
item[:insertTextFormat] = 2
|
|
508
|
+
end
|
|
509
|
+
item
|
|
510
510
|
end
|
|
511
|
-
item
|
|
512
511
|
end
|
|
513
|
-
end
|
|
514
512
|
|
|
515
|
-
|
|
516
|
-
|
|
513
|
+
def resolve_nested_type_binding(facts, name)
|
|
514
|
+
return { qualified_name: nil, type: nil } unless facts
|
|
517
515
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
516
|
+
facts.types.each_value do |type|
|
|
517
|
+
next unless type.respond_to?(:nested_types)
|
|
518
|
+
if (nested = type.nested_types[name])
|
|
519
|
+
return { qualified_name: "#{type.name}.#{name}", type: nested }
|
|
520
|
+
end
|
|
522
521
|
end
|
|
522
|
+
{ qualified_name: nil, type: nil }
|
|
523
523
|
end
|
|
524
|
-
{ qualified_name: nil, type: nil }
|
|
525
|
-
end
|
|
526
524
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
525
|
+
def resolve_type_receiver_info(facts, receiver_name, receiver_path)
|
|
526
|
+
if facts.types.key?(receiver_name)
|
|
527
|
+
type = facts.types.fetch(receiver_name)
|
|
528
|
+
module_name = type.respond_to?(:module_name) ? type.module_name : facts.module_name
|
|
529
|
+
return { label: receiver_name, type:, module_name: }
|
|
530
|
+
end
|
|
533
531
|
|
|
534
|
-
|
|
535
|
-
|
|
532
|
+
nested = resolve_nested_type_binding(facts, receiver_name)
|
|
533
|
+
return { label: nested[:qualified_name], type: nested[:type], module_name: nested[:type].module_name } if nested[:type]
|
|
536
534
|
|
|
537
|
-
|
|
535
|
+
return nil unless receiver_path&.include?('.')
|
|
538
536
|
|
|
539
|
-
|
|
540
|
-
|
|
537
|
+
module_alias, type_name = receiver_path.split('.', 2)
|
|
538
|
+
return nil unless module_alias && type_name
|
|
541
539
|
|
|
542
|
-
|
|
543
|
-
|
|
540
|
+
module_binding = facts.imports[module_alias]
|
|
541
|
+
return nil unless module_binding
|
|
544
542
|
|
|
545
|
-
|
|
546
|
-
|
|
543
|
+
type = module_binding.types[type_name]
|
|
544
|
+
return nil unless type
|
|
547
545
|
|
|
548
|
-
|
|
549
|
-
|
|
546
|
+
{ label: receiver_path, type:, module_name: module_binding.name }
|
|
547
|
+
end
|
|
550
548
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
549
|
+
def resolve_static_type_receiver_method(facts, receiver_name, receiver_path, method_name)
|
|
550
|
+
receiver_info = resolve_type_receiver_info(facts, receiver_name, receiver_path)
|
|
551
|
+
return nil unless receiver_info
|
|
554
552
|
|
|
555
|
-
|
|
556
|
-
|
|
553
|
+
binding = static_method_binding_for_receiver(facts, receiver_info[:type], method_name)
|
|
554
|
+
return nil unless binding
|
|
557
555
|
|
|
558
|
-
|
|
559
|
-
|
|
556
|
+
receiver_info.merge(binding:)
|
|
557
|
+
end
|
|
560
558
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
559
|
+
def static_method_binding_for_receiver(facts, receiver_type, method_name)
|
|
560
|
+
methods = methods_for_receiver_type(facts, receiver_type)
|
|
561
|
+
binding = methods[method_name] || methods["static:#{method_name}"]
|
|
562
|
+
return nil unless binding&.ast.is_a?(AST::MethodDef) && binding.ast.kind == :static
|
|
565
563
|
|
|
566
|
-
|
|
567
|
-
|
|
564
|
+
binding
|
|
565
|
+
end
|
|
568
566
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
567
|
+
def resolve_static_type_reference_target(uri, token, facts)
|
|
568
|
+
token_end_char = token.column - 1 + token.lexeme.length
|
|
569
|
+
receiver_name = @workspace.find_dot_receiver(uri, token.line - 1, token_end_char)
|
|
570
|
+
receiver_path = @workspace.find_dot_receiver_path(uri, token.line - 1, token_end_char)
|
|
573
571
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
572
|
+
if (target = resolve_static_type_receiver_method(facts, receiver_name, receiver_path, token.lexeme))
|
|
573
|
+
location = module_member_binding_location(uri, target[:module_name], token.lexeme, target[:binding])
|
|
574
|
+
location ||= module_member_definition_location(uri, target[:module_name], token.lexeme)
|
|
575
|
+
return target.merge(location:)
|
|
576
|
+
end
|
|
579
577
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
578
|
+
binding = static_method_binding_at_token(facts, token)
|
|
579
|
+
return nil unless binding
|
|
580
|
+
|
|
581
|
+
location = module_member_binding_location(uri, facts.module_name, token.lexeme, binding)
|
|
582
|
+
location ||= module_member_definition_location(uri, facts.module_name, token.lexeme)
|
|
583
|
+
{
|
|
584
|
+
label: token.lexeme,
|
|
585
|
+
type: binding.declared_receiver_type,
|
|
586
|
+
module_name: facts.module_name,
|
|
587
|
+
binding:,
|
|
588
|
+
location:,
|
|
589
|
+
}
|
|
590
|
+
end
|
|
593
591
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
592
|
+
def static_method_binding_at_token(facts, token)
|
|
593
|
+
binding = method_binding_at_token(facts, token)
|
|
594
|
+
return nil unless binding&.ast.is_a?(AST::MethodDef) && binding.ast.kind == :static
|
|
597
595
|
|
|
598
|
-
|
|
599
|
-
|
|
596
|
+
binding
|
|
597
|
+
end
|
|
600
598
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
599
|
+
def static_type_method_references(target, include_declaration:)
|
|
600
|
+
name = target[:binding].name
|
|
601
|
+
target_uri = target[:location][:uri]
|
|
604
602
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
603
|
+
candidate_uris = nil
|
|
604
|
+
if target_uri
|
|
605
|
+
target_facts = @workspace.get_facts(target_uri)
|
|
606
|
+
if target_facts&.module_name
|
|
607
|
+
importing = @workspace.reverse_import_dependents_for(target_facts.module_name)
|
|
608
|
+
candidate_uris = importing.to_a + [target_uri] if importing && !importing.empty?
|
|
609
|
+
end
|
|
611
610
|
end
|
|
612
|
-
end
|
|
613
611
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
612
|
+
refs = if candidate_uris
|
|
613
|
+
@workspace.find_all_references_in(name, candidate_uris)
|
|
614
|
+
else
|
|
615
|
+
@workspace.find_all_references(name)
|
|
616
|
+
end
|
|
617
|
+
refs.filter do |ref|
|
|
618
|
+
if location_matches_reference?(target[:location], ref)
|
|
619
|
+
include_declaration
|
|
620
|
+
else
|
|
621
|
+
static_type_method_reference?(ref, target)
|
|
622
|
+
end
|
|
624
623
|
end
|
|
625
624
|
end
|
|
626
|
-
end
|
|
627
625
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
626
|
+
def static_type_method_reference?(ref, target)
|
|
627
|
+
token = @workspace.find_token_at(ref[:uri], ref.dig(:range, :start, :line), ref.dig(:range, :start, :character))
|
|
628
|
+
return false unless token&.type == :identifier
|
|
631
629
|
|
|
632
|
-
|
|
633
|
-
|
|
630
|
+
facts = @workspace.get_facts(ref[:uri])
|
|
631
|
+
return false unless facts
|
|
634
632
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
633
|
+
token_end_char = ref.dig(:range, :end, :character)
|
|
634
|
+
receiver_name = @workspace.find_dot_receiver(ref[:uri], ref.dig(:range, :start, :line), token_end_char)
|
|
635
|
+
receiver_path = @workspace.find_dot_receiver_path(ref[:uri], ref.dig(:range, :start, :line), token_end_char)
|
|
636
|
+
candidate = resolve_static_type_receiver_method(facts, receiver_name, receiver_path, token.lexeme)
|
|
637
|
+
return false unless candidate
|
|
640
638
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
639
|
+
candidate_location = module_member_binding_location(ref[:uri], candidate[:module_name], token.lexeme, candidate[:binding])
|
|
640
|
+
candidate_location ||= module_member_definition_location(ref[:uri], candidate[:module_name], token.lexeme)
|
|
641
|
+
same_location?(candidate_location, target[:location])
|
|
642
|
+
end
|
|
645
643
|
|
|
646
|
-
|
|
647
|
-
|
|
644
|
+
def location_matches_reference?(location, ref)
|
|
645
|
+
return false unless location
|
|
648
646
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
647
|
+
same_location?(location, {
|
|
648
|
+
uri: ref[:uri],
|
|
649
|
+
range: ref[:range]
|
|
650
|
+
})
|
|
651
|
+
end
|
|
654
652
|
|
|
655
|
-
|
|
656
|
-
|
|
653
|
+
def same_location?(left, right)
|
|
654
|
+
return false unless left && right
|
|
657
655
|
|
|
658
|
-
|
|
659
|
-
|
|
656
|
+
left[:uri] == right[:uri] && left[:range] == right[:range]
|
|
657
|
+
end
|
|
660
658
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
659
|
+
def module_member_access_info(tokens, index)
|
|
660
|
+
dot_index = previous_non_trivia_token_index(tokens, index)
|
|
661
|
+
return nil unless dot_index && tokens[dot_index].type == :dot
|
|
664
662
|
|
|
665
|
-
|
|
666
|
-
|
|
663
|
+
receiver_index = previous_non_trivia_token_index(tokens, dot_index)
|
|
664
|
+
return nil unless receiver_index
|
|
667
665
|
|
|
668
|
-
|
|
669
|
-
|
|
666
|
+
receiver = tokens[receiver_index]
|
|
667
|
+
return nil unless receiver.type == :identifier
|
|
670
668
|
|
|
671
|
-
|
|
672
|
-
|
|
669
|
+
{ receiver: receiver.lexeme }
|
|
670
|
+
end
|
|
673
671
|
|
|
674
|
-
|
|
675
|
-
|
|
672
|
+
def completion_items_for_value_receiver(facts, receiver_type, prefix)
|
|
673
|
+
items = []
|
|
676
674
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
675
|
+
field_receiver_type = project_field_receiver_type_for_completion(receiver_type, facts)
|
|
676
|
+
if field_receiver_type.respond_to?(:fields)
|
|
677
|
+
field_receiver_type.fields.each do |fname, ftype|
|
|
678
|
+
next unless prefix.empty? || fname.start_with?(prefix)
|
|
681
679
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
680
|
+
items << {
|
|
681
|
+
label: fname,
|
|
682
|
+
kind: 8, # Field
|
|
683
|
+
detail: "#{fname}: #{ftype}",
|
|
684
|
+
insertText: fname,
|
|
685
|
+
sortText: "0_#{fname}",
|
|
686
|
+
data: completion_data(fname),
|
|
687
|
+
}
|
|
688
|
+
end
|
|
690
689
|
end
|
|
691
|
-
end
|
|
692
690
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
691
|
+
method_receiver_type = project_method_receiver_type_for_completion(receiver_type)
|
|
692
|
+
methods = methods_for_receiver_type(facts, method_receiver_type)
|
|
693
|
+
methods.each do |mname, binding|
|
|
694
|
+
next unless prefix.empty? || mname.start_with?(prefix)
|
|
695
|
+
|
|
696
|
+
params_str = format_params(binding.type.params)
|
|
697
|
+
item = {
|
|
698
|
+
label: mname,
|
|
699
|
+
kind: 6, # Method
|
|
700
|
+
detail: "#{mname}(#{params_str}) -> #{binding.type.return_type}",
|
|
701
|
+
insertText: mname,
|
|
702
|
+
sortText: "1_#{mname}",
|
|
703
|
+
data: completion_data(mname),
|
|
704
|
+
}
|
|
705
|
+
if (ld = label_details_for_params(params_str, nil))
|
|
706
|
+
item[:labelDetails] = ld
|
|
707
|
+
end
|
|
708
|
+
if (snippet = snippet_for_callable(mname, binding.type.params))
|
|
709
|
+
item[:insertText] = snippet
|
|
710
|
+
item[:insertTextFormat] = 2
|
|
711
|
+
end
|
|
712
|
+
items << item
|
|
713
713
|
end
|
|
714
|
-
items << item
|
|
715
|
-
end
|
|
716
714
|
|
|
717
|
-
|
|
718
|
-
|
|
715
|
+
items
|
|
716
|
+
end
|
|
719
717
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
718
|
+
def methods_for_receiver_type(facts, receiver_type)
|
|
719
|
+
methods = {}
|
|
720
|
+
return methods unless receiver_type
|
|
723
721
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
722
|
+
receiver_candidates = [receiver_type]
|
|
723
|
+
dispatch_receiver_type = method_dispatch_receiver_type_for_completion(receiver_type)
|
|
724
|
+
receiver_candidates << dispatch_receiver_type if dispatch_receiver_type != receiver_type
|
|
727
725
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
726
|
+
# For GenericInstance or Nullable-wrapped GenericInstance, also try the
|
|
727
|
+
# struct definition from facts.types — method registration keys are
|
|
728
|
+
# always the GenericStructDefinition, not a GenericInstance.
|
|
729
|
+
[dispatch_receiver_type, receiver_type].each do |candidate|
|
|
730
|
+
base = candidate
|
|
731
|
+
base = base.base while base.is_a?(Types::Nullable)
|
|
732
|
+
next unless base.is_a?(Types::GenericInstance)
|
|
735
733
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
end
|
|
739
|
-
|
|
740
|
-
receiver_candidates.each do |candidate|
|
|
741
|
-
facts.methods.fetch(candidate, {}).each do |name, binding|
|
|
742
|
-
methods[name] ||= binding
|
|
734
|
+
definition = facts.types[base.name]
|
|
735
|
+
receiver_candidates << definition if definition && !receiver_candidates.include?(definition)
|
|
743
736
|
end
|
|
744
|
-
end
|
|
745
737
|
|
|
746
|
-
facts.imports.each_value do |module_binding|
|
|
747
738
|
receiver_candidates.each do |candidate|
|
|
748
|
-
|
|
739
|
+
facts.methods.fetch(candidate, {}).each do |name, binding|
|
|
749
740
|
methods[name] ||= binding
|
|
750
741
|
end
|
|
751
742
|
end
|
|
752
|
-
end
|
|
753
|
-
methods
|
|
754
|
-
end
|
|
755
|
-
|
|
756
|
-
def method_dispatch_receiver_type_for_completion(receiver_type)
|
|
757
|
-
return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
|
|
758
743
|
|
|
759
|
-
|
|
760
|
-
|
|
744
|
+
facts.imports.each_value do |module_binding|
|
|
745
|
+
receiver_candidates.each do |candidate|
|
|
746
|
+
module_binding.methods.fetch(candidate, {}).each do |name, binding|
|
|
747
|
+
methods[name] ||= binding
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
methods
|
|
761
752
|
end
|
|
762
753
|
|
|
763
|
-
|
|
754
|
+
def method_dispatch_receiver_type_for_completion(receiver_type)
|
|
755
|
+
return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
|
|
764
756
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
argument.is_a?(Types::LiteralTypeArg) ? argument : Types::TypeVar.new("__receiver_arg#{index}")
|
|
769
|
-
end,
|
|
770
|
-
)
|
|
771
|
-
|
|
772
|
-
dispatch_receiver_type == receiver_type ? receiver_type : dispatch_receiver_type
|
|
773
|
-
end
|
|
757
|
+
if receiver_type.is_a?(Types::Nullable)
|
|
758
|
+
return method_dispatch_receiver_type_for_completion(receiver_type.base)
|
|
759
|
+
end
|
|
774
760
|
|
|
775
|
-
|
|
776
|
-
type = type.base while type.is_a?(Types::Nullable)
|
|
777
|
-
type = project_receiver_type_for_completion(type)
|
|
778
|
-
return type.definition if type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
|
|
779
|
-
return field_type_from_struct_definition(type, facts) if facts
|
|
761
|
+
return receiver_type unless receiver_type.is_a?(Types::GenericInstance)
|
|
780
762
|
|
|
781
|
-
|
|
782
|
-
|
|
763
|
+
dispatch_receiver_type = Types::Registry.generic_instance(
|
|
764
|
+
receiver_type.name,
|
|
765
|
+
receiver_type.arguments.each_with_index.map do |argument, index|
|
|
766
|
+
argument.is_a?(Types::LiteralTypeArg) ? argument : Types::TypeVar.new("__receiver_arg#{index}")
|
|
767
|
+
end,
|
|
768
|
+
)
|
|
783
769
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
return type if ref_type_name?(type) || pointer_type_name?(type)
|
|
770
|
+
dispatch_receiver_type == receiver_type ? receiver_type : dispatch_receiver_type
|
|
771
|
+
end
|
|
787
772
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
773
|
+
def project_field_receiver_type_for_completion(type, facts = nil)
|
|
774
|
+
type = type.base while type.is_a?(Types::Nullable)
|
|
775
|
+
type = project_receiver_type_for_completion(type)
|
|
776
|
+
return type.definition if type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
|
|
777
|
+
return field_type_from_struct_definition(type, facts) if facts
|
|
791
778
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
end
|
|
779
|
+
type
|
|
780
|
+
end
|
|
795
781
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
782
|
+
def field_type_from_struct_definition(type, facts)
|
|
783
|
+
return type unless type.is_a?(Types::GenericInstance)
|
|
784
|
+
return type if ref_type_name?(type) || pointer_type_name?(type)
|
|
799
785
|
|
|
800
|
-
|
|
801
|
-
|
|
786
|
+
struct_def = facts.types[type.name]
|
|
787
|
+
struct_def if struct_def&.respond_to?(:fields)
|
|
788
|
+
end
|
|
802
789
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
790
|
+
def project_method_receiver_type_for_completion(type)
|
|
791
|
+
project_receiver_type_for_completion(type)
|
|
792
|
+
end
|
|
806
793
|
|
|
807
|
-
|
|
808
|
-
|
|
794
|
+
def project_receiver_type_for_completion(type)
|
|
795
|
+
return type.arguments.first if ref_type_name?(type)
|
|
796
|
+
return type.arguments.first if pointer_type_name?(type)
|
|
809
797
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
end
|
|
798
|
+
type
|
|
799
|
+
end
|
|
813
800
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
801
|
+
def field_owner_type(receiver_type, facts = nil)
|
|
802
|
+
aggregate_type = project_field_receiver_type_for_completion(receiver_type, facts)
|
|
803
|
+
return aggregate_type.definition if aggregate_type.is_a?(Types::StructInstance) || aggregate_type.is_a?(Types::VariantInstance)
|
|
817
804
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
return params unless data.is_a?(Hash)
|
|
805
|
+
aggregate_type
|
|
806
|
+
end
|
|
821
807
|
|
|
822
|
-
|
|
823
|
-
|
|
808
|
+
def ref_type_name?(type)
|
|
809
|
+
type.is_a?(Types::GenericInstance) && type.name == 'ref' && type.arguments.length == 1
|
|
810
|
+
end
|
|
824
811
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
812
|
+
def pointer_type_name?(type)
|
|
813
|
+
type.is_a?(Types::GenericInstance) && %w[ptr const_ptr].include?(type.name) && type.arguments.length == 1
|
|
814
|
+
end
|
|
828
815
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
816
|
+
def handle_completion_resolve(params)
|
|
817
|
+
data = params['data']
|
|
818
|
+
return params unless data.is_a?(Hash)
|
|
832
819
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
warn "Error in completion resolve handler: #{e.message}"
|
|
836
|
-
params
|
|
837
|
-
end
|
|
820
|
+
name = data['name']
|
|
821
|
+
return params if name.to_s.empty?
|
|
838
822
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
line = lines[lsp_line] || ''
|
|
843
|
-
stripped = line.lstrip
|
|
844
|
-
return nil unless stripped.start_with?('import ')
|
|
823
|
+
uri = data['uri'] || ''
|
|
824
|
+
definition_entry = @workspace.find_definition_token_global(name, preferred_uri: uri)
|
|
825
|
+
return params unless definition_entry
|
|
845
826
|
|
|
846
|
-
|
|
847
|
-
|
|
827
|
+
doc_comment = @workspace.doc_comment_data_for_definition(definition_entry[:uri], definition_entry[:token])
|
|
828
|
+
docs = signature_help_markdown_for_doc_comment(doc_comment)
|
|
829
|
+
return params if docs.empty?
|
|
848
830
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
after_import = after_import[0...alias_pos]
|
|
831
|
+
params.merge('documentation' => { 'kind' => 'markdown', 'value' => docs })
|
|
832
|
+
rescue StandardError => e
|
|
833
|
+
warn "Error in completion resolve handler: #{e.message}"
|
|
834
|
+
params
|
|
854
835
|
end
|
|
855
836
|
|
|
856
|
-
|
|
857
|
-
|
|
837
|
+
def import_completions(uri, lsp_line, lsp_char)
|
|
838
|
+
content = @workspace.get_content(uri)
|
|
839
|
+
lines = content.split("\n", -1)
|
|
840
|
+
line = lines[lsp_line] || ''
|
|
841
|
+
stripped = line.lstrip
|
|
842
|
+
return nil unless stripped.start_with?('import ')
|
|
843
|
+
|
|
844
|
+
import_kw_pos = line.index('import ')
|
|
845
|
+
after_import = line[(import_kw_pos + 7)..].to_s
|
|
846
|
+
|
|
847
|
+
if (as_match = after_import.match(/\s+as\s+/))
|
|
848
|
+
alias_pos = as_match.begin(0)
|
|
849
|
+
cursor_in_import = lsp_char - import_kw_pos - 7
|
|
850
|
+
return nil if cursor_in_import > alias_pos
|
|
851
|
+
after_import = after_import[0...alias_pos]
|
|
852
|
+
end
|
|
858
853
|
|
|
859
|
-
|
|
854
|
+
cursor_in_import = lsp_char - import_kw_pos - 7
|
|
855
|
+
cursor_in_import = [[cursor_in_import, after_import.length].min, 0].max
|
|
860
856
|
|
|
861
|
-
|
|
862
|
-
dir_segments = segments[0...-1]
|
|
863
|
-
filter = segments.last.to_s
|
|
857
|
+
path_typed = after_import[0...cursor_in_import].strip
|
|
864
858
|
|
|
865
|
-
|
|
866
|
-
|
|
859
|
+
segments = path_typed.split('.', -1)
|
|
860
|
+
dir_segments = segments[0...-1]
|
|
861
|
+
filter = segments.last.to_s
|
|
867
862
|
|
|
868
|
-
|
|
869
|
-
|
|
863
|
+
current_path = uri_to_path(uri)
|
|
864
|
+
return nil unless current_path
|
|
870
865
|
|
|
871
|
-
|
|
872
|
-
|
|
866
|
+
roots = MilkTea::ModuleRoots.roots_for_path(current_path)
|
|
867
|
+
fs_dir = dir_segments.join(File::SEPARATOR)
|
|
873
868
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
next unless File.directory?(search_dir)
|
|
869
|
+
modules = {}
|
|
870
|
+
filter_lower = filter.downcase
|
|
877
871
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
872
|
+
roots.each do |root|
|
|
873
|
+
search_dir = fs_dir.empty? ? root : File.join(root, fs_dir)
|
|
874
|
+
next unless File.directory?(search_dir)
|
|
881
875
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
876
|
+
Dir.children(search_dir).sort.each do |name|
|
|
877
|
+
next if name.start_with?('.')
|
|
878
|
+
full_path = File.join(search_dir, name)
|
|
879
|
+
|
|
880
|
+
if name.end_with?('.mt')
|
|
881
|
+
mod_name = name.delete_suffix('.mt')
|
|
882
|
+
next if mod_name.start_with?('.')
|
|
883
|
+
next if full_path == current_path
|
|
884
|
+
next unless filter.empty? || mod_name.downcase.start_with?(filter_lower)
|
|
885
|
+
modules[mod_name] = mod_name
|
|
886
|
+
elsif File.directory?(full_path) && module_dir_contains_mt?(full_path)
|
|
887
|
+
next unless filter.empty? || name.downcase.start_with?(filter_lower)
|
|
888
|
+
modules[name] = name
|
|
889
|
+
end
|
|
891
890
|
end
|
|
892
891
|
end
|
|
893
|
-
end
|
|
894
892
|
|
|
895
|
-
|
|
893
|
+
return nil if modules.empty?
|
|
896
894
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
895
|
+
modules.values.sort.map do |mod_name|
|
|
896
|
+
{
|
|
897
|
+
label: mod_name,
|
|
898
|
+
kind: 2,
|
|
899
|
+
detail: "module #{mod_name}",
|
|
900
|
+
insertText: mod_name,
|
|
901
|
+
sortText: "0_#{mod_name}",
|
|
902
|
+
data: completion_data(mod_name),
|
|
903
|
+
}
|
|
904
|
+
end
|
|
906
905
|
end
|
|
907
|
-
end
|
|
908
906
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
907
|
+
def module_dir_contains_mt?(dir)
|
|
908
|
+
Dir.children(dir).any? do |name|
|
|
909
|
+
next false if name.start_with?('.')
|
|
910
|
+
full = File.join(dir, name)
|
|
911
|
+
name.end_with?('.mt') || (File.directory?(full) && module_dir_contains_mt?(full))
|
|
912
|
+
end
|
|
914
913
|
end
|
|
915
|
-
end
|
|
916
914
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
%w[packed align deprecated].each do |name|
|
|
931
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
932
|
-
items << {
|
|
933
|
-
label: name,
|
|
934
|
-
kind: 14,
|
|
935
|
-
detail: "built-in attribute #{name}",
|
|
936
|
-
insertText: name,
|
|
937
|
-
sortText: "0_#{name}",
|
|
938
|
-
data: completion_data(name),
|
|
939
|
-
}
|
|
940
|
-
end
|
|
915
|
+
def attribute_completions(facts, uri, line, char)
|
|
916
|
+
content = @workspace.get_content(uri)
|
|
917
|
+
return nil unless content
|
|
918
|
+
lines = content.split("\n", -1)
|
|
919
|
+
line_text = lines[line] || ''
|
|
920
|
+
return nil if line_text.empty?
|
|
921
|
+
|
|
922
|
+
attr_match = line_text[0...char].match(/@\[(\w*)$/)
|
|
923
|
+
return nil unless attr_match
|
|
924
|
+
|
|
925
|
+
prefix = attr_match[1]
|
|
926
|
+
items = []
|
|
941
927
|
|
|
942
|
-
|
|
943
|
-
facts.attributes&.each do |name, _binding|
|
|
928
|
+
%w[packed align deprecated].each do |name|
|
|
944
929
|
next unless prefix.empty? || name.start_with?(prefix)
|
|
945
930
|
items << {
|
|
946
931
|
label: name,
|
|
947
932
|
kind: 14,
|
|
948
|
-
detail: "attribute #{name}",
|
|
933
|
+
detail: "built-in attribute #{name}",
|
|
949
934
|
insertText: name,
|
|
950
|
-
sortText: "
|
|
935
|
+
sortText: "0_#{name}",
|
|
951
936
|
data: completion_data(name),
|
|
952
937
|
}
|
|
953
938
|
end
|
|
954
|
-
end
|
|
955
|
-
|
|
956
|
-
items.empty? ? nil : items
|
|
957
|
-
end
|
|
958
|
-
|
|
959
|
-
def format_string_completions(facts, uri, line, char)
|
|
960
|
-
return nil unless facts
|
|
961
|
-
|
|
962
|
-
content = @workspace.get_content(uri)
|
|
963
|
-
return nil unless content
|
|
964
|
-
lines = content.split("\n", -1)
|
|
965
|
-
line_text = lines[line] || ''
|
|
966
|
-
return nil if line_text.empty?
|
|
967
|
-
|
|
968
|
-
text_before = line_text[0...char]
|
|
969
|
-
open_pos = text_before.rindex('#{')
|
|
970
|
-
return nil unless open_pos
|
|
971
|
-
|
|
972
|
-
close_pos = text_before.index('}', open_pos)
|
|
973
|
-
return nil if close_pos && close_pos < char
|
|
974
|
-
|
|
975
|
-
prefix = text_before[(open_pos + 2)..] || ''
|
|
976
|
-
items = []
|
|
977
|
-
|
|
978
|
-
facts.values.each do |name, binding|
|
|
979
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
980
|
-
items << {
|
|
981
|
-
label: name,
|
|
982
|
-
kind: 13,
|
|
983
|
-
detail: "#{name}: #{binding.type}",
|
|
984
|
-
insertText: name,
|
|
985
|
-
sortText: "0_#{name}",
|
|
986
|
-
data: completion_data(name),
|
|
987
|
-
}
|
|
988
|
-
end
|
|
989
|
-
|
|
990
|
-
facts.functions.each do |name, _binding|
|
|
991
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
992
|
-
items << {
|
|
993
|
-
label: name,
|
|
994
|
-
kind: 12,
|
|
995
|
-
detail: "function #{name}",
|
|
996
|
-
insertText: name,
|
|
997
|
-
sortText: "1_#{name}",
|
|
998
|
-
data: completion_data(name),
|
|
999
|
-
}
|
|
1000
|
-
end
|
|
1001
939
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
snapshot = latest_completion_snapshot(frame, line + 1, char + 1)
|
|
1005
|
-
if snapshot
|
|
1006
|
-
snapshot.bindings.each do |name, binding|
|
|
940
|
+
if facts
|
|
941
|
+
facts.attributes&.each do |name, _binding|
|
|
1007
942
|
next unless prefix.empty? || name.start_with?(prefix)
|
|
1008
|
-
next unless binding.respond_to?(:name) && binding.respond_to?(:type)
|
|
1009
943
|
items << {
|
|
1010
944
|
label: name,
|
|
1011
|
-
kind:
|
|
1012
|
-
detail: "#{name}
|
|
945
|
+
kind: 14,
|
|
946
|
+
detail: "attribute #{name}",
|
|
1013
947
|
insertText: name,
|
|
1014
|
-
sortText: "
|
|
948
|
+
sortText: "1_#{name}",
|
|
1015
949
|
data: completion_data(name),
|
|
1016
950
|
}
|
|
1017
951
|
end
|
|
1018
952
|
end
|
|
1019
|
-
end
|
|
1020
|
-
|
|
1021
|
-
items.empty? ? nil : items
|
|
1022
|
-
end
|
|
1023
953
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
content = @workspace.get_content(uri)
|
|
1028
|
-
return nil unless content
|
|
1029
|
-
lines = content.split("\n", -1)
|
|
1030
|
-
line_text = lines[line] || ''
|
|
1031
|
-
return nil if line_text.empty?
|
|
954
|
+
items.empty? ? nil : items
|
|
955
|
+
end
|
|
1032
956
|
|
|
1033
|
-
|
|
957
|
+
def format_string_completions(facts, uri, line, char)
|
|
958
|
+
return nil unless facts
|
|
1034
959
|
|
|
1035
|
-
|
|
1036
|
-
|
|
960
|
+
content = @workspace.get_content(uri)
|
|
961
|
+
return nil unless content
|
|
962
|
+
lines = content.split("\n", -1)
|
|
963
|
+
line_text = lines[line] || ''
|
|
964
|
+
return nil if line_text.empty?
|
|
1037
965
|
|
|
1038
|
-
|
|
966
|
+
text_before = line_text[0...char]
|
|
967
|
+
open_pos = text_before.rindex('#{')
|
|
968
|
+
return nil unless open_pos
|
|
1039
969
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
prefix = prefix_match ? prefix_match[1] : ''
|
|
970
|
+
close_pos = text_before.index('}', open_pos)
|
|
971
|
+
return nil if close_pos && close_pos < char
|
|
1043
972
|
|
|
1044
|
-
|
|
973
|
+
prefix = text_before[(open_pos + 2)..] || ''
|
|
974
|
+
items = []
|
|
1045
975
|
|
|
1046
|
-
|
|
976
|
+
facts.values.each do |name, binding|
|
|
977
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
978
|
+
items << {
|
|
979
|
+
label: name,
|
|
980
|
+
kind: 13,
|
|
981
|
+
detail: "#{name}: #{binding.type}",
|
|
982
|
+
insertText: name,
|
|
983
|
+
sortText: "0_#{name}",
|
|
984
|
+
data: completion_data(name),
|
|
985
|
+
}
|
|
986
|
+
end
|
|
1047
987
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
next if already_provided.include?(param.name)
|
|
1051
|
-
next unless prefix.empty? || param.name.start_with?(prefix)
|
|
988
|
+
facts.functions.each do |name, _binding|
|
|
989
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
1052
990
|
items << {
|
|
1053
|
-
label:
|
|
1054
|
-
kind:
|
|
1055
|
-
detail: "#{
|
|
1056
|
-
insertText:
|
|
1057
|
-
sortText: "
|
|
1058
|
-
data: completion_data(
|
|
991
|
+
label: name,
|
|
992
|
+
kind: 12,
|
|
993
|
+
detail: "function #{name}",
|
|
994
|
+
insertText: name,
|
|
995
|
+
sortText: "1_#{name}",
|
|
996
|
+
data: completion_data(name),
|
|
1059
997
|
}
|
|
1060
998
|
end
|
|
999
|
+
|
|
1000
|
+
frame = enclosing_completion_frame(facts, line + 1)
|
|
1001
|
+
if frame
|
|
1002
|
+
snapshot = latest_completion_snapshot(frame, line + 1, char + 1)
|
|
1003
|
+
if snapshot
|
|
1004
|
+
snapshot.bindings.each do |name, binding|
|
|
1005
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
1006
|
+
next unless binding.respond_to?(:name) && binding.respond_to?(:type)
|
|
1007
|
+
items << {
|
|
1008
|
+
label: name,
|
|
1009
|
+
kind: 13,
|
|
1010
|
+
detail: "#{name}: #{binding.type}",
|
|
1011
|
+
insertText: name,
|
|
1012
|
+
sortText: "2_#{name}",
|
|
1013
|
+
data: completion_data(name),
|
|
1014
|
+
}
|
|
1015
|
+
end
|
|
1016
|
+
end
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
items.empty? ? nil : items
|
|
1061
1020
|
end
|
|
1062
1021
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1022
|
+
def named_argument_completions(facts, uri, line, char)
|
|
1023
|
+
return nil unless facts
|
|
1024
|
+
|
|
1025
|
+
content = @workspace.get_content(uri)
|
|
1026
|
+
return nil unless content
|
|
1027
|
+
lines = content.split("\n", -1)
|
|
1028
|
+
line_text = lines[line] || ''
|
|
1029
|
+
return nil if line_text.empty?
|
|
1030
|
+
|
|
1031
|
+
text_before = line_text[0...char]
|
|
1032
|
+
|
|
1033
|
+
call_match = text_before.match(/([\w.]+)\s*\(\s*(?:[^)]*,\s*)\s*$/)
|
|
1034
|
+
call_match ||= text_before.match(/([\w.]+)\s*\(\s*$/)
|
|
1035
|
+
|
|
1036
|
+
return nil unless call_match
|
|
1037
|
+
|
|
1038
|
+
callable_name = call_match[1]
|
|
1039
|
+
prefix_match = text_before.match(/(?:^|[,\s(])(\w*)$/)
|
|
1040
|
+
prefix = prefix_match ? prefix_match[1] : ''
|
|
1041
|
+
|
|
1042
|
+
already_provided = text_before.scan(/(\w+)\s*[=:]/).flatten.to_set
|
|
1043
|
+
|
|
1044
|
+
items = []
|
|
1045
|
+
|
|
1046
|
+
if (func_binding = facts.functions[callable_name])
|
|
1047
|
+
func_binding.type.params.each do |param|
|
|
1048
|
+
next if already_provided.include?(param.name)
|
|
1049
|
+
next unless prefix.empty? || param.name.start_with?(prefix)
|
|
1050
|
+
items << {
|
|
1051
|
+
label: "#{param.name} = ",
|
|
1052
|
+
kind: 14,
|
|
1053
|
+
detail: "#{param.name}: #{param.type}",
|
|
1054
|
+
insertText: "#{param.name} = ",
|
|
1055
|
+
sortText: "0_#{param.name}",
|
|
1056
|
+
data: completion_data(param.name),
|
|
1057
|
+
}
|
|
1058
|
+
end
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
resolved_type = facts.types[callable_name]
|
|
1062
|
+
if resolved_type.nil? && callable_name.include?('.')
|
|
1063
|
+
parts = callable_name.split('.', 2)
|
|
1064
|
+
mod_binding = facts.imports[parts[0]]
|
|
1065
|
+
resolved_type = mod_binding&.types&.[](parts[1]) if mod_binding
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
if resolved_type&.respond_to?(:fields) && !resolved_type.fields.empty?
|
|
1069
|
+
resolved_type.fields.each do |fname, ftype|
|
|
1070
|
+
next if already_provided.include?(fname)
|
|
1071
|
+
next unless prefix.empty? || fname.start_with?(prefix)
|
|
1072
|
+
items << {
|
|
1073
|
+
label: "#{fname} = ",
|
|
1074
|
+
kind: 8,
|
|
1075
|
+
detail: "#{fname}: #{ftype}",
|
|
1076
|
+
insertText: "#{fname} = ",
|
|
1077
|
+
sortText: "0_#{fname}",
|
|
1078
|
+
data: completion_data(fname),
|
|
1079
|
+
}
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
items.empty? ? nil : items
|
|
1068
1084
|
end
|
|
1069
1085
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1086
|
+
def specialization_completions(facts, _uri, line, char)
|
|
1087
|
+
return nil unless facts
|
|
1088
|
+
|
|
1089
|
+
content = @workspace.get_content(_uri)
|
|
1090
|
+
return nil unless content
|
|
1091
|
+
lines = content.split("\n", -1)
|
|
1092
|
+
line_text = lines[line] || ''
|
|
1093
|
+
return nil if line_text.empty?
|
|
1094
|
+
|
|
1095
|
+
text_before = line_text[0...char]
|
|
1096
|
+
sp_match = text_before.match(/([\w.]+)\[(.*)$/)
|
|
1097
|
+
return nil unless sp_match
|
|
1098
|
+
|
|
1099
|
+
prefix = sp_match[2]
|
|
1100
|
+
items = []
|
|
1101
|
+
|
|
1102
|
+
facts.types.each do |name, _type|
|
|
1103
|
+
next unless prefix.empty? || name.start_with?(prefix)
|
|
1074
1104
|
items << {
|
|
1075
|
-
label:
|
|
1076
|
-
kind:
|
|
1077
|
-
detail: "
|
|
1078
|
-
insertText:
|
|
1079
|
-
sortText: "0_#{
|
|
1080
|
-
data: completion_data(
|
|
1105
|
+
label: name,
|
|
1106
|
+
kind: 5,
|
|
1107
|
+
detail: "type #{name}",
|
|
1108
|
+
insertText: name,
|
|
1109
|
+
sortText: "0_#{name}",
|
|
1110
|
+
data: completion_data(name),
|
|
1081
1111
|
}
|
|
1082
1112
|
end
|
|
1083
|
-
end
|
|
1084
1113
|
|
|
1085
|
-
|
|
1086
|
-
end
|
|
1087
|
-
|
|
1088
|
-
def specialization_completions(facts, _uri, line, char)
|
|
1089
|
-
return nil unless facts
|
|
1090
|
-
|
|
1091
|
-
content = @workspace.get_content(_uri)
|
|
1092
|
-
return nil unless content
|
|
1093
|
-
lines = content.split("\n", -1)
|
|
1094
|
-
line_text = lines[line] || ''
|
|
1095
|
-
return nil if line_text.empty?
|
|
1096
|
-
|
|
1097
|
-
text_before = line_text[0...char]
|
|
1098
|
-
sp_match = text_before.match(/([\w.]+)\[(.*)$/)
|
|
1099
|
-
return nil unless sp_match
|
|
1100
|
-
|
|
1101
|
-
prefix = sp_match[2]
|
|
1102
|
-
items = []
|
|
1103
|
-
|
|
1104
|
-
facts.types.each do |name, _type|
|
|
1105
|
-
next unless prefix.empty? || name.start_with?(prefix)
|
|
1106
|
-
items << {
|
|
1107
|
-
label: name,
|
|
1108
|
-
kind: 5,
|
|
1109
|
-
detail: "type #{name}",
|
|
1110
|
-
insertText: name,
|
|
1111
|
-
sortText: "0_#{name}",
|
|
1112
|
-
data: completion_data(name),
|
|
1113
|
-
}
|
|
1114
|
+
items.empty? ? nil : items
|
|
1114
1115
|
end
|
|
1115
|
-
|
|
1116
|
-
items.empty? ? nil : items
|
|
1117
|
-
end
|
|
1118
1116
|
end
|
|
1119
1117
|
end
|
|
1120
1118
|
end
|