mt-lang 0.2.13 → 0.2.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34c858250b3d56b9c9e2184e4cf3a29d180b176e49d77be5bffd52063a458718
4
- data.tar.gz: 8ad1e294bd42fc71273ee495ff91b632b788a835c1f4d96203f298d6e2b6db6b
3
+ metadata.gz: 684d2aada4af1e2b5fc1c0685e26004ed9442931692cb25007777b09df77dbe5
4
+ data.tar.gz: 5f16c0ce02404a51f4ac8dd8c12b8047fb4ae5ab442b1269ba7ce8c58809a065
5
5
  SHA512:
6
- metadata.gz: ebd5cae94ad9eaa939d265bbc31d57a1b29ecb53b2abf8e3437b6264d0ce635ca21a64bbaeb930b4526c34a633fd5f8742ed579c3b7aa1ad63e4c953e98d3651
7
- data.tar.gz: 977bea5a674e4a5a0b7eade459a18a7c7aaf1eb157c7e09bd7c13f4baf2665461e480943e8c8a509f21f399e5e9315c1c9b70d75d500bad20b7d9301af29aed6
6
+ metadata.gz: 9e129d1a7a428094755af01cb5bb98c29fb78ee9003b6938cb4335eb0e58763804a9e1c902f5d1563111a20c7b70ca69f4497f11c4cf476ed5b9ef8696ccc9ed
7
+ data.tar.gz: ffc46a4a1658c84a2fa6d2d7d6331b98164487c488112055adc9a2f20367074f26a02002d0a36ec493f9f9738ac2123f80a2e1e04fbaa5b364a54509898a2644
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.2.13"
6
+ VERSION = "0.2.14"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -166,6 +166,7 @@ module MilkTea
166
166
  module_binding = @ctx.imported_modules[module_path]
167
167
  unless module_binding
168
168
  install_fallback_builtin_type(module_path)
169
+ @ctx.imports[module_path] = empty_module_binding(module_path) unless @ctx.imports.key?(module_path)
169
170
  next
170
171
  end
171
172
 
@@ -178,6 +179,19 @@ module MilkTea
178
179
  end
179
180
  end
180
181
 
182
+ def empty_module_binding(module_path)
183
+ ModuleBinding.new(
184
+ name: module_path,
185
+ types: {}, type_declarations: {}, interfaces: {},
186
+ attributes: {}, attribute_applications: {},
187
+ values: {}, functions: {}, methods: {},
188
+ implemented_interfaces: {}, imports: {},
189
+ private_types: {}, private_interfaces: {}, private_attributes: {},
190
+ private_values: {}, private_functions: {}, private_methods: {},
191
+ private_implemented_interfaces: {},
192
+ )
193
+ end
194
+
181
195
  def install_fallback_builtin_type(module_path)
182
196
  case module_path
183
197
  when "std.option"
@@ -742,7 +742,7 @@ module MilkTea
742
742
  end
743
743
 
744
744
  def method_dispatch_receiver_type_for_completion(receiver_type)
745
- return receiver_type.definition if receiver_type.is_a?(Types::StructInstance)
745
+ return receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
746
746
 
747
747
  if receiver_type.is_a?(Types::Nullable)
748
748
  dispatch_base_type = method_dispatch_receiver_type_for_completion(receiver_type.base)
@@ -765,7 +765,7 @@ module MilkTea
765
765
 
766
766
  def project_field_receiver_type_for_completion(type, facts = nil)
767
767
  type = project_receiver_type_for_completion(type)
768
- return type.definition if type.is_a?(Types::StructInstance)
768
+ return type.definition if type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
769
769
  return field_type_from_struct_definition(type, facts) if facts
770
770
 
771
771
  type
@@ -792,7 +792,7 @@ module MilkTea
792
792
 
793
793
  def field_owner_type(receiver_type, facts = nil)
794
794
  aggregate_type = project_field_receiver_type_for_completion(receiver_type, facts)
795
- return aggregate_type.definition if aggregate_type.is_a?(Types::StructInstance)
795
+ return aggregate_type.definition if aggregate_type.is_a?(Types::StructInstance) || aggregate_type.is_a?(Types::VariantInstance)
796
796
 
797
797
  aggregate_type
798
798
  end
