mt-lang 0.3.38 → 0.3.39
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/docs/lsp-performance.md +347 -0
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/lsp/diagnostics.rb +15 -5
- data/lib/milk_tea/lsp/server/completion.rb +100 -77
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +4 -6
- data/lib/milk_tea/lsp/server/formatting.rb +13 -3
- data/lib/milk_tea/lsp/server/hover.rb +321 -41
- data/lib/milk_tea/lsp/server/lifecycle.rb +12 -0
- data/lib/milk_tea/lsp/server/references.rb +3 -1
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +55 -16
- data/lib/milk_tea/lsp/server/text_documents.rb +2 -2
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -2
- data/lib/milk_tea/lsp/server/utilities.rb +4 -0
- data/lib/milk_tea/lsp/server.rb +9 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +14 -3
- data/lib/milk_tea/lsp/workspace/caches.rb +17 -1
- data/lib/milk_tea/lsp/workspace/collection.rb +4 -0
- data/lib/milk_tea/lsp/workspace/module_index.rb +134 -0
- data/lib/milk_tea/lsp/workspace/store.rb +9 -4
- data/lib/milk_tea/lsp/workspace.rb +8 -0
- metadata +4 -2
|
@@ -37,11 +37,15 @@ module MilkTea
|
|
|
37
37
|
result.empty? ? nil : result
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
TRIGGER_KIND_INCOMPLETE = 2
|
|
41
|
+
MAX_COMPLETION_SESSION_ENTRIES = 64
|
|
42
|
+
|
|
40
43
|
def handle_completion(params)
|
|
41
44
|
stages = new_perf_stages
|
|
42
45
|
total_start = stages ? monotonic_time : nil
|
|
43
46
|
uri = params['textDocument']['uri']
|
|
44
47
|
@current_completion_uri = uri
|
|
48
|
+
@current_completion_trigger_kind = params.dig('context', 'triggerKind') || 1
|
|
45
49
|
lsp_line = params['position']['line']
|
|
46
50
|
lsp_char = params['position']['character']
|
|
47
51
|
branch = 'none'
|
|
@@ -49,50 +53,61 @@ module MilkTea
|
|
|
49
53
|
|
|
50
54
|
prefix = measure_perf_stage(stages, 'prefix') { current_word_prefix(uri, lsp_line, lsp_char) }
|
|
51
55
|
|
|
56
|
+
# Completion session re-filtering: when the editor keeps requesting
|
|
57
|
+
# while the prefix grows (triggerFromIncompleteCompletions), re-filter
|
|
58
|
+
# the previously computed candidate pool instead of rebuilding it.
|
|
59
|
+
response = measure_perf_stage(stages, 'session') { completion_session_response(uri, lsp_line, lsp_char, prefix) }
|
|
60
|
+
if response
|
|
61
|
+
branch = 'session'
|
|
62
|
+
item_count = response[:items].length
|
|
63
|
+
return response
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
branch, item_count, response = measure_perf_stage(stages, 'compute') { compute_completion_items(uri, lsp_line, lsp_char, prefix, stages) }
|
|
67
|
+
store_completion_session(uri, lsp_line, lsp_char, prefix, response)
|
|
68
|
+
response
|
|
69
|
+
rescue StandardError => e
|
|
70
|
+
branch = 'error'
|
|
71
|
+
warn "Error in completion handler: #{e.message}"
|
|
72
|
+
{ isIncomplete: false, items: [] }
|
|
73
|
+
ensure
|
|
74
|
+
log_request_stage_breakdown('textDocument/completion', total_start, uri: uri, stages: stages, summary: "branch=#{branch} items=#{item_count}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def compute_completion_items(uri, lsp_line, lsp_char, prefix, stages)
|
|
52
78
|
import_items = measure_perf_stage(stages, 'import_context') { import_completions(uri, lsp_line, lsp_char) }
|
|
53
79
|
if import_items
|
|
54
|
-
|
|
55
|
-
item_count = import_items.length
|
|
56
|
-
return { isIncomplete: false, items: import_items }
|
|
80
|
+
return ['import', import_items.length, { isIncomplete: false, items: import_items }]
|
|
57
81
|
end
|
|
58
82
|
|
|
59
83
|
facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
|
|
60
84
|
|
|
61
85
|
unless facts
|
|
62
|
-
|
|
63
|
-
return { isIncomplete: false, items: [] }
|
|
86
|
+
return ['no-facts', 0, { isIncomplete: false, items: [] }]
|
|
64
87
|
end
|
|
65
88
|
|
|
66
89
|
# Attribute context: complete inside @[...]
|
|
67
90
|
attr_items = attribute_completions(facts, uri, lsp_line, lsp_char)
|
|
68
91
|
if attr_items
|
|
69
|
-
|
|
70
|
-
item_count = attr_items.length
|
|
71
|
-
return { isIncomplete: false, items: attr_items }
|
|
92
|
+
return ['attribute', attr_items.length, { isIncomplete: false, items: attr_items }]
|
|
72
93
|
end
|
|
73
94
|
|
|
74
95
|
# Format string interpolation: complete inside f"... #{ }
|
|
75
96
|
fmt_items = format_string_completions(facts, uri, lsp_line, lsp_char)
|
|
76
97
|
if fmt_items
|
|
77
|
-
|
|
78
|
-
item_count = fmt_items.length
|
|
79
|
-
return { isIncomplete: false, items: fmt_items }
|
|
98
|
+
return ['format-string', fmt_items.length, { isIncomplete: false, items: fmt_items }]
|
|
80
99
|
end
|
|
81
100
|
|
|
82
101
|
# Named argument completions: inside function/struct call e.g. Point(x: 1, |)
|
|
83
102
|
named_items = named_argument_completions(facts, uri, lsp_line, lsp_char)
|
|
84
103
|
if named_items
|
|
85
|
-
|
|
86
|
-
item_count = named_items.length
|
|
87
|
-
return { isIncomplete: false, items: named_items }
|
|
104
|
+
return ['named-arg', named_items.length, { isIncomplete: false, items: named_items }]
|
|
88
105
|
end
|
|
89
106
|
|
|
90
107
|
# Specialization context: inside name[...]
|
|
91
108
|
spec_items = specialization_completions(facts, uri, lsp_line, lsp_char)
|
|
92
109
|
if spec_items
|
|
93
|
-
|
|
94
|
-
item_count = spec_items.length
|
|
95
|
-
return { isIncomplete: false, items: spec_items }
|
|
110
|
+
return ['specialization', spec_items.length, { isIncomplete: false, items: spec_items }]
|
|
96
111
|
end
|
|
97
112
|
|
|
98
113
|
# When user is typing after '.', return module members or method completions.
|
|
@@ -105,7 +120,6 @@ module MilkTea
|
|
|
105
120
|
if dot_recv
|
|
106
121
|
# Module member access: rl.init_window, rl.RAYWHITE, etc.
|
|
107
122
|
if (module_binding = facts.imports[dot_recv])
|
|
108
|
-
branch = 'module'
|
|
109
123
|
items = measure_perf_stage(stages, 'build') do
|
|
110
124
|
result = []
|
|
111
125
|
module_binding.functions.each do |fname, binding|
|
|
@@ -152,8 +166,7 @@ module MilkTea
|
|
|
152
166
|
end
|
|
153
167
|
result
|
|
154
168
|
end
|
|
155
|
-
|
|
156
|
-
return { isIncomplete: false, items: items }
|
|
169
|
+
return ['module', items.length, { isIncomplete: false, items: items }]
|
|
157
170
|
end
|
|
158
171
|
if (type_receiver = measure_perf_stage(stages, 'type_receiver') { resolve_type_receiver_info(facts, dot_recv, dot_recv_path) })
|
|
159
172
|
receiver_label = type_receiver[:label]
|
|
@@ -162,7 +175,6 @@ module MilkTea
|
|
|
162
175
|
|
|
163
176
|
# Enum/Flags member access: Color.RED, KeyboardKey.A, etc.
|
|
164
177
|
if type.is_a?(Types::EnumBase)
|
|
165
|
-
branch = 'enum-members'
|
|
166
178
|
items = measure_perf_stage(stages, 'build') do
|
|
167
179
|
type.members.filter_map do |mname|
|
|
168
180
|
next if !prefix.empty? && !mname.start_with?(prefix)
|
|
@@ -176,13 +188,11 @@ module MilkTea
|
|
|
176
188
|
}
|
|
177
189
|
end
|
|
178
190
|
end
|
|
179
|
-
|
|
180
|
-
return { isIncomplete: false, items: items }
|
|
191
|
+
return ['enum-members', items.length, { isIncomplete: false, items: items }]
|
|
181
192
|
end
|
|
182
193
|
|
|
183
194
|
# Variant arm access: Option.none, Result.success, etc.
|
|
184
195
|
if type.is_a?(Types::Variant)
|
|
185
|
-
branch = 'variant-arms'
|
|
186
196
|
items = measure_perf_stage(stages, 'build') do
|
|
187
197
|
type.arm_names.filter_map do |aname|
|
|
188
198
|
next if !prefix.empty? && !aname.start_with?(prefix)
|
|
@@ -196,13 +206,11 @@ module MilkTea
|
|
|
196
206
|
}
|
|
197
207
|
end
|
|
198
208
|
end
|
|
199
|
-
|
|
200
|
-
return { isIncomplete: false, items: items }
|
|
209
|
+
return ['variant-arms', items.length, { isIncomplete: false, items: items }]
|
|
201
210
|
end
|
|
202
211
|
|
|
203
212
|
# Nested struct type members: ShapeGroup.CircleData, etc.
|
|
204
213
|
if type.is_a?(Types::Struct) && type.respond_to?(:nested_types) && type.nested_types.any?
|
|
205
|
-
branch = 'nested-types'
|
|
206
214
|
items = measure_perf_stage(stages, 'build') do
|
|
207
215
|
type.nested_types.filter_map do |nt_name, _nt_type|
|
|
208
216
|
next if !prefix.empty? && !nt_name.start_with?(prefix)
|
|
@@ -216,15 +224,12 @@ module MilkTea
|
|
|
216
224
|
}
|
|
217
225
|
end
|
|
218
226
|
end
|
|
219
|
-
|
|
220
|
-
return { isIncomplete: false, items: items } unless items.empty?
|
|
227
|
+
return ['nested-types', items.length, { isIncomplete: false, items: items }] unless items.empty?
|
|
221
228
|
end
|
|
222
229
|
|
|
223
230
|
items = measure_perf_stage(stages, 'build') { completion_items_for_type_receiver(facts, type, prefix) }
|
|
224
231
|
unless items.empty?
|
|
225
|
-
|
|
226
|
-
item_count = items.length
|
|
227
|
-
return { isIncomplete: false, items: items }
|
|
232
|
+
return ['type-receiver', items.length, { isIncomplete: false, items: items }]
|
|
228
233
|
end
|
|
229
234
|
end
|
|
230
235
|
|
|
@@ -237,17 +242,13 @@ module MilkTea
|
|
|
237
242
|
receiver_type = measure_perf_stage(stages, 'imported_value_receiver') { val_binding.type }
|
|
238
243
|
items = measure_perf_stage(stages, 'build') { completion_items_for_value_receiver(facts, receiver_type, prefix) }
|
|
239
244
|
unless items.empty?
|
|
240
|
-
|
|
241
|
-
item_count = items.length
|
|
242
|
-
return { isIncomplete: false, items: items }
|
|
245
|
+
return ['imported-value-receiver', items.length, { isIncomplete: false, items: items }]
|
|
243
246
|
end
|
|
244
247
|
end
|
|
245
248
|
else
|
|
246
249
|
chain_items = measure_perf_stage(stages, 'value_chain') { value_chain_completions(facts, dot_recv_path, lsp_line, lsp_char, prefix) }
|
|
247
250
|
if chain_items
|
|
248
|
-
|
|
249
|
-
item_count = chain_items.length
|
|
250
|
-
return { isIncomplete: false, items: chain_items }
|
|
251
|
+
return ['value-chain', chain_items.length, { isIncomplete: false, items: chain_items }]
|
|
251
252
|
end
|
|
252
253
|
end
|
|
253
254
|
end
|
|
@@ -255,14 +256,11 @@ module MilkTea
|
|
|
255
256
|
if (receiver_type = measure_perf_stage(stages, 'value_receiver') { resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1) })
|
|
256
257
|
items = measure_perf_stage(stages, 'build') { completion_items_for_value_receiver(facts, receiver_type, prefix) }
|
|
257
258
|
unless items.empty?
|
|
258
|
-
|
|
259
|
-
item_count = items.length
|
|
260
|
-
return { isIncomplete: false, items: items }
|
|
259
|
+
return ['value-receiver', items.length, { isIncomplete: false, items: items }]
|
|
261
260
|
end
|
|
262
261
|
end
|
|
263
262
|
|
|
264
263
|
# Method completions on a non-module receiver.
|
|
265
|
-
branch = 'method-fallback'
|
|
266
264
|
method_items = measure_perf_stage(stages, 'build') do
|
|
267
265
|
result = []
|
|
268
266
|
facts.methods.each do |_recv_type, methods|
|
|
@@ -296,10 +294,9 @@ module MilkTea
|
|
|
296
294
|
method_items = method_items.first(MAX_COMPLETION_ITEMS)
|
|
297
295
|
item_count = MAX_COMPLETION_ITEMS
|
|
298
296
|
end
|
|
299
|
-
return { isIncomplete: truncated, items: method_items }
|
|
297
|
+
return ['method-fallback', item_count, { isIncomplete: truncated, items: method_items }]
|
|
300
298
|
end
|
|
301
299
|
|
|
302
|
-
branch = 'global'
|
|
303
300
|
items = measure_perf_stage(stages, 'build') do
|
|
304
301
|
result = []
|
|
305
302
|
function_docs_cache = {}
|
|
@@ -450,13 +447,51 @@ module MilkTea
|
|
|
450
447
|
items = items.first(MAX_COMPLETION_ITEMS)
|
|
451
448
|
item_count = MAX_COMPLETION_ITEMS
|
|
452
449
|
end
|
|
453
|
-
{ isIncomplete: truncated, items: items }
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
450
|
+
['global', item_count, { isIncomplete: truncated, items: items }]
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Serve a previously computed candidate pool when the editor is
|
|
454
|
+
# re-triggering with a longer prefix (triggerFromIncompleteCompletions)
|
|
455
|
+
# on the same line. Safe only when the text before the cursor is a strict
|
|
456
|
+
# extension of the cached context and the word prefix has grown; re-filter
|
|
457
|
+
# the (already prefix-filtered) pool with the longer prefix.
|
|
458
|
+
def completion_session_response(uri, lsp_line, lsp_char, prefix)
|
|
459
|
+
return nil if prefix.empty?
|
|
460
|
+
return nil unless @current_completion_trigger_kind == TRIGGER_KIND_INCOMPLETE
|
|
461
|
+
|
|
462
|
+
content = @workspace.get_content(uri)
|
|
463
|
+
line_prefix = (content.split("\n", -1)[lsp_line] || '')[0...lsp_char]
|
|
464
|
+
cached = @completion_session_cache[[uri, lsp_line]]
|
|
465
|
+
return nil unless cached
|
|
466
|
+
return nil unless line_prefix.start_with?(cached[:line_prefix])
|
|
467
|
+
return nil unless prefix.start_with?(cached[:prefix])
|
|
468
|
+
|
|
469
|
+
items = cached[:items].select { |item| item[:label].to_s.start_with?(prefix) }
|
|
470
|
+
{ isIncomplete: cached[:isIncomplete], items: items }
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def store_completion_session(uri, lsp_line, lsp_char, prefix, response)
|
|
474
|
+
return if prefix.empty?
|
|
475
|
+
|
|
476
|
+
content = @workspace.get_content(uri)
|
|
477
|
+
line_prefix = (content.split("\n", -1)[lsp_line] || '')[0...lsp_char]
|
|
478
|
+
key = [uri, lsp_line]
|
|
479
|
+
@completion_session_cache[key] = {
|
|
480
|
+
line_prefix: line_prefix,
|
|
481
|
+
prefix: prefix,
|
|
482
|
+
isIncomplete: response[:isIncomplete],
|
|
483
|
+
items: response[:items],
|
|
484
|
+
}
|
|
485
|
+
if @completion_session_order_set.add?(key)
|
|
486
|
+
@completion_session_order << key
|
|
487
|
+
evict_completion_sessions while @completion_session_order.length > MAX_COMPLETION_SESSION_ENTRIES
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def evict_completion_sessions
|
|
492
|
+
key = @completion_session_order.shift
|
|
493
|
+
@completion_session_cache.delete(key)
|
|
494
|
+
@completion_session_order_set.delete(key)
|
|
460
495
|
end
|
|
461
496
|
|
|
462
497
|
def value_chain_completions(facts, dot_recv_path, lsp_line, lsp_char, prefix)
|
|
@@ -821,6 +856,11 @@ module MilkTea
|
|
|
821
856
|
return params if name.to_s.empty?
|
|
822
857
|
|
|
823
858
|
uri = data['uri'] || ''
|
|
859
|
+
key = [uri, name]
|
|
860
|
+
if @completion_resolve_cache.key?(key)
|
|
861
|
+
return @completion_resolve_cache[key]
|
|
862
|
+
end
|
|
863
|
+
|
|
824
864
|
definition_entry = @workspace.find_definition_token_global(name, preferred_uri: uri)
|
|
825
865
|
return params unless definition_entry
|
|
826
866
|
|
|
@@ -828,7 +868,7 @@ module MilkTea
|
|
|
828
868
|
docs = signature_help_markdown_for_doc_comment(doc_comment)
|
|
829
869
|
return params if docs.empty?
|
|
830
870
|
|
|
831
|
-
params.merge('documentation' => { 'kind' => 'markdown', 'value' => docs })
|
|
871
|
+
@completion_resolve_cache[key] = params.merge('documentation' => { 'kind' => 'markdown', 'value' => docs })
|
|
832
872
|
rescue StandardError => e
|
|
833
873
|
warn "Error in completion resolve handler: #{e.message}"
|
|
834
874
|
params
|
|
@@ -862,6 +902,7 @@ module MilkTea
|
|
|
862
902
|
|
|
863
903
|
current_path = uri_to_path(uri)
|
|
864
904
|
return nil unless current_path
|
|
905
|
+
current_path = File.expand_path(current_path)
|
|
865
906
|
|
|
866
907
|
roots = MilkTea::ModuleRoots.roots_for_path(current_path)
|
|
867
908
|
fs_dir = dir_segments.join(File::SEPARATOR)
|
|
@@ -870,23 +911,13 @@ module MilkTea
|
|
|
870
911
|
filter_lower = filter.downcase
|
|
871
912
|
|
|
872
913
|
roots.each do |root|
|
|
873
|
-
|
|
874
|
-
next unless
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
next
|
|
878
|
-
|
|
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
|
|
914
|
+
names = @workspace.module_importable_names(root, fs_dir, current_path: current_path)
|
|
915
|
+
next unless names
|
|
916
|
+
|
|
917
|
+
names.each_key do |mod_name|
|
|
918
|
+
next unless filter.empty? || mod_name.downcase.start_with?(filter_lower)
|
|
919
|
+
|
|
920
|
+
modules[mod_name] = mod_name
|
|
890
921
|
end
|
|
891
922
|
end
|
|
892
923
|
|
|
@@ -904,14 +935,6 @@ module MilkTea
|
|
|
904
935
|
end
|
|
905
936
|
end
|
|
906
937
|
|
|
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
|
|
913
|
-
end
|
|
914
|
-
|
|
915
938
|
def attribute_completions(facts, uri, line, char)
|
|
916
939
|
content = @workspace.get_content(uri)
|
|
917
940
|
return nil unless content
|
|
@@ -142,6 +142,10 @@ module MilkTea
|
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
144
|
end
|
|
145
|
+
# Fresh facts just landed for this document; let the editor
|
|
146
|
+
# re-fetch semantic tokens so analyzed highlighting replaces the
|
|
147
|
+
# lexical fallback that was served while facts were unavailable.
|
|
148
|
+
refresh_client_semantic_tokens
|
|
145
149
|
elsif perf_logging?
|
|
146
150
|
@diagnostics_perf[:dropped_stale] += 1
|
|
147
151
|
end
|
|
@@ -223,12 +227,6 @@ module MilkTea
|
|
|
223
227
|
dependency_export_surface_fingerprint(previous_content) != dependency_export_surface_fingerprint(current_content)
|
|
224
228
|
end
|
|
225
229
|
|
|
226
|
-
def semantic_tokens_allow_last_good_fallback?(uri)
|
|
227
|
-
@diagnostics_mutex.synchronize do
|
|
228
|
-
@diagnostics_pending.key?(uri) || @diagnostics_enqueued.include?(uri)
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
230
|
def notify_diagnostic_errors(uri, diagnostics)
|
|
233
231
|
errors = diagnostics.select { |d| d.is_a?(Hash) && (d["severity"] || d[:severity]) == 1 }
|
|
234
232
|
return if errors.empty?
|
|
@@ -10,19 +10,29 @@ module MilkTea
|
|
|
10
10
|
stages = new_perf_stages
|
|
11
11
|
total_start = stages ? monotonic_time : nil
|
|
12
12
|
uri = params['textDocument']['uri']
|
|
13
|
+
|
|
14
|
+
content_hash = @workspace.get_content(uri).hash
|
|
15
|
+
facts = @workspace.get_facts(uri)
|
|
16
|
+
facts_id = facts&.object_id
|
|
17
|
+
cached = @document_symbol_cache[uri]
|
|
18
|
+
if cached && cached[:content_hash] == content_hash && cached[:facts_id] == facts_id
|
|
19
|
+
return cached[:result]
|
|
20
|
+
end
|
|
21
|
+
|
|
13
22
|
symbols = measure_perf_stage(stages, 'symbols') { @workspace.get_symbols(uri) }
|
|
14
23
|
result = measure_perf_stage(stages, 'format') { symbols.map { |sym| format_document_symbol(sym) } }
|
|
15
24
|
|
|
16
|
-
# Enrich with hierarchical children from AST
|
|
17
|
-
|
|
25
|
+
# Enrich with hierarchical children from AST. Use the facts' own AST so
|
|
26
|
+
# binding_resolution node object_ids line up with the outline locals.
|
|
27
|
+
ast = facts&.ast || @workspace.get_ast(uri)
|
|
18
28
|
if ast && result
|
|
19
|
-
facts = @workspace.get_facts(uri)
|
|
20
29
|
enrich_with_children(result, ast, facts)
|
|
21
30
|
end
|
|
22
31
|
|
|
23
32
|
module_name = resolve_outline_module_name(uri)
|
|
24
33
|
result = wrap_in_module_hierarchy(result, module_name, uri) if module_name && result&.any?
|
|
25
34
|
|
|
35
|
+
@document_symbol_cache[uri] = { content_hash: content_hash, facts_id: facts_id, result: result }
|
|
26
36
|
result
|
|
27
37
|
rescue StandardError => e
|
|
28
38
|
warn "Error in documentSymbol handler: #{e.message}"
|