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,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerDiagnosticsScheduling
7
+ private
8
+
9
+ def schedule_diagnostics(uri, force: false, lint_tier: :full)
10
+ content = @workspace.get_content(uri)
11
+ content_digest = Digest::SHA256.hexdigest(content)
12
+ normalized_lint_tier = Linter.normalize_lint_tier(lint_tier)
13
+ enqueue = false
14
+
15
+ @diagnostics_mutex.synchronize do
16
+ previous = @diagnostics_last_scheduled_hash[uri]
17
+ if !force && previous && previous[:digest] == content_digest && lint_tier_rank(previous[:lint_tier]) >= lint_tier_rank(normalized_lint_tier)
18
+ @diagnostics_perf[:skipped_unchanged] += 1 if perf_logging?
19
+ return
20
+ end
21
+
22
+ @diagnostics_generation[uri] += 1
23
+ @diagnostics_last_scheduled_hash[uri] = {
24
+ digest: content_digest,
25
+ lint_tier: normalized_lint_tier,
26
+ }
27
+ @diagnostics_perf[:scheduled] += 1 if perf_logging?
28
+
29
+ pending = @diagnostics_pending[uri]
30
+ pending_lint_tier = pending ? pending[:lint_tier] : normalized_lint_tier
31
+ merged_lint_tier = if pending && pending[:content] == content
32
+ more_strict_lint_tier(pending_lint_tier, normalized_lint_tier)
33
+ else
34
+ normalized_lint_tier
35
+ end
36
+
37
+ @diagnostics_pending[uri] = {
38
+ generation: @diagnostics_generation[uri],
39
+ content: content,
40
+ lint_tier: merged_lint_tier,
41
+ }
42
+
43
+ unless @diagnostics_enqueued.include?(uri)
44
+ @diagnostics_enqueued << uri
45
+ enqueue = true
46
+ end
47
+ end
48
+
49
+ if enqueue
50
+ @diagnostics_queue << uri
51
+ if perf_logging?
52
+ @diagnostics_perf[:queue_peak] = [@diagnostics_perf[:queue_peak], @diagnostics_queue.length].max
53
+ end
54
+ end
55
+ end
56
+
57
+ def cancel_diagnostics(uri)
58
+ @diagnostics_mutex.synchronize do
59
+ @diagnostics_generation[uri] += 1
60
+ @diagnostics_pending.delete(uri)
61
+ @diagnostics_last_scheduled_hash.delete(uri)
62
+ @diagnostics_perf[:cancelled] += 1 if perf_logging?
63
+ end
64
+ end
65
+
66
+ def start_diagnostics_workers
67
+ return if @diagnostics_workers.any?(&:alive?)
68
+
69
+ DIAGNOSTICS_WORKER_COUNT.times do |index|
70
+ @diagnostics_workers << Thread.new do
71
+ if Thread.current.respond_to?(:name=)
72
+ Thread.current.name = "mt-lsp-diagnostics-#{index + 1}"
73
+ end
74
+
75
+ loop do
76
+ uri = @diagnostics_queue.pop
77
+ break if uri == :__stop__
78
+
79
+ process_diagnostics_for_uri(uri)
80
+ end
81
+ rescue StandardError => e
82
+ warn "LSP diagnostics worker error: #{e.message}"
83
+ warn " #{e.backtrace.first(8).join("\n ")}" if e.backtrace
84
+ end
85
+ end
86
+ end
87
+
88
+ def stop_diagnostics_workers
89
+ workers = @diagnostics_workers
90
+ return if workers.empty?
91
+
92
+ workers.length.times { @diagnostics_queue << :__stop__ }
93
+ workers.each do |worker|
94
+ worker.join(1.0)
95
+ next unless worker.alive?
96
+
97
+ worker.kill
98
+ worker.join
99
+ end
100
+ @diagnostics_workers = []
101
+ rescue StandardError => e
102
+ warn "LSP diagnostics worker shutdown error: #{e.message}"
103
+ end
104
+
105
+ def drain_diagnostics_queue
106
+ @diagnostics_queue.clear
107
+ nil
108
+ end
109
+
110
+ def process_diagnostics_for_uri(uri)
111
+ loop do
112
+ snapshot = nil
113
+ @diagnostics_mutex.synchronize do
114
+ snapshot = @diagnostics_pending.delete(uri)
115
+ end
116
+ break unless snapshot
117
+
118
+ @diagnostics_perf[:dequeued] += 1 if perf_logging?
119
+
120
+ diagnostics = collect_diagnostics_for_content(uri, snapshot[:content], lint_tier: snapshot[:lint_tier])
121
+ publish = false
122
+ @diagnostics_mutex.synchronize do
123
+ publish = snapshot[:generation] == @diagnostics_generation[uri]
124
+ end
125
+
126
+ if publish
127
+ if defined?(@pull_diagnostics_active) && @pull_diagnostics_active
128
+ @diagnostics_perf[:collected_for_pull] += 1 if perf_logging?
129
+ else
130
+ @diagnostics_perf[:published] += 1 if perf_logging?
131
+ @protocol.write_notification('textDocument/publishDiagnostics', {
132
+ uri: uri,
133
+ diagnostics: diagnostics
134
+ })
135
+ notify_diagnostic_errors(uri, diagnostics)
136
+ end
137
+ elsif perf_logging?
138
+ @diagnostics_perf[:dropped_stale] += 1
139
+ end
140
+ end
141
+ ensure
142
+ requeue = false
143
+ @diagnostics_mutex.synchronize do
144
+ @diagnostics_enqueued.delete(uri)
145
+ if @diagnostics_pending.key?(uri)
146
+ @diagnostics_enqueued << uri
147
+ requeue = true
148
+ end
149
+ end
150
+
151
+ if requeue
152
+ @diagnostics_perf[:requeued] += 1 if perf_logging?
153
+ @diagnostics_queue << uri
154
+ if perf_logging?
155
+ @diagnostics_perf[:queue_peak] = [@diagnostics_perf[:queue_peak], @diagnostics_queue.length].max
156
+ end
157
+ end
158
+ end
159
+
160
+ def collect_diagnostics_for_content(uri, _content, lint_tier: :full)
161
+ @workspace.collect_diagnostics(uri, lint_tier: lint_tier)
162
+ rescue StandardError => e
163
+ warn "LSP diagnostics error #{uri}: #{e.message}"
164
+ warn " #{e.backtrace.first(6).join("\n ")}" if e.backtrace
165
+ []
166
+ end
167
+
168
+ def lint_tier_rank(lint_tier)
169
+ case Linter.normalize_lint_tier(lint_tier)
170
+ when :full
171
+ 2
172
+ when :fast
173
+ 1
174
+ else
175
+ 0
176
+ end
177
+ end
178
+
179
+ def more_strict_lint_tier(a, b)
180
+ lint_tier_rank(a) >= lint_tier_rank(b) ? Linter.normalize_lint_tier(a) : Linter.normalize_lint_tier(b)
181
+ end
182
+
183
+ def dependency_import_fingerprint(content)
184
+ content.to_s.each_line.filter_map do |line|
185
+ stripped = line.strip
186
+ next if stripped.empty? || stripped.start_with?('#')
187
+ next unless stripped.start_with?('import ')
188
+
189
+ stripped
190
+ end.join("\n")
191
+ end
192
+
193
+ def dependency_export_surface_fingerprint(content)
194
+ content.to_s.each_line.filter_map do |line|
195
+ stripped = line.strip
196
+ next if stripped.empty? || stripped.start_with?('#')
197
+
198
+ if stripped.match?(/\A(?:public\s+)?(?:type|struct|union|enum|flags|variant|interface|event|function|const|var)\b/)
199
+ stripped
200
+ elsif stripped.match?(/\Aextending\b/)
201
+ stripped
202
+ elsif stripped.match?(/\Apublic\s+(?:function|const|var|type)\b/)
203
+ stripped
204
+ end
205
+ end.join("\n")
206
+ end
207
+
208
+ def dependency_refresh_required_for_edit?(changed_uri, previous_content, current_content)
209
+ return false if previous_content == current_content
210
+ return true if dependency_import_fingerprint(previous_content) != dependency_import_fingerprint(current_content)
211
+
212
+ related_uris = @workspace.related_open_document_uris(changed_uri)
213
+ return false unless related_uris.length > 1
214
+
215
+ dependency_export_surface_fingerprint(previous_content) != dependency_export_surface_fingerprint(current_content)
216
+ end
217
+
218
+ def semantic_tokens_allow_last_good_fallback?(uri)
219
+ @diagnostics_mutex.synchronize do
220
+ @diagnostics_pending.key?(uri) || @diagnostics_enqueued.include?(uri)
221
+ end
222
+ end
223
+
224
+ def notify_diagnostic_errors(uri, diagnostics)
225
+ errors = diagnostics.select { |d| d.is_a?(Hash) && (d["severity"] || d[:severity]) == 1 }
226
+ return if errors.empty?
227
+
228
+ @notified_error_uris ||= Set.new
229
+ return if @notified_error_uris.include?(uri)
230
+
231
+ @notified_error_uris.add(uri)
232
+ path = uri_to_path(uri) || uri
233
+ short_path = path.split("/").last(1).join
234
+ show_message(:warning, "#{short_path}: #{errors.length} error#{errors.length == 1 ? '' : 's'}")
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerExecuteCommand
7
+ private
8
+
9
+ def handle_execute_command(params)
10
+ command = params["command"]
11
+ case command
12
+ when "mtc.restartServer"
13
+ exit(0)
14
+ else
15
+ warn "Unknown executeCommand: #{command}"
16
+ nil
17
+ end
18
+ rescue StandardError => e
19
+ warn "Error in executeCommand handler: #{e.message}"
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerFoldingRange
7
+ private
8
+
9
+ BLOCK_START_PATTERN = /\A(public\s+)?(async\s+)?(editable\s+)?(function|struct|enum|flags|variant|union|interface|if|while|for|match|unsafe|extending|defer)\b/
10
+
11
+ CONTINUATION_KEYWORDS = %w[else elif when].freeze
12
+
13
+ def handle_folding_range(params)
14
+ text_document = params["textDocument"]
15
+ uri = text_document["uri"]
16
+ content = @workspace.get_content(uri)
17
+ return [] unless content
18
+
19
+ lines = content.split("\n", -1)
20
+ folds = []
21
+
22
+ compute_block_folds(lines, folds)
23
+ compute_import_folds(lines, folds)
24
+ compute_comment_folds(lines, folds)
25
+ trim_trailing_blank_lines(folds, lines)
26
+
27
+ folds
28
+ end
29
+
30
+ private
31
+
32
+ def compute_block_folds(lines, folds)
33
+ stack = [] # [[start_line, indent], ...]
34
+ last_indent = 0
35
+
36
+ lines.each_with_index do |line, line_num|
37
+ stripped = line.lstrip
38
+ next if stripped.empty?
39
+
40
+ indent = line.length - stripped.length
41
+ stripped_no_comment = strip_comment(stripped)
42
+
43
+ if indent < last_indent
44
+ while stack.any? && stack.last[1] >= indent
45
+ start_line, = stack.pop
46
+ folds << folding_range(start_line, line_num - 1) if line_num - 1 > start_line
47
+ end
48
+ end
49
+
50
+ if continuation_start?(stripped_no_comment)
51
+ if stack.any? && stack.last[1] == indent
52
+ start_line, = stack.pop
53
+ folds << folding_range(start_line, line_num - 1) if line_num - 1 > start_line
54
+ end
55
+ stack << [line_num, indent]
56
+ elsif block_start?(stripped_no_comment)
57
+ stack << [line_num, indent]
58
+ end
59
+
60
+ last_indent = indent
61
+ end
62
+
63
+ last_line = lines.length - 1
64
+ while stack.any?
65
+ start_line, = stack.pop
66
+ folds << folding_range(start_line, last_line) if last_line > start_line
67
+ end
68
+ end
69
+
70
+ def compute_import_folds(lines, folds)
71
+ import_start = nil
72
+
73
+ lines.each_with_index do |line, line_num|
74
+ stripped = line.lstrip
75
+ if stripped.start_with?("import ")
76
+ import_start ||= line_num
77
+ else
78
+ if import_start && line_num - 1 > import_start
79
+ folds << {
80
+ startLine: import_start,
81
+ endLine: line_num - 1,
82
+ kind: "imports",
83
+ }
84
+ end
85
+ import_start = nil
86
+ end
87
+ end
88
+
89
+ return unless import_start
90
+
91
+ last_line = lines.length - 1
92
+ return unless last_line > import_start
93
+
94
+ folds << {
95
+ startLine: import_start,
96
+ endLine: last_line,
97
+ kind: "imports",
98
+ }
99
+ end
100
+
101
+ def compute_comment_folds(lines, folds)
102
+ comment_start = nil
103
+
104
+ lines.each_with_index do |line, line_num|
105
+ stripped = line.lstrip
106
+ if stripped.start_with?("#>") && comment_start.nil?
107
+ comment_start = line_num
108
+ elsif stripped.include?("<#") && comment_start
109
+ folds << {
110
+ startLine: comment_start,
111
+ endLine: line_num,
112
+ kind: "comment",
113
+ }
114
+ comment_start = nil
115
+ end
116
+ end
117
+ end
118
+
119
+ def block_start?(stripped)
120
+ return false unless stripped.end_with?(":") || stripped == ":"
121
+
122
+ stripped.match?(BLOCK_START_PATTERN) && !CONTINUATION_KEYWORDS.any? { |kw| stripped.start_with?(kw) }
123
+ end
124
+
125
+ def continuation_start?(stripped)
126
+ return false unless stripped.end_with?(":") || stripped == ":"
127
+
128
+ CONTINUATION_KEYWORDS.any? { |kw| stripped.start_with?(kw) }
129
+ end
130
+
131
+ def strip_comment(line)
132
+
133
+ idx = line.index(" #")
134
+ idx ? line[0...idx] : line
135
+ end
136
+
137
+ def trim_trailing_blank_lines(folds, lines)
138
+ folds.each do |fold|
139
+ end_line = fold[:endLine]
140
+ while end_line > fold[:startLine] && lines[end_line].strip.empty?
141
+ end_line -= 1
142
+ end
143
+ fold[:endLine] = end_line
144
+ end
145
+ end
146
+
147
+ def folding_range(start_line, end_line)
148
+ { startLine: start_line, endLine: end_line }
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end