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.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -4,205 +4,203 @@ module MilkTea
4
4
  module LSP
5
5
  class Server
6
6
  module ServerSignatureHelp
7
- private
8
-
9
- def signature_help_bindings_for_name(facts, uri, lsp_line, lsp_char, name)
10
- # 1. Top-level functions
11
- if (binding = facts.functions[name])
12
- return [binding, name]
13
- end
14
-
15
- # 2. Imported module functions (e.g. mod.func(...))
16
- facts.imports.each_value do |mod|
17
- if (binding = mod.functions[name])
7
+ def signature_help_bindings_for_name(facts, uri, lsp_line, lsp_char, name)
8
+ # 1. Top-level functions
9
+ if (binding = facts.functions[name])
18
10
  return [binding, name]
19
11
  end
20
- end
21
12
 
22
- # 3. Methods — find via dot-receiver resolution
23
- dot_recv = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
24
- if dot_recv && name
25
- if (mod_binding = facts.imports[dot_recv])
26
- if (binding = mod_binding.functions[name])
13
+ # 2. Imported module functions (e.g. mod.func(...))
14
+ facts.imports.each_value do |mod|
15
+ if (binding = mod.functions[name])
27
16
  return [binding, name]
28
17
  end
29
18
  end
30
19
 
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))
33
- methods = methods_for_receiver_type(facts, receiver_type)
34
- if (binding = methods[name] || methods["static:#{name}"])
35
- return [binding, name]
20
+ # 3. Methods — find via dot-receiver resolution
21
+ dot_recv = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
22
+ if dot_recv && name
23
+ if (mod_binding = facts.imports[dot_recv])
24
+ if (binding = mod_binding.functions[name])
25
+ return [binding, name]
26
+ end
27
+ end
28
+
29
+ if (receiver_type = resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1) ||
30
+ resolve_dot_receiver_value_type(facts, dot_recv.sub(/\[.*\]\z/, ""), lsp_line + 1, lsp_char + 1))
31
+ methods = methods_for_receiver_type(facts, receiver_type)
32
+ if (binding = methods[name] || methods["static:#{name}"])
33
+ return [binding, name]
34
+ end
35
+ end
36
+
37
+ if (type_receiver = resolve_type_receiver_info(facts, dot_recv, nil) ||
38
+ resolve_type_receiver_info(facts, dot_recv.sub(/\[.*\]\z/, ""), nil))
39
+ methods = methods_for_receiver_type(facts, type_receiver[:type])
40
+ if (binding = methods[name] || methods["static:#{name}"])
41
+ return [binding, name]
42
+ end
43
+ if type_receiver[:type].respond_to?(:arms) && (arm = type_receiver[:type].arms[name])
44
+ if arm.respond_to?(:payload) && arm.payload
45
+ return [arm.payload, name]
46
+ end
47
+ end
36
48
  end
37
49
  end
38
50
 
39
- if (type_receiver = resolve_type_receiver_info(facts, dot_recv, nil) ||
40
- resolve_type_receiver_info(facts, dot_recv.sub(/\[.*\]\z/, ""), nil))
41
- methods = methods_for_receiver_type(facts, type_receiver[:type])
51
+ # 4. Method name directly (e.g. method(...) on `this` in extending block)
52
+ receiver_type = resolve_this_receiver_type(facts, uri, lsp_line)
53
+ if receiver_type
54
+ methods = methods_for_receiver_type(facts, receiver_type)
42
55
  if (binding = methods[name] || methods["static:#{name}"])
43
56
  return [binding, name]
44
57
  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
58
+ end
59
+
60
+ # 5. Value with callable type (fn/proc)
61
+ if (val_binding = facts.values[name])
62
+ if val_binding.type.is_a?(Types::Function)
63
+ return [val_binding, name]
49
64
  end
50
65
  end
51
- end
52
66
 
53
- # 4. Method name directly (e.g. method(...) on `this` in extending block)
54
- receiver_type = resolve_this_receiver_type(facts, uri, lsp_line)
55
- if receiver_type
56
- methods = methods_for_receiver_type(facts, receiver_type)
57
- if (binding = methods[name] || methods["static:#{name}"])
58
- return [binding, name]
67
+ # 6. Type constructor (struct constructor)
68
+ if (type = facts.types[name] || facts.imports.each_value.find { |mod| mod.types.key?(name) }&.types&.dig(name))
69
+ if type.respond_to?(:fields) && !type.fields.empty?
70
+ return [type, name]
71
+ end
59
72
  end