@@ -704,7 +704,7 @@ module MilkTea
704
704
  end
705
705
 
706
706
  def interface_receiver_definition_location(current_uri, receiver_type)
707
- receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance)
707
+ receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
708
708
 
709
709
  if receiver_type.module_name.nil? || receiver_type.module_name.empty?
710
710
  token = local_type_definition_token(current_uri, receiver_type.name)
@@ -716,7 +716,7 @@ module MilkTea
716
716
  end
717
717
 
718
718
  def receiver_module_name(receiver_type)
719
- receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance)
719
+ receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance) || receiver_type.is_a?(Types::VariantInstance)
720
720
 
721
721
  receiver_type.module_name
722
722
  end
@@ -285,6 +285,7 @@ module MilkTea
285
285
  'subscribe' => 'function subscribe(listener) -> Result[Subscription, EventError]',
286
286
  'subscribe_once' => 'function subscribe_once(listener) -> Result[Subscription, EventError]',
287
287
  'unsubscribe' => 'function unsubscribe(subscription) -> bool',
288
+ 'emit' => 'function emit() / function emit(payload)',
288
289
  'wait' => 'function wait() -> Task[Result[T, EventError]]',
289
290
  },
290
291
  'str_buffer' => {
@@ -313,21 +314,23 @@ module MilkTea
313
314
  token = context&.fetch(:token, nil)
314
315
  token_kind = token&.type || :none
315
316
  unless token&.type == :identifier
316
- info = builtin_keyword_hover_info(token) ||
317
- BUILTIN_CALL_HOVER_INFO[token.lexeme]&.slice(:signature, :docs)
318
- if info
319
- result_state = 'hit'
320
- return {
321
- contents: {
322
- kind: 'markdown',
323
- value: render_hover_markdown(info)
324
- },
325
- range: token_to_range(token)
326
- }
327
- end
317
+ unless member_access_token?(context)
318
+ info = builtin_keyword_hover_info(token) ||
319
+ BUILTIN_CALL_HOVER_INFO[token.lexeme]&.slice(:signature, :docs)
320
+ if info
321
+ result_state = 'hit'
322
+ return {
323
+ contents: {
324
+ kind: 'markdown',
325
+ value: render_hover_markdown(info)
326
+ },
327
+ range: token_to_range(token)
328
+ }
329
+ end
328
330
 
329
- result_state = 'not-identifier'
330
- return nil
331
+ result_state = 'not-identifier'
332
+ return nil
333
+ end
331
334
  end
332
335
 
333
336
  info = resolve_hover_info(uri, lsp_line, lsp_char, token: token, tokens: context[:tokens], token_index: context[:token_index], stages: stages)
@@ -362,10 +365,11 @@ module MilkTea
362
365
  token_index = context[:token_index]
363
366
  end
364
367
 
365
- return nil unless token&.type == :identifier
366
-
367
368
  tokens ||= @workspace.get_tokens(uri) || []
368
369
  token_index = tokens.index(token) if token_index.nil?
370
+ member_access_kw = token_index && keyword_member_access_token?(tokens, token_index)
371
+ return nil unless token&.type == :identifier || member_access_kw
372
+
369
373
  if token_index
370
374
  module_info = module_declaration_info_at(tokens, token_index)
371
375
  if module_info
@@ -462,11 +466,11 @@ module MilkTea
462
466
  docs ||= BUILTIN_TYPE_DOCS[name]
463
467
  elsif (binding = facts.values[name])
464
468
  signature = value_hover_signature(binding)
465
- elsif (member_info = find_member_in_types(facts, name))
469
+ elsif !BUILTIN_CALL_HOVER_INFO.key?(name) && (member_info = find_member_in_types(facts, name))
466
470
  type, member, value = member_info
467
471
  signature = value ? "#{type.name}.#{member} = #{value}" : "#{type.name}.#{member}"
468
472
  resolved_via_type_member = true
469
- elsif (arm_sig = find_variant_arm_in_types(facts, name))
473
+ elsif !BUILTIN_CALL_HOVER_INFO.key?(name) && (arm_sig = find_variant_arm_in_types(facts, name))
470
474
  signature = arm_sig
471
475
  resolved_via_type_member = true
472
476
  elsif (import_binding = facts.imports[name])
@@ -522,7 +526,7 @@ module MilkTea
522
526
  if (method_binding = methods[name])
523
527
  signature = method_signature(method_binding)
524
528
  else
525
- type_base = receiver_type.to_s[/^([a-z_]+)/, 1]
529
+ type_base = receiver_type.to_s[/^([A-Za-z_]+)/, 1]
526
530
  if type_base && (method_sigs = BUILTIN_TYPE_METHOD_SIGNATURES[type_base])
527
531
  signature = method_sigs[name]
528
532
  end
@@ -550,19 +554,6 @@ module MilkTea
550
554
  end
551
555
  end
552
556
 
553
- unless signature
554
- unless token_index && tokens && (tokens[previous_non_trivia_token_index(tokens, token_index)]&.type == :dot || call_argument_token?(tokens, token_index))
555
- local_def = @workspace.find_definition_token_global(
556
- name,
557
- preferred_uri: uri,
558
- before_line: lsp_line + 1,
559
- before_char: lsp_char + 1,
560
- )
561
- if local_def
562
- signature = resolve_lexical_local_hover_signature(local_def[:uri], name, local_def[:token])
563
- end
564
- end
565
- end
566
557
  end
567
558
 
568
559
  return nil unless signature
@@ -998,9 +989,10 @@ module MilkTea
998
989
  source_location = field_definition_location(current_uri, field_receiver_type, segment[:name])
999
990
 
1000
991
  if segment[:token_index] == token_index
992
+ definition_entry = hover_definition_entry_from_location(source_location)
1001
993
  return {
1002
994
  signature: field_hover_signature(segment[:name], field_type),
1003
- docs: nil,
995
+ docs: hover_doc_comment_for_definition(definition_entry),
1004
996
  source: hover_source_label_from_location(source_location),
1005
997
  source_uri: hover_source_uri_from_location(source_location),
1006
998
  source_line: hover_source_line_from_location(source_location),
@@ -1015,7 +1007,7 @@ module MilkTea
1015
1007
  if segment[:token_index] == token_index
1016
1008
  return {
1017
1009
  signature: type_hover_signature(segment[:name], nested),
1018
- docs: nil,
1010
+ docs: BUILTIN_TYPE_DOCS[segment[:name]],
1019
1011
  }
1020
1012
  end
1021
1013
  current_type = nested
@@ -1030,10 +1022,11 @@ module MilkTea
1030
1022
 
1031
1023
  source_location = module_member_binding_location(current_uri, method_info[:module_name], segment[:name], method_info[:binding])
1032
1024
  source_location ||= module_member_definition_location(current_uri, method_info[:module_name], segment[:name])
1025
+ definition_entry = hover_definition_entry_from_location(source_location)
1033
1026
 
1034
1027
  return {
1035
1028
  signature: method_signature(method_info[:binding]),
1036
- docs: nil,
1029
+ docs: hover_doc_comment_for_definition(definition_entry),
1037
1030
  source: hover_source_label_from_location(source_location),
1038
1031
  source_uri: hover_source_uri_from_location(source_location),
1039
1032
  source_line: hover_source_line_from_location(source_location),
@@ -1511,6 +1504,22 @@ module MilkTea
1511
1504
  prev_index && tokens[prev_index].type == :dot
1512
1505
  end
1513
1506
 
1507
+ def member_access_token?(context)
1508
+ tokens = context[:tokens]
1509
+ token_index = context[:token_index]
1510
+ return false unless tokens && token_index
1511
+ prev = previous_non_trivia_token_index(tokens, token_index)
1512
+ prev && tokens[prev].type == :dot
1513
+ end
1514
+
1515
+ def keyword_member_access_token?(tokens, token_index)
1516
+ return false unless tokens && token_index
1517
+ tok = tokens[token_index]
1518
+ return false unless tok && tok.type != :identifier
1519
+ prev = previous_non_trivia_token_index(tokens, token_index)
1520
+ prev && tokens[prev].type == :dot
1521
+ end
1522
+
1514
1523
  def builtin_value_specialization_info(name, tokens, token_index)
1515
1524
  return nil unless %w[zero default reinterpret].include?(name)
1516
1525
 
@@ -44,47 +44,62 @@ module MilkTea
44
44
  while i < tokens.length - 1
45
45
  callee = tokens[i]
46
46
  next_tok = tokens[i + 1]
47
-
48
47
  prev_tok = i > 0 ? tokens[i - 1] : nil
49
48
 
50
- if callee.type == :identifier && prev_tok&.type != :function && prev_tok&.type != :dot
51
- binding = nil
52
- lparen_index = nil
49
+ binding = nil
50
+ lparen_index = nil
53
51
 
52
+ if callee.type == :identifier && prev_tok&.type != :function
53
+ # Direct function call: func(...)
54
54
  if next_tok&.type == :lparen
55
55
  binding = facts.functions[callee.lexeme]
56
+ binding ||= facts.imports.each_value.find { |mod| mod.functions.key?(callee.lexeme) }&.functions&.dig(callee.lexeme)
56
57
  lparen_index = i + 1
57
58
  elsif next_tok&.type == :dot
58
59
  member = tokens[i + 2]
59
60
  member_lparen = tokens[i + 3]
60
61
  if member&.type == :identifier && member_lparen&.type == :lparen
61
- module_binding = facts.imports[callee.lexeme]
62
- binding = module_binding&.functions&.[](member.lexeme)
62
+ # Module-qualified call: mod.func(...)
63
+ mod_binding = facts.imports[callee.lexeme]
64
+ if mod_binding
65
+ binding = mod_binding.functions[member.lexeme]
66
+ else
67
+ # Instance method call: obj.method(...) or Type.static(...)
68
+ receiver_type = resolve_dot_receiver_value_type(facts, callee.lexeme, callee.line, callee.column)
69
+ receiver_type ||= facts.types[callee.lexeme]
70
+ unless receiver_type
71
+ receiver_type = facts.imports.each_value.find { |mod| mod.types.key?(callee.lexeme) }&.types&.dig(callee.lexeme)
72
+ end
73
+ if receiver_type
74
+ methods = methods_for_receiver_type(facts, receiver_type)
75
+ binding = methods[member.lexeme] || methods["static:#{member.lexeme}"]
76
+ end
77
+ end
63
78
  lparen_index = i + 3
64
79
  end
65
80
  end
81
+ end
66
82
 
67
- if binding && lparen_index
68
- arg_starts, closing_index = collect_call_argument_starts(tokens, lparen_index)
69
- params_list = binding.type.params
70
-
71
- arg_starts.each_with_index do |arg_tok, index|
72
- break if index >= params_list.length
73
- next unless position_in_range?(arg_tok.line - 1, arg_tok.column - 1, start_line, start_char, end_line, end_char)
74
- next if self_describing_argument_expression?(tokens, arg_tok)
75
- param_name = params_list[index].name
76
- next if arg_tok.type == :identifier && arg_tok.lexeme == param_name
77
-
78
- hints << {
79
- position: { line: arg_tok.line - 1, character: arg_tok.column - 1 },
80
- label: "#{params_list[index].name}: ",
81
- kind: 2,
82
- paddingRight: true
83
- }
84
- end
85
-
86
- i = closing_index if closing_index
83
+ if binding && lparen_index
84
+ arg_starts, closing_index = collect_call_argument_starts(tokens, lparen_index)
85
+ params_list = binding.respond_to?(:type) ? binding.type.params : []
86
+
87
+ arg_starts.each_with_index do |arg_tok, index|
88
+ break if index >= params_list.length
89
+ next unless position_in_range?(arg_tok.line - 1, arg_tok.column - 1, start_line, start_char, end_line, end_char)
90
+ next if self_describing_argument_expression?(tokens, arg_tok)
91
+ param_name = params_list[index].name
92
+ next if arg_tok.type == :identifier && arg_tok.lexeme == param_name
93
+
94
+ hints << {
95
+ position: { line: arg_tok.line - 1, character: arg_tok.column - 1 },
96
+ label: "#{params_list[index].name}: ",
97
+ kind: 2,
98
+ paddingRight: true
99
+ }
87
100
  end
101
+
102
+ i = closing_index if closing_index
88
103
  end
89
104
 
90
105
  i += 1
@@ -121,6 +136,12 @@ module MilkTea
121
136
  next unless position_in_range?(func.line - 1, func.column - 1, start_line, start_char, end_line, end_char)
122
137
 
123
138
  binding = facts.functions[func.name]
139
+ unless binding
140
+ facts.imports.each_value do |mod|
141
+ binding = mod.functions[func.name]
142
+ break if binding
143
+ end
144
+ end
124
145
  next unless binding
125
146
 
126
147
  return_type = binding.type.return_type
@@ -311,6 +311,30 @@ module MilkTea
311
311
  unless scoped.nil?
312
312
  return scoped.map { |entry| { range: entry[:range], kind: 1 } }
313
313
  end
314
+
315
+ binding_id = begin
316
+ rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
317
+ rescue StandardError
318
+ nil
319
+ end
320
+ if binding_id
321
+ ranges = begin
322
+ scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: true)
323
+ rescue StandardError
324
+ []
325
+ end
326
+ unless ranges.empty?
327
+ return ranges.map do |range|
328
+ {
329
+ range: {
330
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
331
+ end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
332
+ },
333
+ kind: 1,
334
+ }
335
+ end
336
+ end
337
+ end
314
338
  end
315
339
 
316
340
  toks = @workspace.get_tokens(uri) || []
@@ -70,11 +70,6 @@ module MilkTea
70
70
  workspace_symbol_changes = workspace_symbol_identity_rename_changes(uri, token, lsp_line, lsp_char, new_name)
71
71
  return { changes: workspace_symbol_changes } if workspace_symbol_changes
72
72
 
73
- if (facts = @workspace.get_facts(uri))
74
- module_symbol_changes = module_level_symbol_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
75
- return { changes: module_symbol_changes } if module_symbol_changes
76
- end
77
-
78
73
  lexical_changes = lexical_rename_changes_in_document(uri, token.lexeme, new_name)
79
74
  return { changes: lexical_changes } if lexical_changes
80
75
 
@@ -202,27 +197,6 @@ module MilkTea
202
197
  { uri => edits }
203
198
  end
204
199
 
205
- def module_level_symbol_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
206
- name = token.lexeme
207
- module_level = facts.functions.key?(name) || facts.values.key?(name) || facts.types.key?(name) || facts.interfaces&.key?(name)
208
- return nil unless module_level
209
-
210
- related_uris = @workspace.related_open_document_uris(uri)
211
- changes = {}
212
- [uri, *related_uris].uniq.each do |doc_uri|
213
- edits = []
214
- doc_tokens = @workspace.get_tokens(doc_uri) || []
215
- doc_tokens.each do |tok|
216
- next unless tok.type == :identifier && tok.lexeme == name
217
- edits << { range: token_to_range(tok), newText: new_name }
218
- end
219
- changes[doc_uri] = edits unless edits.empty?
220
- end
221
-
222
- return nil if changes.empty?
223
- changes
224
- end
225
-
226
200
  def import_alias_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
227
201
  alias_name = token.lexeme
228
202
  if facts
@@ -286,22 +260,28 @@ module MilkTea
286
260
  type_name_member_access?(tokens, cursor_index, facts)
287
261
  return nil unless cursor_is_enum_member
288
262
 
289
- edits = tokens.each_with_index.filter_map do |tok, i|
290
- next unless tok.type == :identifier && tok.lexeme == token.lexeme
263
+ changes = {}
264
+ [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
265
+ doc_tokens = @workspace.get_tokens(doc_uri) || []
266
+ doc_facts = doc_uri == uri ? facts : @workspace.get_facts(doc_uri)
267
+ edits = doc_tokens.each_with_index.filter_map do |tok, i|
268
+ next unless tok.type == :identifier && tok.lexeme == token.lexeme
291
269
 
292
- enum_member_decl = variant_enum_member_declaration?(tokens, i)
293
- enum_member_access = type_name_member_access?(tokens, i, facts)
294
- next unless enum_member_decl || enum_member_access
270
+ enum_member_decl = variant_enum_member_declaration?(doc_tokens, i)
271
+ enum_member_access = type_name_member_access?(doc_tokens, i, doc_facts)
272
+ next unless enum_member_decl || enum_member_access
295
273
 
296
- {
297
- range: token_to_range(tok),
298
- newText: new_name,
299
- }
300
- end
274
+ {
275
+ range: token_to_range(tok),
276
+ newText: new_name,
277
+ }
278
+ end
301
279
 
302
- return nil if edits.empty?
280
+ changes[doc_uri] = edits unless edits.empty?
281
+ end
303
282
 
304
- { uri => edits }
283
+ return nil if changes.empty?
284
+ changes
305
285
  end
306
286
 
307
287
  def scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration:)
@@ -482,16 +462,28 @@ module MilkTea
482
462
  struct_type = find_struct_containing_field(facts, field_name)
483
463
  return nil unless struct_type
484
464
 
465
+ changes = collect_struct_field_changes(uri, field_name, new_name)
466
+ related_uris = @workspace.related_open_document_uris(uri)
467
+ related_uris.each do |related_uri|
468
+ next if related_uri == uri
469
+ related_changes = collect_struct_field_changes(related_uri, field_name, new_name)
470
+ changes[related_uri] = related_changes if related_changes
471
+ end
472
+
473
+ return nil if changes.empty?
474
+ changes
475
+ end
476
+
477
+ def collect_struct_field_changes(uri, field_name, new_name)
478
+ tokens = @workspace.get_tokens(uri) || []
485
479
  edits = []
486
480
  tokens.each_with_index do |tok, i|
487
481
  next unless tok.type == :identifier && tok.lexeme == field_name
488
- next unless struct_field_edit_context?(tokens, i, struct_type)
482
+ next unless struct_field_edit_context?(tokens, i, nil)
489
483
 
490
484
  edits << { range: token_to_range(tok), newText: new_name }
491
485
  end
492
-
493
- return nil if edits.empty?
494
- { uri => edits }
486
+ edits.empty? ? nil : edits
495
487
  end
496
488
 
497
489
  def struct_field_cursor_context?(tokens, index)
@@ -555,17 +547,25 @@ module MilkTea
555
547
  receiver_type = method_info[:receiver_type]
556
548
  return nil unless receiver_type
557
549
 
558
- edits = []
559
- doc_tokens = @workspace.get_tokens(uri) || []
560
- doc_tokens.each_with_index do |tok, i|
561
- next unless tok.type == :identifier && tok.lexeme == method_name
562
- next unless i > 0 && doc_tokens[i - 1].type == :dot
550
+ changes = {}
551
+ [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
552
+ edits = []
553
+ doc_tokens = @workspace.get_tokens(doc_uri) || []
554
+ doc_tokens.each_with_index do |tok, i|
555
+ next unless tok.type == :identifier && tok.lexeme == method_name
563
556
 
564
- edits << { range: token_to_range(tok), newText: new_name }
557
+ is_call = i > 0 && doc_tokens[i - 1].type == :dot
558
+ is_def = doc_uri == uri && tok.line == token.line && tok.column == token.column
559
+ next unless is_call || is_def
560
+
561
+ edits << { range: token_to_range(tok), newText: new_name }
562
+ end
563
+
564
+ changes[doc_uri] = edits unless edits.empty?
565
565
  end
566
566
 
567
- return nil if edits.empty?
568
- { uri => edits }
567
+ return nil if changes.empty?
568
+ changes
569
569
  end
570
570
 
571
571
  def find_method_at_position(uri, token, facts)
@@ -28,18 +28,25 @@ module MilkTea
28
28
  end
29
29
  end
30
30
 
31
- if (receiver_type = resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1))
31
+ if (receiver_type = resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1) ||
32
+ resolve_dot_receiver_value_type(facts, dot_recv.sub(/\[.*\]\z/, ""), lsp_line + 1, lsp_char + 1))
32
33
  methods = methods_for_receiver_type(facts, receiver_type)
33
34
  if (binding = methods[name] || methods["static:#{name}"])
34
35
  return [binding, name]
35
36
  end
36
37
  end
37
38
 
38
- if (type_receiver = resolve_type_receiver_info(facts, dot_recv, nil))
39
+ if (type_receiver = resolve_type_receiver_info(facts, dot_recv, nil) ||
40
+ resolve_type_receiver_info(facts, dot_recv.sub(/\[.*\]\z/, ""), nil))
39
41
  methods = methods_for_receiver_type(facts, type_receiver[:type])
40
42
  if (binding = methods[name] || methods["static:#{name}"])
41
43
  return [binding, name]
42
44
  end
45
+ if type_receiver[:type].respond_to?(:arms) && (arm = type_receiver[:type].arms[name])
46
+ if arm.respond_to?(:payload) && arm.payload
47
+ return [arm.payload, name]
48
+ end
49
+ end
43
50
  end
44
51
  end
45
52
 
@@ -60,7 +67,7 @@ module MilkTea
60
67
  end
61
68
 
62
69
  # 6. Type constructor (struct constructor)
63
- if (type = facts.types[name])
70
+ if (type = facts.types[name] || facts.imports.each_value.find { |mod| mod.types.key?(name) }&.types&.dig(name))
64
71
  if type.respond_to?(:fields) && !type.fields.empty?
65
72
  return [type, name]
66
73
  end
@@ -99,12 +106,13 @@ module MilkTea
99
106
  end
100
107
 
101
108
  return nil unless extending
102
- facts.types[extending.type_name]
109
+ facts.types[extending.type_name] ||
110
+ facts.imports.each_value.find { |mod| mod.types.key?(extending.type_name) }&.types&.dig(extending.type_name)
103
111
  end
104
112
 
105
113
  def build_signature_from_binding(binding, name, ctx)
106
- if binding.is_a?(Types::Struct) || binding.is_a?(Types::StructInstance)
107
- fields = binding.fields
114
+ if binding.is_a?(Types::Struct) || binding.is_a?(Types::StructInstance) || binding.is_a?(Types::VariantArmPayload)
115
+ fields = binding.is_a?(Types::StructInstance) ? binding.definition.fields : binding.fields
108
116
  params_list = fields.map { |fname, ftype| Types::Registry.parameter(fname, ftype) }
109
117
  return_type_label = binding.is_a?(Types::StructInstance) ? binding.to_s : binding.name
110
118
  elsif binding.respond_to?(:[])
@@ -120,6 +128,7 @@ module MilkTea
120
128
  activeParameter: ctx[:active_parameter],
121
129
  }
