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,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
|