60
- end
61
73
 
62
- # 5. Value with callable type (fn/proc)
63
- if (val_binding = facts.values[name])
64
- if val_binding.type.is_a?(Types::Function)
65
- return [val_binding, name]
74
+ # Resolve module-qualified type constructor (mod.Type(...))
75
+ if name.include?('.')
76
+ parts = name.split('.', 2)
77
+ if (mod_binding = facts.imports[parts[0]])
78
+ if (type = mod_binding.types[parts[1]])
79
+ if type.respond_to?(:fields) && !type.fields.empty?
80
+ return [type, name]
81
+ end
82
+ end
83
+ end
66
84
  end
67
- end
68
85
 
69
- # 6. Type constructor (struct constructor)
70
- if (type = facts.types[name] || facts.imports.each_value.find { |mod| mod.types.key?(name) }&.types&.dig(name))
71
- if type.respond_to?(:fields) && !type.fields.empty?
72
- return [type, name]
86
+ # 7. Builtin callables
87
+ if BUILTIN_CALL_HOVER_INFO.key?(name)
88
+ return [{ builtin: name }, name]
73
89
  end
90
+
91
+ nil
74
92
  end
75
93
 
76
- # Resolve module-qualified type constructor (mod.Type(...))
77
- if name.include?('.')
78
- parts = name.split('.', 2)
79
- if (mod_binding = facts.imports[parts[0]])
80
- if (type = mod_binding.types[parts[1]])
81
- if type.respond_to?(:fields) && !type.fields.empty?
82
- return [type, name]
83
- end
94
+ def resolve_this_receiver_type(facts, uri, lsp_line)
95
+ ast = @workspace.get_ast(uri)
96
+ return nil unless ast
97
+
98
+ extending = nil
99
+ each_ast_node(ast) do |node|
100
+ next unless node.is_a?(AST::ExtendingBlock)
101
+ if node.line && lsp_line + 1 >= node.line && (!node.respond_to?(:end_line) || !node.end_line || lsp_line + 1 <= node.end_line)
102
+ extending = node
84
103
  end
85
104
  end
86
- end
87
105
 
88
- # 7. Builtin callables
89
- if BUILTIN_CALL_HOVER_INFO.key?(name)
90
- return [{ builtin: name }, name]
106
+ return nil unless extending
107
+ facts.types[extending.type_name] ||
108
+ facts.imports.each_value.find { |mod| mod.types.key?(extending.type_name) }&.types&.dig(extending.type_name)
91
109
  end
92
110
 
93
- nil
94
- end
111
+ def build_signature_from_binding(binding, name, ctx)
112
+ if binding.is_a?(Types::Struct) || binding.is_a?(Types::StructInstance) || binding.is_a?(Types::VariantArmPayload)
113
+ fields = binding.is_a?(Types::StructInstance) ? binding.definition.fields : binding.fields
114
+ params_list = fields.map { |fname, ftype| Types::Registry.parameter(fname, ftype) }
115
+ return_type_label = binding.is_a?(Types::StructInstance) ? binding.to_s : binding.name
116
+ elsif binding.respond_to?(:[])
117
+ builtin_name = binding[:builtin]
118
+ info = BUILTIN_CALL_HOVER_INFO[builtin_name]
119
+ return nil unless info
120
+
121
+ label = info[:signature].sub(/^builtin /, '')
122
+ parameters = []
123
+ return {
124
+ signatures: [{ label: label, parameters: parameters }],
125
+ activeSignature: 0,
126
+ activeParameter: ctx[:active_parameter],
127
+ }
128
+ else
129
+ return nil unless binding.respond_to?(:type) && binding.type.respond_to?(:params)
130
+ params_list = binding.type.params
131
+ return_type_label = binding.type.return_type
132
+ end
95
133
 
96
- def resolve_this_receiver_type(facts, uri, lsp_line)
97
- ast = @workspace.get_ast(uri)
98
- return nil unless ast
134
+ params_str = format_params(params_list)
135
+ label = "#{name}(#{params_str}) -> #{return_type_label}"
99
136
 
100
- extending = nil
101
- each_ast_node(ast) do |node|
102
- next unless node.is_a?(AST::ExtendingBlock)
103
- if node.line && lsp_line + 1 >= node.line && (!node.respond_to?(:end_line) || !node.end_line || lsp_line + 1 <= node.end_line)
104
- extending = node
137
+ parameters = params_list.map do |parameter|
138
+ { label: "#{parameter.name}: #{parameter.type}" }
105
139
  end