122
130
  else
131
+ return nil unless binding.respond_to?(:type) && binding.type.respond_to?(:params)
123
132
  params_list = binding.type.params
124
133
  return_type_label = binding.type.return_type
125
134
  end
@@ -6,7 +6,15 @@ module MilkTea
6
6
  module WorkspaceCaches
7
7
  def get_content(uri)
8
8
  @document_state_mutex.synchronize do
9
- @open_documents[uri] || @indexed_documents[uri] || ''
9
+ @open_documents[uri] || @indexed_documents[uri] ||
10
+ begin
11
+ path = uri_to_path(uri)
12
+ if path && File.file?(path)
13
+ @indexed_documents[uri] = File.read(path)
14
+ end
15
+ rescue StandardError
16
+ nil
17
+ end || ''
10
18
  end
11
19
  end
12
20
 
data/std/option.mt CHANGED
@@ -8,6 +8,7 @@ public variant Option[T]:
8
8
  none
9
9
 
10
10
  extending Option[T]:
11
+ ## Returns true when the option holds a value.
11
12
  public function is_some() -> bool:
12
13
  match this:
13
14
  Option.some:
@@ -15,6 +16,7 @@ extending Option[T]:
15
16
  Option.none:
16
17
  return false
17
18
 
19
+ ## Returns true when the option holds no value.
18
20
  public function is_none() -> bool:
19
21
  match this:
20
22
  Option.some:
@@ -22,6 +24,7 @@ extending Option[T]:
22
24
  Option.none:
23
25
  return true
24
26
 
27
+ ## Returns the contained value or aborts via fatal.
25
28
  public function unwrap() -> T:
26
29
  match this:
27
30
  Option.some as payload:
@@ -29,6 +32,7 @@ extending Option[T]:
29
32
  Option.none:
30
33
  fatal(c"called Option.unwrap on a none value")
31
34
 
35
+ ## Returns the contained value or aborts with the given message.
32
36
  public function expect(msg: str) -> T:
33
37
  match this:
34
38
  Option.some as payload:
@@ -36,6 +40,7 @@ extending Option[T]:
36
40
  Option.none:
37
41
  fatal(msg)
38
42
 
43
+ ## Returns the contained value or a default.
39
44
  public function unwrap_or(default: T) -> T:
40
45
  match this:
41
46
  Option.some as payload:
@@ -43,6 +48,7 @@ extending Option[T]:
43
48
  Option.none:
44
49
  return default
45
50
 
51
+ ## Returns the contained value or calls a fallback closure.
46
52
  public function unwrap_or_else(f: proc() -> T) -> T:
47
53
  match this:
48
54
  Option.some as payload:
data/std/result.mt CHANGED
@@ -11,6 +11,7 @@ public variant Result[T, E]:
11
11
  failure(error: E)
12
12
 
13
13
  extending Result[T, E]:
14
+ ## Returns true when the result holds a success value.
14
15
  public function is_success() -> bool:
15
16
  match this:
16
17
  Result.success:
@@ -18,6 +19,7 @@ extending Result[T, E]:
18
19
  Result.failure:
19
20
  return false
20
21
 
22
+ ## Returns true when the result holds a failure value.
21
23
  public function is_failure() -> bool:
22
24
  match this:
23
25
  Result.success:
@@ -25,6 +27,7 @@ extending Result[T, E]:
25
27
  Result.failure:
26
28
  return true
27
29
 
30
+ ## Returns the success value or aborts via fatal.
28
31
  public function unwrap() -> T:
29
32
  match this:
30
33
  Result.success as payload:
@@ -32,6 +35,7 @@ extending Result[T, E]:
32
35
  Result.failure:
33
36
  fatal(c"called Result.unwrap on a failure value")
34
37
 
38
+ ## Returns the success value or aborts with the given message.
35
39
  public function expect(msg: str) -> T:
36
40
  match this:
37
41
  Result.success as payload:
@@ -39,6 +43,7 @@ extending Result[T, E]:
39
43
  Result.failure:
