mt-lang 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
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])
18
+ return [binding, name]
19
+ end
20
+ end
21
+
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])
27
+ return [binding, name]
28
+ end
29
+ end
30
+
31
+ if (receiver_type = resolve_dot_receiver_value_type(facts, dot_recv, lsp_line + 1, lsp_char + 1))
32
+ methods = methods_for_receiver_type(facts, receiver_type)
33
+ if (binding = methods[name] || methods["static:#{name}"])
34
+ return [binding, name]
35
+ end
36
+ end
37
+
38
+ if (type_receiver = resolve_type_receiver_info(facts, dot_recv, 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
+ end
44
+ end
45
+
46
+ # 4. Method name directly (e.g. method(...) on `this` in extending block)
47
+ receiver_type = resolve_this_receiver_type(facts, uri, lsp_line)
48
+ if receiver_type
49
+ methods = methods_for_receiver_type(facts, receiver_type)
50
+ if (binding = methods[name] || methods["static:#{name}"])
51
+ return [binding, name]
52
+ end
53
+ end
54
+
55
+ # 5. Value with callable type (fn/proc)
56
+ if (val_binding = facts.values[name])
57
+ if val_binding.type.is_a?(Types::Function)
58
+ return [val_binding, name]
59
+ end
60
+ end
61
+
62
+ # 6. Type constructor (struct constructor)
63
+ if (type = facts.types[name])
64
+ if type.respond_to?(:fields) && !type.fields.empty?
65
+ return [type, name]
66
+ end
67
+ end
68
+
69
+ # Resolve module-qualified type constructor (mod.Type(...))
70
+ if name.include?('.')
71
+ parts = name.split('.', 2)
72
+ if (mod_binding = facts.imports[parts[0]])
73
+ if (type = mod_binding.types[parts[1]])
74
+ if type.respond_to?(:fields) && !type.fields.empty?
75
+ return [type, name]
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # 7. Builtin callables
82
+ if BUILTIN_CALL_HOVER_INFO.key?(name)
83
+ return [{ builtin: name }, name]
84
+ end
85
+
86
+ nil
87
+ end
88
+
89
+ def resolve_this_receiver_type(facts, uri, lsp_line)
90
+ ast = @workspace.get_ast(uri)
91
+ return nil unless ast
92
+
93
+ extending = nil
94
+ each_ast_node(ast) do |node|
95
+ next unless node.is_a?(AST::ExtendingBlock)
96
+ if node.line && node.end_line && lsp_line + 1 >= node.line && lsp_line + 1 <= node.end_line
97
+ extending = node
98
+ end
99
+ end
100
+
101
+ return nil unless extending
102
+ facts.types[extending.type_name]
103
+ end
104
+
105
+ def build_signature_from_binding(binding, name, ctx)
106
+ if binding.is_a?(Types::Struct) || binding.is_a?(Types::StructInstance)
107
+ fields = binding.fields
108
+ params_list = fields.map { |fname, ftype| Types::Registry.parameter(fname, ftype) }
109
+ return_type_label = binding.is_a?(Types::StructInstance) ? binding.to_s : binding.name
110
+ elsif binding.respond_to?(:[])
111
+ builtin_name = binding[:builtin]
112
+ info = BUILTIN_CALL_HOVER_INFO[builtin_name]
113
+ return nil unless info
114
+
115
+ label = info[:signature].sub(/^builtin /, '')
116
+ parameters = []
117
+ return {
118
+ signatures: [{ label: label, parameters: parameters }],
119
+ activeSignature: 0,
120
+ activeParameter: ctx[:active_parameter],
121
+ }
122
+ else
123
+ params_list = binding.type.params
124
+ return_type_label = binding.type.return_type
125
+ end
126
+
127
+ params_str = format_params(params_list)
128
+ label = "#{name}(#{params_str}) -> #{return_type_label}"
129
+
130
+ parameters = params_list.map do |parameter|
131
+ { label: "#{parameter.name}: #{parameter.type}" }
132
+ end
133
+
134
+ {
135
+ signatures: [{ label: label, parameters: parameters }],
136
+ activeSignature: 0,
137
+ activeParameter: ctx[:active_parameter],
138
+ }
139
+ end
140
+
141
+ def handle_signature_help(params)
142
+ stages = new_perf_stages
143
+ total_start = stages ? monotonic_time : nil
144
+ uri = params['textDocument']['uri']
145
+ lsp_line = params['position']['line']
146
+ lsp_char = params['position']['character']
147
+ result_state = 'miss'
148
+
149
+ ctx = measure_perf_stage(stages, 'call_context') { @workspace.find_call_context(uri, lsp_line, lsp_char) }
150
+ return nil unless ctx
151
+
152
+ facts = measure_perf_stage(stages, 'facts') do
153
+ @workspace.get_facts(uri, allow_last_good_fallback: allow_hover_last_good_fallback?(uri))
154
+ end
155
+ return nil unless facts
156
+
157
+ binding_info = measure_perf_stage(stages, 'binding') do
158
+ signature_help_bindings_for_name(facts, uri, lsp_line, lsp_char, ctx[:name])
159
+ end
160
+ return nil unless binding_info
161
+
162
+ binding, display_name = binding_info
163
+
164
+ doc_comment = measure_perf_stage(stages, 'docs') do
165
+ signature_help_doc_comment_for_call(uri, ctx[:name], lsp_line, lsp_char)
166
+ end
167
+
168
+ result = measure_perf_stage(stages, 'build') do
169
+ sig = build_signature_from_binding(binding, display_name, ctx)
170
+
171
+ if sig && sig[:signatures] && (sig_entry = sig[:signatures].first)
172
+ param_docs = doc_tag_param_descriptions(doc_comment)
173
+ sig_entry[:parameters]&.each do |param|
174
+ param_name = param[:label].to_s.split(':').first&.strip
175
+ if param_name && param_docs.key?(param_name)
176
+ param[:documentation] = { kind: 'markdown', value: param_docs[param_name] }
177
+ end
178
+ end
179
+
180
+ signature_docs = signature_help_markdown_for_doc_comment(doc_comment)
181
+ unless signature_docs.empty?
182
+ sig_entry[:documentation] = { kind: 'markdown', value: signature_docs }
183
+ end
184
+ end
185
+
186
+ sig
187
+ end
188
+ result_state = 'hit' if result
189
+ result
190
+ rescue StandardError => e
191
+ result_state = 'error'
192
+ warn "Error in signatureHelp handler: #{e.message}"
193
+ nil
194
+ ensure
195
+ log_request_stage_breakdown('textDocument/signatureHelp', total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
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?
59
+ @semantic_tokens_cache.delete(uri)
60
+ @semantic_tokens_delta_cache.delete(uri)
61
+ @fixall_cache.delete(uri)
62
+ schedule_diagnostics(uri, force: true, lint_tier: :full)
63
+ end
64
+
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
84
+
85
+ def handle_did_save(params)
86
+ uri = params.dig('textDocument', 'uri')
87
+ return nil unless uri
88
+
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
95
+
96
+ def handle_will_save_wait_until(params)
97
+ uri = params.dig('textDocument', 'uri')
98
+ return nil unless uri
99
+
100
+ content = @workspace.get_content(uri)
101
+ return nil if content.nil? || content.empty?
102
+ return nil if skip_expensive_work_reason(uri, content)
103
+
104
+ begin
105
+ formatted = Timeout.timeout(0.5) do
106
+ Formatter.format_source(content, path: uri, mode: @format_mode)
107
+ end
108
+ return nil if formatted == content
109
+
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
119
+ nil
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerTypeHierarchy
7
+ private
8
+
9
+ TYPE_KIND_MAP = {
10
+ struct: 22,
11
+ enum: 13,
12
+ flags: 13,
13
+ variant: 13,
14
+ union: 22,
15
+ interface: 11,
16
+ }.freeze
17
+
18
+ def handle_prepare_type_hierarchy(params)
19
+ text_document = params["textDocument"]
20
+ position = params["position"]
21
+ uri = text_document["uri"]
22
+ lsp_line = position["line"]
23
+ lsp_char = position["character"]
24
+
25
+ facts = @workspace.get_facts(uri)
26
+ return nil unless facts
27
+
28
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
29
+ return nil unless token
30
+ return nil unless token.type == :identifier
31
+
32
+ type_name = token.lexeme
33
+
34
+ type_entry = type_hierarchy_entry(facts, type_name, uri)
35
+ return nil unless type_entry
36
+
37
+ [type_entry]
38
+ end
39
+
40
+ def handle_supertypes(params)
41
+ item = params["item"] || params[:item]
42
+ data = item["data"] || item[:data]
43
+ return [] unless data.is_a?(Hash) || data.respond_to?(:[])
44
+
45
+ type_name = data["name"] || data[:name]
46
+ uri = data["uri"] || data[:uri]
47
+ return [] if type_name.to_s.empty?
48
+
49
+ facts = @workspace.get_facts(uri)
50
+ return [] unless facts
51
+
52
+ results = []
53
+ type_obj = facts.types[type_name]
54
+
55
+ if type_obj && facts.implemented_interfaces.key?(type_obj)
56
+ iface_names = facts.implemented_interfaces[type_obj]
57
+ iface_names.each do |iface|
58
+ entry = type_hierarchy_entry(facts, iface.name, uri)
59
+ results << entry if entry
60
+ end
61
+ end
62
+
63
+ results
64
+ end
65
+
66
+ def handle_subtypes(params)
67
+ item = params["item"] || params[:item]
68
+ data = item["data"] || item[:data]
69
+ return [] unless data.is_a?(Hash) || data.respond_to?(:[])
70
+
71
+ type_name = data["name"] || data[:name]
72
+ return [] if type_name.to_s.empty?
73
+
74
+ results = []
75
+ seen = Set.new
76
+
77
+ @workspace.all_documents.each do |doc_uri|
78
+ facts = @workspace.get_facts(doc_uri)
79
+ next unless facts
80
+
81
+ results_for = collect_subtypes(facts, type_name, doc_uri)
82
+ results_for.each do |entry|
83
+ key = [entry[:name], entry[:uri]]
84
+ next if seen.include?(key)
85
+
86
+ seen << key
87
+ results << entry
88
+ end
89
+ end
90
+
91
+ results
92
+ end
93
+
94
+ private
95
+
96
+ def type_hierarchy_entry(facts, name, uri, kind_override: nil)
97
+ type_info = find_type_info(facts, name)
98
+ return nil unless type_info
99
+
100
+ kind = kind_override || TYPE_KIND_MAP[type_info[:kind]] || 22
101
+ range = type_range(type_info)
102
+
103
+ {
104
+ name: name,
105
+ kind: kind,
106
+ uri: uri,
107
+ range: range,
108
+ selectionRange: range,
109
+ data: {
110
+ name: name,
111
+ uri: uri,
112
+ kind: type_info[:kind],
113
+ },
114
+ }
115
+ end
116
+
117
+ def find_type_info(facts, name)
118
+ if (t = facts.types[name])
119
+ 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 }
121
+ end
122
+
123
+ if (t = facts.interfaces[name])
124
+ 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 }
126
+ end
127
+
128
+ nil
129
+ end
130
+
131
+ def type_kind_from_object(type)
132
+ case type
133
+ when Types::Struct then type.is_a?(Types::Union) ? :union : :struct
134
+ when Types::Variant then :variant
135
+ when Types::EnumBase then type.respond_to?(:flags?) && type.flags? ? :flags : :enum
136
+ else :struct
137
+ end
138
+ end
139
+
140
+ def type_range(type_info)
141
+ line = type_info[:line] ? type_info[:line] - 1 : 0
142
+ col = type_info[:column] ? type_info[:column] - 1 : 0
143
+ {
144
+ start: { line: line, character: col },
145
+ end: { line: line, character: col },
146
+ }
147
+ end
148
+
149
+ def collect_subtypes(facts, target_name, uri)
150
+ results = []
151
+
152
+ facts.implemented_interfaces.each do |type_obj, ifaces|
153
+ ifaces.each do |iface|
154
+ next unless iface.respond_to?(:name) && iface.name == target_name
155
+
156
+ type_name = type_obj.respond_to?(:name) ? type_obj.name : type_obj.to_s
157
+ entry = type_hierarchy_entry(facts, type_name, uri)
158
+ results << entry if entry
159
+ end
160
+ end
161
+
162
+ results
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end