106
- end
107
-
108
- return nil unless extending
109
- facts.types[extending.type_name] ||
110
- facts.imports.each_value.find { |mod| mod.types.key?(extending.type_name) }&.types&.dig(extending.type_name)
111
- end
112
140
 
113
- def build_signature_from_binding(binding, name, ctx)
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
116
- params_list = fields.map { |fname, ftype| Types::Registry.parameter(fname, ftype) }
117
- return_type_label = binding.is_a?(Types::StructInstance) ? binding.to_s : binding.name
118
- elsif binding.respond_to?(:[])
119
- builtin_name = binding[:builtin]
120
- info = BUILTIN_CALL_HOVER_INFO[builtin_name]
121
- return nil unless info
122
-
123
- label = info[:signature].sub(/^builtin /, '')
124
- parameters = []
125
- return {
141
+ {
126
142
  signatures: [{ label: label, parameters: parameters }],
127
143
  activeSignature: 0,
128
144
  activeParameter: ctx[:active_parameter],
129
145
  }
130
- else
131
- return nil unless binding.respond_to?(:type) && binding.type.respond_to?(:params)
132
- params_list = binding.type.params
133
- return_type_label = binding.type.return_type
134
- end
135
-
136
- params_str = format_params(params_list)
137
- label = "#{name}(#{params_str}) -> #{return_type_label}"
138
-
139
- parameters = params_list.map do |parameter|
140
- { label: "#{parameter.name}: #{parameter.type}" }
141
146
  end
142
147
 
143
- {
144
- signatures: [{ label: label, parameters: parameters }],
145
- activeSignature: 0,
146
- activeParameter: ctx[:active_parameter],
147
- }
148
- end
148
+ def handle_signature_help(params)
149
+ stages = new_perf_stages
150
+ total_start = stages ? monotonic_time : nil
151
+ uri = params['textDocument']['uri']
152
+ lsp_line = params['position']['line']
153
+ lsp_char = params['position']['character']
154
+ result_state = 'miss'
149
155
 
150
- def handle_signature_help(params)
151
- stages = new_perf_stages
152
- total_start = stages ? monotonic_time : nil
153
- uri = params['textDocument']['uri']
154
- lsp_line = params['position']['line']
155
- lsp_char = params['position']['character']
156
- result_state = 'miss'
156
+ ctx = measure_perf_stage(stages, 'call_context') { @workspace.find_call_context(uri, lsp_line, lsp_char) }
157
+ return nil unless ctx
157
158
 
158
- ctx = measure_perf_stage(stages, 'call_context') { @workspace.find_call_context(uri, lsp_line, lsp_char) }
159
- return nil unless ctx
159
+ facts = measure_perf_stage(stages, 'facts') do
160
+ @workspace.get_facts(uri, allow_last_good_fallback: allow_hover_last_good_fallback?(uri))
161
+ end
162
+ return nil unless facts
160
163
 
161
- facts = measure_perf_stage(stages, 'facts') do
162
- @workspace.get_facts(uri, allow_last_good_fallback: allow_hover_last_good_fallback?(uri))
163
- end
164
- return nil unless facts
164
+ binding_info = measure_perf_stage(stages, 'binding') do
165
+ signature_help_bindings_for_name(facts, uri, lsp_line, lsp_char, ctx[:name])
166
+ end
167
+ return nil unless binding_info
165
168
 
166
- binding_info = measure_perf_stage(stages, 'binding') do
167
- signature_help_bindings_for_name(facts, uri, lsp_line, lsp_char, ctx[:name])
168
- end
169
- return nil unless binding_info
169
+ binding, display_name = binding_info
170
170
 
171
- binding, display_name = binding_info
171
+ doc_comment = measure_perf_stage(stages, 'docs') do
172
+ signature_help_doc_comment_for_call(uri, ctx[:name], lsp_line, lsp_char)
173
+ end
172
174
 
173
- doc_comment = measure_perf_stage(stages, 'docs') do
174
- signature_help_doc_comment_for_call(uri, ctx[:name], lsp_line, lsp_char)
175
- end
175
+ result = measure_perf_stage(stages, 'build') do
176
+ sig = build_signature_from_binding(binding, display_name, ctx)
176
177
 
177
- result = measure_perf_stage(stages, 'build') do
178
- sig = build_signature_from_binding(binding, display_name, ctx)
178
+ if sig && sig[:signatures] && (sig_entry = sig[:signatures].first)
179
+ param_docs = doc_tag_param_descriptions(doc_comment)
180
+ sig_entry[:parameters]&.each do |param|
181
+ param_name = param[:label].to_s.split(':').first&.strip
182
+ if param_name && param_docs.key?(param_name)
183
+ param[:documentation] = { kind: 'markdown', value: param_docs[param_name] }
184
+ end
185
+ end
179
186
 
180
- if sig && sig[:signatures] && (sig_entry = sig[:signatures].first)
181
- param_docs = doc_tag_param_descriptions(doc_comment)
182
- sig_entry[:parameters]&.each do |param|
183
- param_name = param[:label].to_s.split(':').first&.strip
184
- if param_name && param_docs.key?(param_name)
185
- param[:documentation] = { kind: 'markdown', value: param_docs[param_name] }
187
+ signature_docs = signature_help_markdown_for_doc_comment(doc_comment)
188
+ unless signature_docs.empty?
189
+ sig_entry[:documentation] = { kind: 'markdown', value: signature_docs }
186
190
  end
187
191
  end
188
192
 
189
- signature_docs = signature_help_markdown_for_doc_comment(doc_comment)
190
- unless signature_docs.empty?
191
- sig_entry[:documentation] = { kind: 'markdown', value: signature_docs }
192
- end
193
+ sig
193
194
  end
194
-
195
- sig
195
+ result_state = 'hit' if result
196
+ result
197
+ rescue StandardError => e
198
+ result_state = 'error'
199
+ warn "Error in signatureHelp handler: #{e.message}"
200
+ nil
201
+ ensure
202
+ log_request_stage_breakdown('textDocument/signatureHelp', total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
196
203
  end
197
- result_state = 'hit' if result
198
- result
199
- rescue StandardError => e
200
- result_state = 'error'
201
- warn "Error in signatureHelp handler: #{e.message}"
202
- nil
203
- ensure
204
- log_request_stage_breakdown('textDocument/signatureHelp', total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
205
- end
206
204
  end
207
205
  end
208
206
  end
@@ -4,121 +4,119 @@ module MilkTea
4
4
  module LSP
5
5
  class Server
6
6
  module ServerTextDocuments
7
- private
8
-
9
- def handle_did_open(params)
10
- total_start = monotonic_time
11
- uri = params['textDocument']['uri']
12
- content = params['textDocument']['text']
13
- source = @workspace.document_source(uri) || 'unknown'
14
- open_start = monotonic_time
15
- open_stats = @workspace.open_document(uri, content)
16
- open_ms = elapsed_ms(open_start)
17
- @semantic_tokens_cache.delete(uri)
18
- @semantic_tokens_delta_cache.delete(uri)
19
- @fixall_cache.delete(uri)
20
- diagnostics_start = monotonic_time
21
- schedule_diagnostics(uri, lint_tier: :full) unless @workspace.background_document?(uri)
22
-
23
- elapsed = elapsed_ms(total_start)
24
- short_uri = shorten_uri(uri) || uri
25
- facts_detail = if open_stats[:eager_facts]
26
- "on(#{open_stats[:facts_mode] || :unknown})"
27
- else
28
- "off(#{open_stats[:skip_reason] || :unknown})"
29
- end
30
- log_perf_breakdown(
31
- 'textDocument/didOpen',
32
- elapsed,
33
- "uri=#{short_uri} source=#{source} bytes=#{open_stats[:bytes]} lines=#{open_stats[:lines]} imports=#{open_stats[:import_count]} shared_modules=#{open_stats[:shared_module_cache_size]} eager_facts=#{facts_detail} stages_ms=open:#{open_ms},facts:#{open_stats[:facts_ms] || 0.0},diagnostics_enqueue:#{elapsed_ms(diagnostics_start)}"
34
- )
35
- nil
36
- end
37
-
38
- def handle_did_change(params)
39
- uri = params['textDocument']['uri']
40
- changes = params['contentChanges'] || []
41
- previous_content = @workspace.get_content(uri)
42
-
43
- @workspace.apply_incremental_changes(uri, changes)
44
-
45
- invalidate_document_caches(uri)
46
- current_content = @workspace.get_content(uri)
47
- refresh_open_document_dependency_state(uri, previous_content: previous_content, current_content: current_content)
48
- schedule_diagnostics(uri, lint_tier: :fast) unless @workspace.background_document?(uri)
49
- nil
50
- end
51
-
52
- def handle_document_context(params)
53
- uri = params.dig('textDocument', 'uri') || params['uri']
54
- source = params['source']
55
- return nil unless uri && source
56
-
57
- previous_source = @workspace.set_document_source(uri, source)
58
- if previous_source == 'background-document' && source != 'background-document' && !@workspace.get_content(uri).empty?
7
+ def handle_did_open(params)
8
+ total_start = monotonic_time
9
+ uri = params['textDocument']['uri']
10
+ content = params['textDocument']['text']
11
+ source = @workspace.document_source(uri) || 'unknown'
12
+ open_start = monotonic_time
13
+ open_stats = @workspace.open_document(uri, content)
14
+ open_ms = elapsed_ms(open_start)
59
15
  @semantic_tokens_cache.delete(uri)
60
16
  @semantic_tokens_delta_cache.delete(uri)
61
17
  @fixall_cache.delete(uri)
62
- schedule_diagnostics(uri, force: true, lint_tier: :full)
18
+ diagnostics_start = monotonic_time
19
+ schedule_diagnostics(uri, lint_tier: :full) unless @workspace.background_document?(uri)
20
+
21
+ elapsed = elapsed_ms(total_start)
22
+ short_uri = shorten_uri(uri) || uri
23
+ facts_detail = if open_stats[:eager_facts]
24
+ "on(#{open_stats[:facts_mode] || :unknown})"
25
+ else
26
+ "off(#{open_stats[:skip_reason] || :unknown})"
27
+ end
28
+ log_perf_breakdown(
29
+ 'textDocument/didOpen',
30
+ elapsed,
31
+ "uri=#{short_uri} source=#{source} bytes=#{open_stats[:bytes]} lines=#{open_stats[:lines]} imports=#{open_stats[:import_count]} shared_modules=#{open_stats[:shared_module_cache_size]} eager_facts=#{facts_detail} stages_ms=open:#{open_ms},facts:#{open_stats[:facts_ms] || 0.0},diagnostics_enqueue:#{elapsed_ms(diagnostics_start)}"
32
+ )
33
+ nil
63
34
  end
64
35
 
65
- nil
66
- end
67
-
68
- def handle_did_close(params)
69
- uri = params['textDocument']['uri']
70
- cancel_diagnostics(uri)
71
- @workspace.close_document(uri)
72
- invalidate_document_caches(uri)
73
- @diagnostic_report_cache.delete(uri)
74
- @workspace_diagnostic_cache.delete(uri)
75
- refresh_open_document_dependency_state(uri)
76
- unless defined?(@pull_diagnostics_active) && @pull_diagnostics_active
77
- @protocol.write_notification('textDocument/publishDiagnostics', {
78
- uri: uri,
79
- diagnostics: []
80
- })
81
- end
82
- nil
83
- end
36
+ def handle_did_change(params)
37
+ uri = params['textDocument']['uri']
38
+ changes = params['contentChanges'] || []
39
+ previous_content = @workspace.get_content(uri)
84
40
 
85
- def handle_did_save(params)
86
- uri = params.dig('textDocument', 'uri')
87
- return nil unless uri
41
+ @workspace.apply_incremental_changes(uri, changes)
88
42
 
89
- invalidate_document_caches(uri)
90
- affected = refresh_open_document_dependency_state(uri)
91
- refresh_client_semantic_tokens if affected.any?
92
- schedule_diagnostics(uri, force: true, lint_tier: :full) unless @workspace.background_document?(uri)
93
- nil
94
- end
43
+ invalidate_document_caches(uri)
44
+ current_content = @workspace.get_content(uri)
45
+ refresh_open_document_dependency_state(uri, previous_content: previous_content, current_content: current_content)
46
+ schedule_diagnostics(uri, lint_tier: :fast) unless @workspace.background_document?(uri)
47
+ nil
48
+ end
95
49
 
96
- def handle_will_save_wait_until(params)
97
- uri = params.dig('textDocument', 'uri')
98
- return nil unless uri
50
+ def handle_document_context(params)
51
+ uri = params.dig('textDocument', 'uri') || params['uri']
52
+ source = params['source']
53
+ return nil unless uri && source
54
+
55
+ previous_source = @workspace.set_document_source(uri, source)
56
+ if previous_source == 'background-document' && source != 'background-document' && !@workspace.get_content(uri).empty?
57
+ @semantic_tokens_cache.delete(uri)
58
+ @semantic_tokens_delta_cache.delete(uri)
59
+ @fixall_cache.delete(uri)
60
+ schedule_diagnostics(uri, force: true, lint_tier: :full)
61
+ end
99
62
 
100
- content = @workspace.get_content(uri)
101
- return nil if content.nil? || content.empty?
102
- return nil if skip_expensive_work_reason(uri, content)
63
+ nil
64
+ end
103
65
 
104
- begin
105
- formatted = Timeout.timeout(0.5) do
106
- Formatter.format_source(content, path: uri, mode: @format_mode)
66
+ def handle_did_close(params)
67
+ uri = params['textDocument']['uri']
68
+ cancel_diagnostics(uri)
69
+ @workspace.close_document(uri)
70
+ invalidate_document_caches(uri)
71
+ @diagnostic_report_cache.delete(uri)
72
+ @workspace_diagnostic_cache.delete(uri)
73
+ refresh_open_document_dependency_state(uri)
74
+ unless defined?(@pull_diagnostics_active) && @pull_diagnostics_active
75
+ @protocol.write_notification('textDocument/publishDiagnostics', {
76
+ uri: uri,
77
+ diagnostics: []
78
+ })
107
79
  end
108
- return nil if formatted == content
80
+ nil
81
+ end
82
+
83
+ def handle_did_save(params)
84
+ uri = params.dig('textDocument', 'uri')
85
+ return nil unless uri
109
86
 
110
- line_count = content.count("\n")
111
- [{
112
- range: {
113
- start: { line: 0, character: 0 },
114
- end: { line: line_count + 1, character: 0 }
115
- },
116
- newText: formatted
117
- }]
118
- rescue Timeout::Error, StandardError
87
+ invalidate_document_caches(uri)
88
+ affected = refresh_open_document_dependency_state(uri)
89
+ refresh_client_semantic_tokens if affected.any?
90
+ schedule_diagnostics(uri, force: true, lint_tier: :full) unless @workspace.background_document?(uri)
119
91
  nil
120
92
  end
121
- end
93
+
94
+ def handle_will_save_wait_until(params)
95
+ uri = params.dig('textDocument', 'uri')
96
+ return nil unless uri
97
+
98
+ content = @workspace.get_content(uri)
99
+ return nil if content.nil? || content.empty?
100
+ return nil if skip_expensive_work_reason(uri, content)
101
+
102
+ begin
103
+ formatted = Timeout.timeout(0.5) do
104
+ Formatter.format_source(content, path: uri, mode: @format_mode)
105
+ end
106
+ return nil if formatted == content
107
+
108
+ line_count = content.count("\n")
109
+ [{
110
+ range: {
111
+ start: { line: 0, character: 0 },
112
+ end: { line: line_count + 1, character: 0 }
113
+ },
114
+ newText: formatted
115
+ }]
116
+ rescue Timeout::Error, StandardError
117
+ nil
118
+ end
119
+ end
122
120
  end
123
121
  end
124
122
  end
@@ -4,8 +4,6 @@ module MilkTea
4
4
  module LSP
5
5
  class Server
6
6
  module ServerTypeHierarchy
7
- private
8
-
9
7
  TYPE_KIND_MAP = {
10
8
  struct: 22,
11
9
  enum: 13,
@@ -117,12 +115,12 @@ module MilkTea
117
115
  def find_type_info(facts, name)
118
116
  if (t = facts.types[name])
119
117
  ast = t.respond_to?(:ast_declaration) ? t.ast_declaration : nil
120
- return { kind: type_kind_from_object(t), line: ast&.line, column: ast.respond_to?(:column) ? ast.column : 0 }
118
+ return { kind: type_kind_from_object(t), line: ast&.line, column: ast.column || 0 }
121
119
  end
122
120
 
123
121
  if (t = facts.interfaces[name])
124
122
  ast = t.respond_to?(:ast_declaration) ? t.ast_declaration : nil
125
- return { kind: :interface, line: ast&.line, column: ast.respond_to?(:column) ? ast.column : 0 }
123
+ return { kind: :interface, line: ast&.line, column: ast.column || 0 }
126
124
  end
127
125
 
128
126
  nil