40
44
  fatal(msg)
41
45
 
46
+ ## Returns the failure error value or aborts via fatal.
42
47
  public function unwrap_error() -> E:
43
48
  match this:
44
49
  Result.success:
@@ -46,6 +51,7 @@ extending Result[T, E]:
46
51
  Result.failure as payload:
47
52
  return payload.error
48
53
 
54
+ ## Returns the failure error value or aborts with the given message.
49
55
  public function expect_error(msg: str) -> E:
50
56
  match this:
51
57
  Result.success:
@@ -53,6 +59,7 @@ extending Result[T, E]:
53
59
  Result.failure as payload:
54
60
  return payload.error
55
61
 
62
+ ## Returns the success value or a default.
56
63
  public function unwrap_or(default: T) -> T:
57
64
  match this:
58
65
  Result.success as payload:
@@ -60,6 +67,7 @@ extending Result[T, E]:
60
67
  Result.failure:
61
68
  return default
62
69
 
70
+ ## Returns the success value or calls a fallback closure with the error.
63
71
  public function unwrap_or_else(f: proc(error: E) -> T) -> T:
64
72
  match this:
65
73
  Result.success as payload:
@@ -67,6 +75,7 @@ extending Result[T, E]:
67
75
  Result.failure as payload:
68
76
  return f(error=payload.error)
