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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module LSP
|
|
5
|
+
class Workspace
|
|
6
|
+
module WorkspaceUtilities
|
|
7
|
+
def shared_module_cache
|
|
8
|
+
@shared_module_cache
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def workspace_root_path=(path)
|
|
12
|
+
@workspace_root_path = normalize_workspace_root_path(path)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def workspace_root_path
|
|
16
|
+
@workspace_root_path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dependency_resolution_mode=(mode)
|
|
20
|
+
normalized = DependencyResolution.normalize_mode(mode)
|
|
21
|
+
return if @dependency_resolution_mode == normalized
|
|
22
|
+
|
|
23
|
+
@dependency_resolution_mode = normalized
|
|
24
|
+
@facts_state_mutex.synchronize do
|
|
25
|
+
@facts_cache_mutex.synchronize do
|
|
26
|
+
@shared_module_cache.clear
|
|
27
|
+
@facts_cache.clear
|
|
28
|
+
@tooling_snapshot_cache.clear
|
|
29
|
+
@diagnostics_cache.clear
|
|
30
|
+
@last_good_facts_cache.clear
|
|
31
|
+
@last_good_tooling_snapshot_cache.clear
|
|
32
|
+
clear_dependency_index
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def dependency_resolution_mode
|
|
38
|
+
@dependency_resolution_mode
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def platform_override=(platform)
|
|
42
|
+
normalized = platform.nil? ? nil : ModuleLoader.normalize_platform_name(platform)
|
|
43
|
+
return if @platform_override == normalized
|
|
44
|
+
|
|
45
|
+
@platform_override = normalized
|
|
46
|
+
@facts_state_mutex.synchronize do
|
|
47
|
+
@facts_cache_mutex.synchronize do
|
|
48
|
+
@shared_module_cache.clear
|
|
49
|
+
@facts_cache.clear
|
|
50
|
+
@tooling_snapshot_cache.clear
|
|
51
|
+
@diagnostics_cache.clear
|
|
52
|
+
@last_good_facts_cache.clear
|
|
53
|
+
@last_good_tooling_snapshot_cache.clear
|
|
54
|
+
clear_dependency_index
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def platform_override
|
|
60
|
+
@platform_override
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def strict_current_root_diagnostics_enabled=(enabled)
|
|
64
|
+
normalized = !!enabled
|
|
65
|
+
return if @strict_current_root_diagnostics_enabled == normalized
|
|
66
|
+
|
|
67
|
+
@strict_current_root_diagnostics_enabled = normalized
|
|
68
|
+
@facts_state_mutex.synchronize do
|
|
69
|
+
@facts_cache_mutex.synchronize do
|
|
70
|
+
@diagnostics_cache.clear
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def strict_current_root_diagnostics_enabled
|
|
76
|
+
@strict_current_root_diagnostics_enabled
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Scan text up to the cursor to find the innermost open function call context.
|
|
80
|
+
# Returns { name:, active_parameter: } or nil if not inside a call.
|
|
81
|
+
def find_call_context(uri, lsp_line, lsp_char)
|
|
82
|
+
content = get_content(uri)
|
|
83
|
+
return nil if content.empty?
|
|
84
|
+
|
|
85
|
+
lines = content.split("\n", -1)
|
|
86
|
+
cursor_line = lines[lsp_line] || ''
|
|
87
|
+
prefix = lsp_line.positive? ? lines[0...lsp_line].join("\n") + "\n" : ''
|
|
88
|
+
text = prefix + cursor_line[0...lsp_char]
|
|
89
|
+
|
|
90
|
+
depth = 0
|
|
91
|
+
active_param = 0
|
|
92
|
+
i = text.length - 1
|
|
93
|
+
paren_pos = nil
|
|
94
|
+
|
|
95
|
+
while i >= 0
|
|
96
|
+
ch = text[i]
|
|
97
|
+
case ch
|
|
98
|
+
when ')', ']'
|
|
99
|
+
depth += 1
|
|
100
|
+
when '['
|
|
101
|
+
return nil if depth.zero?
|
|
102
|
+
depth -= 1
|
|
103
|
+
when '('
|
|
104
|
+
if depth.zero?
|
|
105
|
+
paren_pos = i
|
|
106
|
+
break
|
|
107
|
+
end
|
|
108
|
+
depth -= 1
|
|
109
|
+
when ','
|
|
110
|
+
active_param += 1 if depth.zero?
|
|
111
|
+
end
|
|
112
|
+
i -= 1
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
return nil unless paren_pos
|
|
116
|
+
|
|
117
|
+
# Find identifier immediately before the '('
|
|
118
|
+
j = paren_pos - 1
|
|
119
|
+
j -= 1 while j >= 0 && (text[j] == ' ' || text[j] == "\t")
|
|
120
|
+
return nil if j < 0 || text[j] !~ /[A-Za-z0-9_]/
|
|
121
|
+
|
|
122
|
+
end_j = j
|
|
123
|
+
j -= 1 while j > 0 && text[j - 1] =~ /[A-Za-z0-9_]/
|
|
124
|
+
name = text[j..end_j]
|
|
125
|
+
return nil if name.empty?
|
|
126
|
+
|
|
127
|
+
{ name: name, active_parameter: active_param }
|
|
128
|
+
rescue StandardError => e
|
|
129
|
+
warn "LSP call context error #{uri}: #{e.message}"
|
|
130
|
+
nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# ── Position helpers ────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
# Find the identifier/keyword token under the cursor.
|
|
136
|
+
# lsp_line and lsp_character are 0-based (LSP convention).
|
|
137
|
+
def find_token_at(uri, lsp_line, lsp_character)
|
|
138
|
+
tokens = get_tokens(uri)
|
|
139
|
+
return nil if tokens.nil?
|
|
140
|
+
|
|
141
|
+
# Tokens use 1-based line/column
|
|
142
|
+
target_line = lsp_line + 1
|
|
143
|
+
target_char = lsp_character + 1
|
|
144
|
+
|
|
145
|
+
tokens.find do |tok|
|
|
146
|
+
next false if [:newline, :indent, :dedent, :eof].include?(tok.type)
|
|
147
|
+
|
|
148
|
+
token_contains_position?(tok, target_line, target_char)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def token_contains_position?(token, target_line, target_char)
|
|
153
|
+
segments = token.lexeme.split("\n", -1)
|
|
154
|
+
end_line = token.line + segments.length - 1
|
|
155
|
+
return false if target_line < token.line || target_line > end_line
|
|
156
|
+
|
|
157
|
+
if segments.length == 1
|
|
158
|
+
return token.column <= target_char && target_char < (token.column + segments.first.length)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if target_line == token.line
|
|
162
|
+
return token.column <= target_char && target_char <= (token.column + segments.first.length - 1)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
target_char <= segments.fetch(target_line - token.line).length
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns the receiver name before '.' if the cursor is in a dot-access
|
|
169
|
+
# context, e.g. for "vec.len|" returns "vec". Returns nil otherwise.
|
|
170
|
+
# lsp_char is the 0-based cursor character position (LSP convention).
|
|
171
|
+
def find_dot_receiver(uri, lsp_line, lsp_char)
|
|
172
|
+
receiver_path = find_dot_receiver_path(uri, lsp_line, lsp_char)
|
|
173
|
+
return nil unless receiver_path
|
|
174
|
+
|
|
175
|
+
receiver_path.split('.').last
|
|
176
|
+
rescue StandardError
|
|
177
|
+
nil
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def find_dot_receiver_path(uri, lsp_line, lsp_char)
|
|
181
|
+
content = get_content(uri)
|
|
182
|
+
lines = content.split("\n", -1)
|
|
183
|
+
line_str = lines[lsp_line] || ''
|
|
184
|
+
|
|
185
|
+
idx = [lsp_char - 1, line_str.length - 1].min
|
|
186
|
+
|
|
187
|
+
# Walk back past the hovered identifier to its dot
|
|
188
|
+
idx -= 1 while idx >= 0 && line_str[idx] =~ /[A-Za-z0-9_]/
|
|
189
|
+
return nil if idx < 0 || line_str[idx] != '.'
|
|
190
|
+
|
|
191
|
+
segments = []
|
|
192
|
+
left = idx - 1
|
|
193
|
+
loop do
|
|
194
|
+
# Skip brackets and their contents, then collect the identifier before them
|
|
195
|
+
if line_str[left] == ']'
|
|
196
|
+
depth = 1
|
|
197
|
+
left -= 1
|
|
198
|
+
while left >= 0 && depth > 0
|
|
199
|
+
depth += 1 if line_str[left] == ']'
|
|
200
|
+
depth -= 1 if line_str[left] == '['
|
|
201
|
+
left -= 1 if depth > 0 && left > 0
|
|
202
|
+
end
|
|
203
|
+
left -= 1 # skip past opening bracket
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Walk back over identifier chars
|
|
207
|
+
start = left
|
|
208
|
+
start -= 1 while start >= 0 && line_str[start] =~ /[A-Za-z0-9_]/
|
|
209
|
+
segments.unshift(line_str[start + 1..left]) if start < left
|
|
210
|
+
|
|
211
|
+
break unless start >= 0 && line_str[start] == '.'
|
|
212
|
+
|
|
213
|
+
left = start - 1
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
segments.any? ? segments.join('.') : nil
|
|
217
|
+
rescue StandardError
|
|
218
|
+
nil
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Find the name identifier token immediately after a definition keyword
|
|
222
|
+
# (def, struct, union, enum, flags, variant, type, const, var) for the given name.
|
|
223
|
+
# Returns the identifier Token, or nil if not found.
|
|
224
|
+
def find_definition_token(uri, name, before_line: nil, before_char: nil)
|
|
225
|
+
tokens = get_tokens(uri)
|
|
226
|
+
return nil if tokens.nil?
|
|
227
|
+
|
|
228
|
+
nearest = nil
|
|
229
|
+
tokens.each_cons(2) do |kw_tok, id_tok|
|
|
230
|
+
next unless DEFINITION_KEYWORDS.include?(kw_tok.type)
|
|
231
|
+
next unless id_tok.type == :identifier && id_tok.lexeme == name
|
|
232
|
+
|
|
233
|
+
if before_line
|
|
234
|
+
next if id_tok.line > before_line
|
|
235
|
+
next if id_tok.line == before_line && before_char && id_tok.column >= before_char
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if nearest.nil? || id_tok.line > nearest.line || (id_tok.line == nearest.line && id_tok.column > nearest.column)
|
|
239
|
+
nearest = id_tok
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
return nearest if nearest
|
|
244
|
+
|
|
245
|
+
tokens.each_cons(2) do |kw_tok, id_tok|
|
|
246
|
+
next unless DEFINITION_KEYWORDS.include?(kw_tok.type)
|
|
247
|
+
next unless id_tok.type == :identifier && id_tok.lexeme == name
|
|
248
|
+
|
|
249
|
+
return id_tok
|
|
250
|
+
end
|
|
251
|
+
nil
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
private
|
|
255
|
+
|
|
256
|
+
# ── Symbol extraction (token-based, no AST position requirement) ────────
|
|
257
|
+
|
|
258
|
+
def extract_symbols_from_tokens(uri)
|
|
259
|
+
tokens = get_tokens(uri)
|
|
260
|
+
return [] if tokens.nil?
|
|
261
|
+
|
|
262
|
+
symbols = []
|
|
263
|
+
tokens.each_cons(2) do |kw_tok, id_tok|
|
|
264
|
+
next unless DEFINITION_KEYWORDS.include?(kw_tok.type)
|
|
265
|
+
next unless id_tok.type == :identifier
|
|
266
|
+
|
|
267
|
+
kind = case kw_tok.type
|
|
268
|
+
when :function then 'function'
|
|
269
|
+
when :struct then 'struct'
|
|
270
|
+
when :union then 'union'
|
|
271
|
+
when :enum then 'enum'
|
|
272
|
+
when :flags then 'flags'
|
|
273
|
+
when :variant then 'variant'
|
|
274
|
+
when :type then 'type_alias'
|
|
275
|
+
when :const then 'constant'
|
|
276
|
+
when :let then 'variable'
|
|
277
|
+
when :var then 'variable'
|
|
278
|
+
when :event then 'event'
|
|
279
|
+
when :extending then 'struct'
|
|
280
|
+
when :opaque then 'struct'
|
|
281
|
+
when :interface then 'interface'
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
symbols << {
|
|
285
|
+
name: id_tok.lexeme,
|
|
286
|
+
kind: kind,
|
|
287
|
+
line: id_tok.line,
|
|
288
|
+
column: id_tok.column
|
|
289
|
+
} unless kind == 'variable' && id_tok.lexeme == '_'
|
|
290
|
+
end
|
|
291
|
+
symbols
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def file_backed_source_overrides
|
|
295
|
+
open_documents = @document_state_mutex.synchronize do
|
|
296
|
+
@open_documents.dup
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
open_documents.each_with_object({}) do |(uri, content), overrides|
|
|
300
|
+
path = uri_to_path(uri)
|
|
301
|
+
next unless path
|
|
302
|
+
|
|
303
|
+
overrides[File.expand_path(path)] = content
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def extract_doc_comments_for_definitions(uri)
|
|
308
|
+
content = get_content(uri)
|
|
309
|
+
return {} if content.empty?
|
|
310
|
+
|
|
311
|
+
tokens = get_tokens(uri)
|
|
312
|
+
return {} if tokens.nil?
|
|
313
|
+
|
|
314
|
+
lines = content.split("\n", -1)
|
|
315
|
+
docs_by_location = {}
|
|
316
|
+
|
|
317
|
+
tokens.each_cons(2) do |kw_tok, id_tok|
|
|
318
|
+
next unless DEFINITION_KEYWORDS.include?(kw_tok.type)
|
|
319
|
+
next unless id_tok.type == :identifier
|
|
320
|
+
|
|
321
|
+
docs = extract_doc_comment_for_line(lines, id_tok.line - 1)
|
|
322
|
+
next unless docs
|
|
323
|
+
|
|
324
|
+
docs_by_location[doc_comment_key(id_tok.line, id_tok.column)] = docs
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
docs_by_location
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def extract_doc_comment_for_line(lines, declaration_line)
|
|
331
|
+
index = declaration_line - 1
|
|
332
|
+
return nil if index.negative?
|
|
333
|
+
|
|
334
|
+
docs = []
|
|
335
|
+
while index >= 0
|
|
336
|
+
stripped = lines[index].to_s.strip
|
|
337
|
+
break if stripped.empty?
|
|
338
|
+
break unless stripped.start_with?(DOC_COMMENT_PREFIX)
|
|
339
|
+
|
|
340
|
+
docs << {
|
|
341
|
+
line: index + 1,
|
|
342
|
+
text: stripped.sub(/\A##\s?/, ''),
|
|
343
|
+
}
|
|
344
|
+
index -= 1
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
return nil if docs.empty?
|
|
348
|
+
|
|
349
|
+
build_doc_comment_data(docs.reverse)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def build_doc_comment_data(lines)
|
|
353
|
+
raw_lines = lines.map { |entry| entry[:text].to_s }
|
|
354
|
+
raw_markdown = raw_lines.join("\n")
|
|
355
|
+
|
|
356
|
+
tags = {
|
|
357
|
+
params: [],
|
|
358
|
+
returns: nil,
|
|
359
|
+
throws: [],
|
|
360
|
+
see: [],
|
|
361
|
+
}
|
|
362
|
+
tag_errors = []
|
|
363
|
+
body_lines = []
|
|
364
|
+
|
|
365
|
+
lines.each do |entry|
|
|
366
|
+
line_text = entry[:text].to_s
|
|
367
|
+
match = line_text.match(DOC_TAG_PATTERN)
|
|
368
|
+
unless match
|
|
369
|
+
body_lines << line_text
|
|
370
|
+
next
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
tag_name = match[1].to_s.downcase
|
|
374
|
+
payload = match[2].to_s.strip
|
|
375
|
+
|
|
376
|
+
case tag_name
|
|
377
|
+
when 'param'
|
|
378
|
+
if payload.empty?
|
|
379
|
+
tag_errors << {
|
|
380
|
+
line: entry[:line],
|
|
381
|
+
message: 'doc tag @param requires a parameter name',
|
|
382
|
+
}
|
|
383
|
+
next
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
name_match = payload.match(/\A([A-Za-z_][A-Za-z0-9_]*)(?:\s+(.*))?\z/)
|
|
387
|
+
unless name_match
|
|
388
|
+
tag_errors << {
|
|
389
|
+
line: entry[:line],
|
|
390
|
+
message: 'doc tag @param has an invalid parameter name',
|
|
391
|
+
}
|
|
392
|
+
next
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
tags[:params] << {
|
|
396
|
+
name: name_match[1],
|
|
397
|
+
text: name_match[2].to_s.strip,
|
|
398
|
+
line: entry[:line],
|
|
399
|
+
}
|
|
400
|
+
when 'return', 'returns'
|
|
401
|
+
tags[:returns] = {
|
|
402
|
+
text: payload,
|
|
403
|
+
line: entry[:line],
|
|
404
|
+
}
|
|
405
|
+
when 'throws', 'throw'
|
|
406
|
+
tags[:throws] << {
|
|
407
|
+
text: payload,
|
|
408
|
+
line: entry[:line],
|
|
409
|
+
}
|
|
410
|
+
when 'see'
|
|
411
|
+
tags[:see] << {
|
|
412
|
+
text: payload,
|
|
413
|
+
line: entry[:line],
|
|
414
|
+
}
|
|
415
|
+
else
|
|
416
|
+
tag_errors << {
|
|
417
|
+
line: entry[:line],
|
|
418
|
+
message: "unknown doc tag @#{tag_name}",
|
|
419
|
+
}
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
body_markdown = body_lines.join("\n").strip
|
|
424
|
+
summary_lines = body_markdown.split("\n").slice_before { |line| line.strip.empty? }.first || []
|
|
425
|
+
summary = summary_lines.join("\n").strip
|
|
426
|
+
summary = nil if summary.empty?
|
|
427
|
+
|
|
428
|
+
{
|
|
429
|
+
raw_markdown: raw_markdown,
|
|
430
|
+
summary: summary,
|
|
431
|
+
body_markdown: body_markdown,
|
|
432
|
+
tags: tags,
|
|
433
|
+
tag_errors: tag_errors,
|
|
434
|
+
}
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def doc_comment_key(line, column)
|
|
438
|
+
"#{line}:#{column}"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# ── Offset utilities ────────────────────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
# Convert a 0-based LSP (line, character) pair into a byte offset.
|
|
444
|
+
# LSP positions use UTF-16 code units for +character+.
|
|
445
|
+
def line_char_to_offset(content, line, char)
|
|
446
|
+
lines = content.split("\n", -1)
|
|
447
|
+
clamped_line = [[line.to_i, 0].max, lines.length - 1].min
|
|
448
|
+
|
|
449
|
+
preceding = if clamped_line.zero?
|
|
450
|
+
''
|
|
451
|
+
else
|
|
452
|
+
lines[0...clamped_line].join("\n") + "\n"
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
line_text = lines[clamped_line] || ''
|
|
456
|
+
target_units = [char.to_i, 0].max
|
|
457
|
+
|
|
458
|
+
utf16_units_seen = 0
|
|
459
|
+
byte_index = 0
|
|
460
|
+
line_text.each_char do |ch|
|
|
461
|
+
codepoint = ch.ord
|
|
462
|
+
units = codepoint > 0xFFFF ? 2 : 1
|
|
463
|
+
break if utf16_units_seen + units > target_units
|
|
464
|
+
|
|
465
|
+
utf16_units_seen += units
|
|
466
|
+
byte_index += ch.bytesize
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
within_line = line_text.byteslice(0, byte_index).to_s
|
|
470
|
+
|
|
471
|
+
(preceding + within_line).bytesize
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def uri_to_path(uri)
|
|
475
|
+
parsed = URI.parse(uri)
|
|
476
|
+
return nil unless parsed.scheme == 'file'
|
|
477
|
+
|
|
478
|
+
CGI.unescape(parsed.path)
|
|
479
|
+
rescue URI::InvalidURIError
|
|
480
|
+
nil
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def path_to_uri(path)
|
|
484
|
+
escaped_path = path.split('/').map { |seg| CGI.escape(seg).gsub('+', '%20') }.join('/')
|
|
485
|
+
"file://#{escaped_path}"
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cgi/escape'
|
|
4
|
+
require 'set'
|
|
5
|
+
require 'thread'
|
|
6
|
+
require 'uri'
|
|
7
|
+
|
|
8
|
+
require_relative 'workspace/store'
|
|
9
|
+
require_relative 'workspace/caches'
|
|
10
|
+
require_relative 'workspace/analysis'
|
|
11
|
+
require_relative 'workspace/dependency_graph'
|
|
12
|
+
require_relative 'workspace/definition_index'
|
|
13
|
+
require_relative 'workspace/collection'
|
|
14
|
+
require_relative 'workspace/utilities'
|
|
15
|
+
|
|
16
|
+
module MilkTea
|
|
17
|
+
module LSP
|
|
18
|
+
# Manages open documents, AST cache, token cache, semantic facts cache, and symbol index.
|
|
19
|
+
# Supports incremental document edits and workspace-wide indexing.
|
|
20
|
+
class Workspace
|
|
21
|
+
DOCUMENT_SOURCES = %w[active-editor visible-editor background-document].freeze
|
|
22
|
+
PERF_LOG_THRESHOLD_MS = 1000
|
|
23
|
+
|
|
24
|
+
# Token types that introduce a named definition, in order of precedence.
|
|
25
|
+
#
|
|
26
|
+
# NOTE: this list is intentionally minimal. Multi-keyword prefixes such as
|
|
27
|
+
# 'const function', 'async function', 'external function', 'foreign function',
|
|
28
|
+
# and visibility modifiers ('public') are captured by the adjacent-token scan
|
|
29
|
+
# in extract_symbols_from_tokens because the scan relies on every pair of
|
|
30
|
+
# adjacent tokens, and the inner keyword (e.g. :function) is always followed
|
|
31
|
+
# by the identifier. If the lexer ever merges a compound keyword into a
|
|
32
|
+
# single token type (e.g. :const_function), it must be added here.
|
|
33
|
+
DEFINITION_KEYWORDS = %i[function struct union enum flags variant type const var let extending opaque interface event].freeze
|
|
34
|
+
DOC_COMMENT_PREFIX = '##'
|
|
35
|
+
DOC_TAG_PATTERN = /\A\s*@([A-Za-z_][A-Za-z0-9_-]*)(?:\s+(.*))?\z/
|
|
36
|
+
DEFINITION_LINE_PREFIX = /^(?:\s)*(?:(?:public|foreign|external)\s+)*(?:function|struct|union|enum|flags|variant|type|const|var|let|extending|opaque|interface|event)\s+/m
|
|
37
|
+
DEFINITION_NAME_REGEX = /^\s*(?:(?:public|foreign|external)\s+)*(?:function|struct|union|enum|flags|variant|type|const|var|let|extending|opaque|interface|event)\s+([A-Za-z_][A-Za-z0-9_]*)\b/
|
|
38
|
+
|
|
39
|
+
def initialize
|
|
40
|
+
@workspace_root_path = nil
|
|
41
|
+
@dependency_resolution_mode = :auto
|
|
42
|
+
@platform_override = nil
|
|
43
|
+
@strict_current_root_diagnostics_enabled = false
|
|
44
|
+
@open_documents = {} # uri -> content String from didOpen/didChange
|
|
45
|
+
@indexed_documents = {} # uri -> content String loaded from disk index
|
|
46
|
+
@document_sources = {} # uri -> source string from the editor client
|
|
47
|
+
@document_state_mutex = Mutex.new
|
|
48
|
+
@tokens_cache = {} # uri -> [Token]
|
|
49
|
+
@last_good_tokens_cache = {} # uri -> last known-good [Token]
|
|
50
|
+
@ast_cache = {} # uri -> AST::SourceFile (nil on parse failure)
|
|
51
|
+
@facts_cache = {} # uri -> SemanticAnalyzer::Facts (projection of cached tooling snapshot facts)
|
|
52
|
+
@tooling_snapshot_cache = {} # uri -> SemanticAnalyzer::ToolingSnapshot (facts may be nil on structural failure)
|
|
53
|
+
@symbols_cache = {} # uri -> [{name, kind, line, column}]
|
|
54
|
+
@doc_comments_cache = {} # uri -> {"line:column" => structured_doc_comment_hash}
|
|
55
|
+
@last_good_facts_cache = {} # uri -> last SemanticAnalyzer::Facts that succeeded
|
|
56
|
+
@last_good_tooling_snapshot_cache = {} # uri -> last SemanticAnalyzer::ToolingSnapshot with facts that succeeded
|
|
57
|
+
@document_module_names = {} # uri -> module name string (populated from last-good facts)
|
|
58
|
+
@shared_module_cache = {}
|
|
59
|
+
@facts_cache_mutex = Mutex.new
|
|
60
|
+
@facts_generation = Hash.new(0)
|
|
61
|
+
@facts_state_mutex = Mutex.new
|
|
62
|
+
# Diagnostics cache: uri -> { content_hash:, diagnostics: }
|
|
63
|
+
@diagnostics_cache = {}
|
|
64
|
+
@dependency_module_name_by_uri = {}
|
|
65
|
+
@dependency_imports_by_uri = {}
|
|
66
|
+
@reverse_import_dependents = Hash.new { |hash, key| hash[key] = Set.new }
|
|
67
|
+
@full_reverse_index_built = false
|
|
68
|
+
@full_reverse_index_built = false
|
|
69
|
+
# Definition index: name -> { uri:, token: } — built lazily from symbols cache.
|
|
70
|
+
# Caches known matching definitions without forcing a full-workspace index
|
|
71
|
+
# build on the first global lookup.
|
|
72
|
+
@definition_index = {} # name -> [{ uri:, token: Token }]
|
|
73
|
+
@definition_miss_cache = Set.new
|
|
74
|
+
@definition_candidate_uris = Hash.new { |hash, key| hash[key] = Set.new }
|
|
75
|
+
@definition_names_by_uri = {}
|
|
76
|
+
@definition_cache_mutex = Mutex.new
|
|
77
|
+
@definition_warmup_queue = Queue.new
|
|
78
|
+
@definition_warmup_enqueued = Set.new
|
|
79
|
+
@definition_warmup_thread = nil
|
|
80
|
+
# Identifier index: name -> [{uri:, line:, col:}] — lazily populated from tokens
|
|
81
|
+
@identifier_index = {}
|
|
82
|
+
@identifier_index_mutex = Mutex.new
|
|
83
|
+
@indexed_uris = Set.new
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def reset
|
|
87
|
+
@workspace_root_path = nil
|
|
88
|
+
@dependency_resolution_mode = :auto
|
|
89
|
+
@platform_override = nil
|
|
90
|
+
@strict_current_root_diagnostics_enabled = false
|
|
91
|
+
@indexed_documents.clear
|
|
92
|
+
@tokens_cache.clear
|
|
93
|
+
@last_good_tokens_cache.clear
|
|
94
|
+
@ast_cache.clear
|
|
95
|
+
@facts_cache.clear
|
|
96
|
+
@tooling_snapshot_cache.clear
|
|
97
|
+
@symbols_cache.clear
|
|
98
|
+
@doc_comments_cache.clear
|
|
99
|
+
@last_good_facts_cache.clear
|
|
100
|
+
@last_good_tooling_snapshot_cache.clear
|
|
101
|
+
@document_module_names.clear
|
|
102
|
+
@shared_module_cache.clear
|
|
103
|
+
@definition_index.clear
|
|
104
|
+
@identifier_index.clear
|
|
105
|
+
@full_reverse_index_built = false
|
|
106
|
+
@indexed_uris.clear
|
|
107
|
+
@definition_miss_cache.clear
|
|
108
|
+
@definition_candidate_uris.clear
|
|
109
|
+
@definition_names_by_uri.clear
|
|
110
|
+
@definition_warmup_enqueued.clear
|
|
111
|
+
@dependency_module_name_by_uri.clear
|
|
112
|
+
@dependency_imports_by_uri.clear
|
|
113
|
+
@reverse_import_dependents.clear
|
|
114
|
+
@diagnostics_cache.clear
|
|
115
|
+
@facts_generation.clear
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
include WorkspaceStore
|
|
119
|
+
include WorkspaceCaches
|
|
120
|
+
include WorkspaceAnalysis
|
|
121
|
+
include WorkspaceDependencyGraph
|
|
122
|
+
include WorkspaceDefinitionIndex
|
|
123
|
+
include WorkspaceCollection
|
|
124
|
+
include WorkspaceUtilities
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
data/lib/milk_tea/lsp.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module LSP
|
|
5
|
+
# LSP implementation - language server protocol support
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative 'lsp/protocol'
|
|
10
|
+
require_relative 'lsp/dependency_resolution'
|
|
11
|
+
require_relative 'lsp/workspace'
|
|
12
|
+
require_relative 'lsp/diagnostics'
|
|
13
|
+
require_relative 'lsp/server'
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
|
|
6
|
+
module MilkTea
|
|
7
|
+
module PackageAtomicWrite
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def write(path, content, binmode: false)
|
|
11
|
+
open(path, binmode:) do |file|
|
|
12
|
+
file.write(content)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def open(path, binmode: false)
|
|
17
|
+
expanded_path = File.expand_path(path)
|
|
18
|
+
directory = File.dirname(expanded_path)
|
|
19
|
+
|
|
20
|
+
FileUtils.mkdir_p(directory)
|
|
21
|
+
|
|
22
|
+
Tempfile.create(["milk-tea-package", File.extname(expanded_path)], directory) do |file|
|
|
23
|
+
file.binmode if binmode
|
|
24
|
+
yield file
|
|
25
|
+
|
|
26
|
+
unless file.closed?
|
|
27
|
+
file.flush
|
|
28
|
+
file.fsync
|
|
29
|
+
file.close
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
replace(file.path, expanded_path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
expanded_path
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def replace(source_path, destination_path)
|
|
39
|
+
File.rename(source_path, destination_path)
|
|
40
|
+
rescue Errno::EACCES, Errno::EEXIST, Errno::EPERM
|
|
41
|
+
replace_via_backup(source_path, destination_path)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def replace_via_backup(source_path, destination_path)
|
|
45
|
+
backup_path = "#{destination_path}.bak.#{$$}.#{Thread.current.object_id}"
|
|
46
|
+
|
|
47
|
+
File.rename(destination_path, backup_path) if File.exist?(destination_path)
|
|
48
|
+
|
|
49
|
+
begin
|
|
50
|
+
File.rename(source_path, destination_path)
|
|
51
|
+
rescue SystemCallError
|
|
52
|
+
if File.exist?(backup_path) && !File.exist?(destination_path)
|
|
53
|
+
File.rename(backup_path, destination_path)
|
|
54
|
+
end
|
|
55
|
+
raise
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
File.delete(backup_path) if File.exist?(backup_path)
|
|
59
|
+
rescue Errno::ENOENT
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|