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,415 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ require 'cgi/escape'
5
+ require 'digest'
6
+ require 'pathname'
7
+ require 'set'
8
+ require 'thread'
9
+ require 'timeout'
10
+ require 'uri'
11
+
12
+
13
+ require_relative "server/call_hierarchy"
14
+ require_relative "server/code_actions"
15
+ require_relative "server/code_lens"
16
+ require_relative "server/debug_info"
17
+ require_relative "server/completion"
18
+ require_relative "server/configuration"
19
+ require_relative "server/definition"
20
+ require_relative "server/diagnostics_scheduling"
21
+ require_relative "server/execute_command"
22
+ require_relative "server/folding_range"
23
+ require_relative "server/formatting"
24
+ require_relative "server/hover"
25
+ require_relative "server/inlay_hints"
26
+ require_relative "server/lifecycle"
27
+ require_relative "server/linked_editing_range"
28
+ require_relative "server/on_type_formatting"
29
+ require_relative "server/progress"
30
+ require_relative "server/references"
31
+ require_relative "server/rename"
32
+ require_relative "server/selection_range"
33
+ require_relative "server/semantic_tokens"
34
+ require_relative "server/signature_help"
35
+ require_relative "server/text_documents"
36
+ require_relative "server/type_hierarchy"
37
+ require_relative "server/utilities"
38
+
39
+ module MilkTea
40
+ module LSP
41
+ # Main LSP server — JSON-RPC 2.0 message loop with full MVP feature set.
42
+ #
43
+ # Enhancements implemented:
44
+ # 1. Hover — real type signature from semantic facts
45
+ # 2. Goto Def — token-based definition site navigation
46
+ # 3. Incremental — textDocumentSync: 2 with range-based edits
47
+ # 4. Multi-file — workspace indexing via workspace/symbol
48
+ # 5. Completion — function/type/value completions from facts
49
+ class Server
50
+ SEMANTIC_TOKEN_TYPES = %w[
51
+ namespace type class enum interface struct typeParameter parameter variable property enumMember event
52
+ function method macro keyword modifier comment string number regexp operator decorator
53
+ ].freeze
54
+ SEMANTIC_TOKEN_MODIFIERS = %w[
55
+ declaration definition readonly static deprecated abstract async modification documentation defaultLibrary
56
+ ].freeze
57
+
58
+ KEYWORD_TOKEN_TYPES = Token::KEYWORDS.values.to_set.freeze
59
+ DEFAULT_LIBRARY_TYPE_NAMES = Types::BUILTIN_TYPE_NAMES.to_set.freeze
60
+ BUILTIN_FUNCTION_NAMES = %w[
61
+ ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get
62
+ ].to_set.freeze
63
+ BUILTIN_ASSOCIATED_HOOK_NAMES = %w[hash equal order].to_set.freeze
64
+ BUILTIN_CALL_HOVER_INFO = {
65
+ 'fatal' => {
66
+ signature: 'builtin fatal(message) -> never',
67
+ docs: '`fatal(message)` aborts execution with the provided message.'
68
+ },
69
+ 'ref_of' => {
70
+ signature: 'builtin ref_of(value) -> ref[T]',
71
+ docs: '`ref_of(x)` borrows a mutable safe lvalue as `ref[T]`.'
72
+ },
73
+ 'const_ptr_of' => {
74
+ signature: 'builtin const_ptr_of(value) -> const_ptr[T]',
75
+ docs: '`const_ptr_of(x)` takes the address of a safe lvalue as `const_ptr[T]`.'
76
+ },
77
+ 'ptr_of' => {
78
+ signature: 'builtin ptr_of(value) -> ptr[T]',
79
+ docs: '`ptr_of(x)` takes the address of a mutable safe lvalue as `ptr[T]`.'
80
+ },
81
+ 'read' => {
82
+ signature: 'builtin read(value) -> T',
83
+ docs: '`read(value)` projects a `ref[T]` or pointer-like value to its referent.'
84
+ },
85
+ 'adapt' => {
86
+ signature: 'builtin adapt[Interface](value) -> dyn[Interface]',
87
+ docs: '`adapt[Interface](value)` constructs a `dyn[Interface]` runtime interface value. Compile-time verified: the value\'s type must implement the interface.'
88
+ },
89
+ 'reinterpret' => {
90
+ signature: 'builtin reinterpret[T](value) -> T',
91
+ docs: '`reinterpret[T](value)` reinterprets the bits of `value` as type `T` without conversion. Requires `unsafe`.'
92
+ },
93
+ 'array' => {
94
+ signature: 'builtin array[T, N](elements...) -> array[T, N]',
95
+ docs: '`array[T, N]` constructs a fixed-size array from elements. Omitted trailing elements default to zero.'
96
+ },
97
+ 'span' => {
98
+ signature: 'builtin span[T](data, len) -> span[T]',
99
+ docs: '`span[T](data = ..., len = ...)` constructs a borrowed pointer-plus-length view over contiguous memory.'
100
+ },
101
+ 'zero' => {
102
+ signature: 'builtin zero[T] -> T',
103
+ docs: '`zero[T]` returns a zero-initialized value of type `T`. All bits are set to zero.'
104
+ },
105
+ 'default' => {
106
+ signature: 'builtin default[T] -> T',
107
+ docs: '`default[T]` calls `T.default()` to produce a semantic default value. Requires an accessible `default()` associated function on `T`.'
108
+ },
109
+ 'get' => {
110
+ signature: 'builtin get(collection, index) -> ptr[T]?',
111
+ docs: '`get(collection, index)` performs recoverable bounds-checked indexing, returning `ptr[T]?` (null on out-of-bounds) instead of aborting.'
112
+ },
113
+ 'attribute_arg' => {
114
+ signature: 'builtin attribute_arg[T](handle) -> T',
115
+ docs: '`attribute_arg[T](handle)` returns the `T`-typed argument of a resolved `attribute_handle`. The attribute must declare an argument of type `T`.'
116
+ },
117
+ 'field_of' => {
118
+ signature: 'builtin field_of(Type, field_name) -> field_handle',
119
+ docs: '`field_of(Type, field_name)` returns a compile-time handle for the named field on a struct type.'
120
+ },
121
+ 'callable_of' => {
122
+ signature: 'builtin callable_of(name) -> callable_handle',
123
+ docs: '`callable_of(name)` returns a compile-time handle for a callable declaration name.'
124
+ },
125
+ 'has_attribute' => {
126
+ signature: 'builtin has_attribute(target, attribute_name) -> bool',
127
+ docs: '`has_attribute(target, attribute_name)` checks at compile time whether the resolved attribute is applied to the target.'
128
+ },
129
+ 'attribute_of' => {
130
+ signature: 'builtin attribute_of(target, attribute_name) -> attribute_handle',
131
+ docs: '`attribute_of(target, attribute_name)` returns the applied attribute handle for the resolved target-and-attribute pair; use `has_attribute(...)` when absence is expected.'
132
+ },
133
+ }.freeze
134
+ OPERATOR_TOKEN_TYPES = %i[
135
+ amp colon comma caret dot lparen rparen pipe lbracket rbracket question
136
+ equal plus minus star slash percent less greater tilde
137
+ arrow shift_left shift_right plus_equal minus_equal star_equal slash_equal percent_equal
138
+ amp_equal pipe_equal caret_equal shift_left_equal shift_right_equal
139
+ equal_equal bang_equal less_equal greater_equal ellipsis
140
+ at dot_dot
141
+ ].to_set.freeze
142
+ DIAGNOSTICS_WORKER_COUNT = Integer(ENV.fetch('MILK_TEA_LSP_DIAGNOSTICS_WORKERS', '2')).clamp(1, 8)
143
+
144
+ def self.semantic_tokens_for_path(path, module_roots: nil, package_graph: nil)
145
+ expanded_path = File.expand_path(path)
146
+ roots = module_roots || MilkTea::ModuleRoots.roots_for_path(expanded_path)
147
+ source = File.read(expanded_path)
148
+ tokens = MilkTea::Lexer.lex(source, path: expanded_path)
149
+ facts = MilkTea::ModuleLoader.new(
150
+ module_roots: roots,
151
+ package_graph: package_graph || load_package_graph(expanded_path),
152
+ ).check_file(expanded_path)
153
+
154
+ helper = allocate
155
+ entries = helper.send(:build_semantic_token_entries, tokens, facts)
156
+ data = helper.send(:encode_semantic_tokens, entries)
157
+
158
+ {
159
+ path: expanded_path,
160
+ moduleName: facts.module_name,
161
+ legend: {
162
+ tokenTypes: SEMANTIC_TOKEN_TYPES,
163
+ tokenModifiers: SEMANTIC_TOKEN_MODIFIERS,
164
+ },
165
+ data: data,
166
+ entries: entries.map do |entry|
167
+ {
168
+ line: entry[:line],
169
+ startChar: entry[:start_char],
170
+ length: entry[:length],
171
+ tokenType: entry[:type].to_s,
172
+ modifiers: entry[:modifiers].map(&:to_s),
173
+ }
174
+ end,
175
+ }
176
+ end
177
+
178
+ def self.load_package_graph(path, locked: false)
179
+ PackageGraph.load(path, locked:)
180
+ rescue PackageManifestError
181
+ nil
182
+ end
183
+ private_class_method :load_package_graph
184
+
185
+ def initialize(protocol: Protocol)
186
+ @protocol = protocol
187
+ @workspace = Workspace.new
188
+ @format_mode = :tidy
189
+ @dependency_resolution_mode = :auto
190
+ @platform_override = nil
191
+ @position_encoding = 'utf-16'
192
+ @handlers = {}
193
+ @diagnostic_report_cache = {}
194
+ @workspace_diagnostic_cache = {}
195
+ @semantic_tokens_cache = {}
196
+ @semantic_tokens_delta_cache = {}
197
+ @fixall_cache = {}
198
+ @definition_file_token_cache = {}
199
+ @definition_file_ast_cache = {}
200
+ @diagnostics_perf = {
201
+ scheduled: 0,
202
+ skipped_unchanged: 0,
203
+ cancelled: 0,
204
+ dequeued: 0,
205
+ collected_for_pull: 0,
206
+ published: 0,
207
+ dropped_stale: 0,
208
+ requeued: 0,
209
+ queue_peak: 0,
210
+ }
211
+ @diagnostics_mutex = Mutex.new
212
+ @diagnostics_pending = {}
213
+ @diagnostics_enqueued = Set.new
214
+ @diagnostics_generation = Hash.new(0)
215
+ @diagnostics_last_scheduled_hash = {}
216
+ @diagnostics_queue = Queue.new
217
+ @diagnostics_workers = []
218
+ @cancelled_requests_mutex = Mutex.new
219
+ @cancelled_request_ids = Set.new
220
+ @_initialize_called = false
221
+ @_indexing_thread = nil
222
+ Diagnostics.protocol = @protocol
223
+ Diagnostics.position_encoding = @position_encoding
224
+ register_handlers
225
+ start_diagnostics_workers
226
+ end
227
+
228
+ def run
229
+ if perf_logging?
230
+ mode = perf_verbose? ? 'verbose' : 'threshold'
231
+ warn "[LSP perf] enabled mode=#{mode} threshold_ms=#{Workspace::PERF_LOG_THRESHOLD_MS}"
232
+ end
233
+
234
+ loop do
235
+ message = @protocol.read_message
236
+ break if message.nil?
237
+ next if message.equal?(Protocol::INVALID_MESSAGE)
238
+
239
+ process_message(message)
240
+ end
241
+ rescue StandardError => e
242
+ warn "Server error: #{e.message}"
243
+ raise
244
+ end
245
+
246
+
247
+ private
248
+
249
+ include ServerCallHierarchy
250
+ include ServerCodeActions
251
+ include ServerCodeLens
252
+ include ServerDebugInfo
253
+ include ServerCompletion
254
+ include ServerConfiguration
255
+ include ServerDefinition
256
+ include ServerDiagnosticsScheduling
257
+ include ServerFoldingRange
258
+ include ServerFormatting
259
+ include ServerHover
260
+ include ServerInlayHints
261
+ include ServerLifecycle
262
+ include ServerOnTypeFormatting
263
+ include ServerProgress
264
+ include ServerReferences
265
+ include ServerExecuteCommand
266
+ include ServerLinkedEditingRange
267
+ include ServerRename
268
+ include ServerSelectionRange
269
+ include ServerSemanticTokens
270
+ include ServerSignatureHelp
271
+ include ServerTextDocuments
272
+ include ServerTypeHierarchy
273
+ include ServerUtilities
274
+
275
+ # ── Handler registration ─────────────────────────────────────────────────
276
+
277
+ def register_handlers
278
+ # Lifecycle
279
+ @handlers['initialize'] = method(:handle_initialize)
280
+ @handlers['initialized'] = method(:handle_initialized)
281
+ @handlers['shutdown'] = method(:handle_shutdown)
282
+ @handlers['exit'] = method(:handle_exit)
283
+ @handlers['$/cancelRequest'] = method(:handle_cancel_request)
284
+
285
+ # Text document sync
286
+ @handlers['milkTea/documentContext'] = method(:handle_document_context)
287
+ @handlers['textDocument/didOpen'] = method(:handle_did_open)
288
+ @handlers['textDocument/didChange'] = method(:handle_did_change)
289
+ @handlers['textDocument/didClose'] = method(:handle_did_close)
290
+ @handlers['textDocument/didSave'] = method(:handle_did_save)
291
+ @handlers['textDocument/willSaveWaitUntil'] = method(:handle_will_save_wait_until)
292
+
293
+ # IDE features
294
+ @handlers['textDocument/hover'] = method(:handle_hover)
295
+ @handlers['textDocument/definition'] = method(:handle_definition)
296
+ @handlers['textDocument/declaration'] = method(:handle_declaration)
297
+ @handlers['textDocument/typeDefinition'] = method(:handle_type_definition)
298
+ @handlers['textDocument/implementation'] = method(:handle_implementation)
299
+ @handlers['textDocument/references'] = method(:handle_references)
300
+ @handlers['milkTea/debugInfo'] = method(:handle_debug_info)
301
+ @handlers['textDocument/documentLink'] = method(:handle_document_link)
302
+ @handlers['documentLink/resolve'] = method(:handle_document_link_resolve)
303
+ @handlers['textDocument/documentHighlight'] = method(:handle_document_highlight)
304
+ @handlers['textDocument/documentSymbol'] = method(:handle_document_symbols)
305
+ @handlers['textDocument/formatting'] = method(:handle_formatting)
306
+ @handlers['textDocument/rangeFormatting'] = method(:handle_range_formatting)
307
+ @handlers['textDocument/onTypeFormatting'] = method(:handle_on_type_formatting)
308
+ @handlers['textDocument/foldingRange'] = method(:handle_folding_range)
309
+ @handlers['textDocument/completion'] = method(:handle_completion)
310
+ @handlers['completionItem/resolve'] = method(:handle_completion_resolve)
311
+ @handlers['textDocument/codeAction'] = method(:handle_code_action)
312
+ @handlers['textDocument/codeLens'] = method(:handle_code_lens)
313
+ @handlers['codeLens/resolve'] = method(:handle_code_lens_resolve)
314
+ @handlers['textDocument/prepareCallHierarchy'] = method(:handle_prepare_call_hierarchy)
315
+ @handlers['callHierarchy/incomingCalls'] = method(:handle_incoming_calls)
316
+ @handlers['callHierarchy/outgoingCalls'] = method(:handle_outgoing_calls)
317
+ @handlers['textDocument/inlayHint'] = method(:handle_inlay_hint)
318
+ @handlers['textDocument/semanticTokens/full'] = method(:handle_semantic_tokens_full)
319
+ @handlers['textDocument/semanticTokens/full/delta'] = method(:handle_semantic_tokens_delta)
320
+ @handlers['textDocument/semanticTokens/range'] = method(:handle_semantic_tokens_range)
321
+ @handlers['textDocument/selectionRange'] = method(:handle_selection_range)
322
+ @handlers['textDocument/diagnostic'] = method(:handle_document_diagnostic)
323
+ @handlers['workspace/diagnostic'] = method(:handle_workspace_diagnostic)
324
+ @handlers['textDocument/signatureHelp'] = method(:handle_signature_help)
325
+ @handlers['textDocument/prepareTypeHierarchy'] = method(:handle_prepare_type_hierarchy)
326
+ @handlers['typeHierarchy/supertypes'] = method(:handle_supertypes)
327
+ @handlers['typeHierarchy/subtypes'] = method(:handle_subtypes)
328
+ @handlers['textDocument/prepareRename'] = method(:handle_prepare_rename)
329
+ @handlers['textDocument/rename'] = method(:handle_rename)
330
+ @handlers['textDocument/linkedEditingRange'] = method(:handle_linked_editing_range)
331
+
332
+ # Workspace
333
+ @handlers['workspace/symbol'] = method(:handle_workspace_symbol)
334
+ @handlers['workspace/executeCommand'] = method(:handle_execute_command)
335
+ @handlers['workspace/didChangeWorkspaceFolders'] = method(:handle_did_change_workspace_folders)
336
+ @handlers['workspace/didChangeConfiguration'] = method(:handle_did_change_configuration)
337
+ @handlers['workspace/didChangeWatchedFiles'] = method(:handle_did_change_watched_files)
338
+ @handlers['workspace/willRenameFiles'] = method(:handle_will_rename_files)
339
+ end
340
+
341
+ # ── Message dispatch ─────────────────────────────────────────────────────
342
+
343
+ def process_message(message)
344
+ method_name = message['method']
345
+ params = message['params'] || {}
346
+ id = message['id']
347
+
348
+ if message.key?('id') && !message.key?('method')
349
+ Protocol.handle_response(message)
350
+ elsif message.key?('id')
351
+ handle_request(method_name, params, id)
352
+ else
353
+ handle_notification(method_name, params)
354
+ end
355
+ rescue StandardError => e
356
+ log_message(:warning, "Error processing message: #{e.message}")
357
+ @protocol.write_error(id, -32_603, 'Internal server error') if id && message.key?('method')
358
+ end
359
+
360
+ def handle_request(method_name, params, id)
361
+ if request_cancelled?(id)
362
+ clear_cancelled_request(id)
363
+ @protocol.write_error(id, -32_800, 'Request cancelled')
364
+ return
365
+ end
366
+
367
+ handler = @handlers[method_name]
368
+ if handler.nil?
369
+ @protocol.write_error(id, -32_601, 'Method not found')
370
+ return
371
+ end
372
+
373
+ begin
374
+ @current_request_id = id
375
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
376
+ result = handler.call(params)
377
+ elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
378
+ if perf_logging? && (perf_verbose? || elapsed_ms > Workspace::PERF_LOG_THRESHOLD_MS)
379
+ detail = perf_log_context(method_name, params, verbose: perf_verbose?)
380
+ warn "[LSP perf] req #{method_name} #{elapsed_ms}ms id=#{id}#{detail}"
381
+ end
382
+ if request_cancelled?(id)
383
+ clear_cancelled_request(id)
384
+ @protocol.write_error(id, -32_800, 'Request cancelled')
385
+ else
386
+ @protocol.write_response(id, result)
387
+ end
388
+ rescue StandardError => e
389
+ log_message(:warning, "Error in handler for #{method_name}: #{e.message}")
390
+ @protocol.write_error(id, -32_603, "Internal error: #{e.message}")
391
+ ensure
392
+ clear_cancelled_request(id)
393
+ @current_request_id = nil
394
+ end
395
+ end
396
+
397
+ def handle_notification(method_name, params)
398
+ handler = @handlers[method_name]
399
+ return unless handler
400
+
401
+ begin
402
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
403
+ handler.call(params)
404
+ elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
405
+ if perf_logging? && (perf_verbose? || elapsed_ms > Workspace::PERF_LOG_THRESHOLD_MS)
406
+ detail = perf_log_context(method_name, params, verbose: perf_verbose?)
407
+ warn "[LSP perf] ntf #{method_name} #{elapsed_ms}ms#{detail}"
408
+ end
409
+ rescue StandardError => e
410
+ log_message(:warning, "Error in notification handler for #{method_name}: #{e.message}")
411
+ end
412
+ end
413
+ end
414
+ end
415
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Workspace
6
+ module WorkspaceAnalysis
7
+ private
8
+
9
+ # ── Compilation helpers ─────────────────────────────────────────────────
10
+
11
+ def lex_document(uri)
12
+ content = get_content(uri)
13
+ return nil if content.empty?
14
+
15
+ recovery_errors = []
16
+ tokens = MilkTea::Lexer.lex(content, path: uri, recovery_errors:)
17
+ @last_good_tokens_cache[uri] = tokens
18
+ index_identifier_tokens(uri, tokens) if tokens
19
+ tokens
20
+ rescue StandardError
21
+ @last_good_tokens_cache[uri] || @tokens_cache[uri]
22
+ end
23
+
24
+ def parse_document(uri)
25
+ tokens = get_tokens(uri)
26
+ return nil if tokens.nil?
27
+
28
+ MilkTea::Parser.parse_collecting_errors(tokens: tokens, path: uri).ast
29
+ rescue StandardError
30
+ nil
31
+ end
32
+
33
+ def perf_logging?
34
+ @perf_logging ||= !ENV.fetch('MILK_TEA_LSP_PERF', nil).to_s.empty?
35
+ end
36
+
37
+ def perf_verbose?
38
+ @perf_verbose ||= ENV.fetch('MILK_TEA_LSP_PERF', nil).to_s == 'verbose'
39
+ end
40
+
41
+ def perf_breakdown_logging?(elapsed_ms_value)
42
+ perf_logging? && (perf_verbose? || elapsed_ms_value > PERF_LOG_THRESHOLD_MS)
43
+ end
44
+
45
+ def monotonic_time
46
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
47
+ end
48
+
49
+ def elapsed_ms(start_time)
50
+ ((monotonic_time - start_time) * 1000).round(1)
51
+ end
52
+
53
+ def log_perf_breakdown(name, elapsed_ms_value, detail)
54
+ return unless perf_breakdown_logging?(elapsed_ms_value)
55
+
56
+ warn "[LSP perf] breakdown #{name} #{elapsed_ms_value}ms #{detail}"
57
+ end
58
+
59
+ def ast_for_content(uri, content)
60
+ return get_ast(uri) if get_content(uri) == content
61
+
62
+ MilkTea::Parser.parse(content, path: uri)
63
+ rescue StandardError
64
+ nil
65
+ end
66
+
67
+ def warm_document_facts(uri, content)
68
+ ast = ast_for_content(uri, content)
69
+ stats = {
70
+ bytes: content.bytesize,
71
+ lines: content.count("\n") + 1,
72
+ eager_facts: false,
73
+ facts_mode: nil,
74
+ facts_ms: nil,
75
+ skip_reason: nil,
76
+ import_count: ast.respond_to?(:imports) ? ast.imports.length : 0,
77
+ shared_module_cache_size: @shared_module_cache.length,
78
+ }
79
+
80
+ if background_document?(uri)
81
+ stats[:skip_reason] = :background_document
82
+ return stats
83
+ end
84
+
85
+ facts_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
86
+ facts_path = uri_to_path(uri)
87
+ get_facts(uri)
88
+ stats[:eager_facts] = true
89
+ stats[:facts_mode] = facts_path && File.file?(facts_path) ? :module_loader : :memory
90
+ stats[:facts_ms] = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - facts_start) * 1000).round(1)
91
+ stats[:shared_module_cache_size] = @shared_module_cache.length
92
+ stats
93
+ end
94
+
95
+ def compute_facts_for_content(uri, content)
96
+ path = uri_to_path(uri)
97
+ if path && File.file?(path)
98
+ parse_result = MilkTea::Parser.parse_collecting_errors(content, path: uri)
99
+ ast = parse_result.ast
100
+ return nil unless ast
101
+
102
+ effective_platform = effective_platform_for_path(path)
103
+ ensure_root_platform_compatible!(path, effective_platform)
104
+ loader = MilkTea::ModuleLoader.new(
105
+ module_roots: module_roots_for_path(path),
106
+ package_graph: package_graph_for_path(path),
107
+ shared_cache: @shared_module_cache,
108
+ source_overrides: file_backed_source_overrides,
109
+ platform: effective_platform,
110
+ )
111
+ ast = with_inferred_module_name(ast, loader:, path:)
112
+ import_resolution = loader.imported_modules_for_ast_collecting_errors(ast, importer_path: path)
113
+ MilkTea::SemanticAnalyzer.tooling_snapshot(
114
+ ast,
115
+ imported_modules: import_resolution.modules,
116
+ allow_missing_imports: true,
117
+ path: path,
118
+ ).facts
119
+ else
120
+ ast = MilkTea::Parser.parse(content, path: uri)
121
+ MilkTea::SemanticAnalyzer.check(ast)
122
+ end
123
+ rescue MilkTea::LexError, MilkTea::SemanticError, ModuleLoadError
124
+ nil
125
+ end
126
+
127
+ def analyze_document(uri)
128
+ path = uri_to_path(uri)
129
+ snapshot = if path && File.file?(path)
130
+ parse_result = MilkTea::Parser.parse_collecting_errors(get_content(uri), path: uri)
131
+ ast = parse_result.ast
132
+ return @last_good_tooling_snapshot_cache[uri] if ast.nil?
133
+
134
+ @ast_cache[uri] = ast
135
+
136
+ resolution = DependencyResolution.resolve(path, mode: @dependency_resolution_mode)
137
+ return nil unless resolution.ok?
138
+
139
+ effective_platform = effective_platform_for_path(path)
140
+ ensure_root_platform_compatible!(path, effective_platform)
141
+
142
+ loader = MilkTea::ModuleLoader.new(
143
+ module_roots: module_roots_for_path(path, locked: resolution.locked),
144
+ package_graph: package_graph_for_path(path, locked: resolution.locked),
145
+ shared_cache: @shared_module_cache,
146
+ source_overrides: file_backed_source_overrides,
147
+ platform: effective_platform,
148
+ )
149
+ ast = with_inferred_module_name(ast, loader:, path:)
150
+ import_resolution = loader.imported_modules_for_ast_collecting_errors(ast, importer_path: path)
151
+ MilkTea::SemanticAnalyzer.tooling_snapshot(
152
+ ast,
153
+ imported_modules: import_resolution.modules,
154
+ allow_missing_imports: true,
155
+ path: path,
156
+ )
157
+ else
158
+ ast = get_ast(uri)
159
+ return @last_good_tooling_snapshot_cache[uri] if ast.nil?
160
+
161
+ result = MilkTea::SemanticAnalyzer.check_collecting_errors(ast)
162
+ facts = result[:analysis]
163
+ MilkTea::SemanticAnalyzer::ToolingSnapshot.new(facts:, diagnostics: (Array(result[:errors]).map { |e| e.to_diagnostic(path: uri) } || []).freeze)
164
+ end
165
+ if snapshot&.facts
166
+ @last_good_tooling_snapshot_cache[uri] = snapshot
167
+ @last_good_facts_cache[uri] = snapshot.facts
168
+ @document_module_names[uri] = snapshot.facts.module_name
169
+ end
170
+ snapshot
171
+ rescue MilkTea::LexError, MilkTea::SemanticError, ModuleLoadError, PackageLockError
172
+ @last_good_tooling_snapshot_cache[uri]
173
+ rescue StandardError => e
174
+ warn "LSP sema error #{uri}: #{e.message}"
175
+ @last_good_tooling_snapshot_cache[uri]
176
+ end
177
+
178
+ def package_graph_for_path(path, locked: false)
179
+ PackageGraph.load(path, locked:)
180
+ rescue PackageManifestError
181
+ nil
182
+ end
183
+
184
+ def with_inferred_module_name(ast, loader:, path:)
185
+ inferred_module_name = loader.send(:inferred_module_name_for_path, path)
186
+ AST::SourceFile.new(
187
+ module_name: AST::QualifiedName.new(inferred_module_name.split('.')),
188
+ module_kind: ast.module_kind,
189
+ imports: ast.imports,
190
+ directives: ast.directives,
191
+ declarations: ast.declarations,
192
+ line: ast.line,
193
+ node_ids: ast.node_ids,
194
+ )
195
+ end
196
+
197
+ def effective_platform_for_path(path)
198
+ ModuleLoader.effective_platform_for_path(path, platform_override: @platform_override)
199
+ end
200
+
201
+ def ensure_root_platform_compatible!(path, effective_platform)
202
+ return unless ModuleLoader.platform_suffix_for_path(path)
203
+
204
+ ModuleLoader.resolve_source_path(path, platform: effective_platform, error_class: ModuleLoadError)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end