69
77
 
78
+ ## Maps the error through a closure while preserving the success value.
70
79
  public function map_error[F](f: proc(error: E) -> F) -> Result[T, F]:
71
80
  match this:
72
81
  Result.success as payload:
@@ -74,6 +83,7 @@ extending Result[T, E]:
74
83
  Result.failure as payload:
75
84
  return Result[T, F].failure(error = f(payload.error))
76
85
 
86
+ ## Converts the result into an Option, discarding the error.
77
87
  public function ok() -> Option[T]:
78
88
  match this:
79
89
  Result.success as payload:
@@ -81,6 +91,7 @@ extending Result[T, E]:
81
91
  Result.failure:
82
92
  return Option[T].none
83
93
 
94
+ ## Converts the failure into an Option, discarding the success value.
84
95
  public function error() -> Option[E]:
85
96
  match this:
86
97
  Result.success:
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mt-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.13
4
+ version: 0.2.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Long (Teefan) Tran
@@ -583,7 +583,7 @@ metadata:
583
583
  homepage_uri: https://teefan.github.io/mt-lang/
584
584
  source_code_uri: https://github.com/teefan/mt-lang
585
585
  post_install_message: |
586
- Milk Tea 0.2.13 installed!
586
+ Milk Tea 0.2.14 installed!
587
587
 
588
588
  System requirements:
589
589
  - A C compiler (gcc or clang) must be available on PATH