Class: MilkTea::LSP::Server

Inherits:
Object
  • Object
show all
Includes:
ServerCallHierarchy, ServerCodeActions, ServerCodeLens, ServerCompletion, ServerConfiguration, ServerDebugInfo, ServerDefinition, ServerDiagnosticsScheduling, ServerExecuteCommand, ServerFoldingRange, ServerFormatting, ServerHover, ServerInlayHints, ServerLifecycle, ServerLinkedEditingRange, ServerOnTypeFormatting, ServerProgress, ServerReferences, ServerRename, ServerSelectionRange, ServerSemanticTokens, ServerSignatureHelp, ServerTextDocuments, ServerTypeHierarchy, ServerUtilities
Defined in:
lib/milk_tea/lsp/server.rb,
lib/milk_tea/lsp/server/hover.rb,
lib/milk_tea/lsp/server/rename.rb,
lib/milk_tea/lsp/server/progress.rb,
lib/milk_tea/lsp/server/code_lens.rb,
lib/milk_tea/lsp/server/lifecycle.rb,
lib/milk_tea/lsp/server/utilities.rb,
lib/milk_tea/lsp/server/completion.rb,
lib/milk_tea/lsp/server/debug_info.rb,
lib/milk_tea/lsp/server/definition.rb,
lib/milk_tea/lsp/server/formatting.rb,
lib/milk_tea/lsp/server/references.rb,
lib/milk_tea/lsp/server/inlay_hints.rb,
lib/milk_tea/lsp/server/code_actions.rb,
lib/milk_tea/lsp/server/configuration.rb,
lib/milk_tea/lsp/server/folding_range.rb,
lib/milk_tea/lsp/server/call_hierarchy.rb,
lib/milk_tea/lsp/server/signature_help.rb,
lib/milk_tea/lsp/server/text_documents.rb,
lib/milk_tea/lsp/server/type_hierarchy.rb,
lib/milk_tea/lsp/server/execute_command.rb,
lib/milk_tea/lsp/server/selection_range.rb,
lib/milk_tea/lsp/server/semantic_tokens.rb,
lib/milk_tea/lsp/server/on_type_formatting.rb,
lib/milk_tea/lsp/server/linked_editing_range.rb,
lib/milk_tea/lsp/server/diagnostics_scheduling.rb

Overview

Main LSP server — JSON-RPC 2.0 message loop with full MVP feature set.

Enhancements implemented:

1. Hover         — real type signature from semantic facts
2. Goto Def      — token-based definition site navigation
3. Incremental   — textDocumentSync: 2 with range-based edits
4. Multi-file    — workspace indexing via workspace/symbol
5. Completion    — function/type/value completions from facts

Defined Under Namespace

Modules: ServerCallHierarchy, ServerCodeActions, ServerCodeLens, ServerCompletion, ServerConfiguration, ServerDebugInfo, ServerDefinition, ServerDiagnosticsScheduling, ServerExecuteCommand, ServerFoldingRange, ServerFormatting, ServerHover, ServerInlayHints, ServerLifecycle, ServerLinkedEditingRange, ServerOnTypeFormatting, ServerProgress, ServerReferences, ServerRename, ServerSelectionRange, ServerSemanticTokens, ServerSignatureHelp, ServerTextDocuments, ServerTypeHierarchy, ServerUtilities Classes: FakeWarning

Constant Summary collapse

SEMANTIC_TOKEN_TYPES =
%w[
  namespace type class enum interface struct typeParameter parameter variable property enumMember event
  function method macro keyword modifier comment string number regexp operator decorator
].freeze
SEMANTIC_TOKEN_MODIFIERS =
%w[
  declaration definition readonly static deprecated abstract async modification documentation defaultLibrary
].freeze
KEYWORD_TOKEN_TYPES =
Token::KEYWORDS.values.to_set.freeze
DEFAULT_LIBRARY_TYPE_NAMES =
Types::BUILTIN_TYPE_NAMES.to_set.freeze
BUILTIN_FUNCTION_NAMES =
%w[
  ref_of const_ptr_of ptr_of read fatal reinterpret array span zero default adapt get field_of callable_of has_attribute attribute_of attribute_arg fields_of members_of attributes_of
].to_set.freeze
BUILTIN_ASSOCIATED_HOOK_NAMES =
%w[hash equal order].to_set.freeze
BUILTIN_CALL_HOVER_INFO =
{
  'fatal' => {
    signature: 'builtin fatal(message) -> never',
    docs: '`fatal(message)` aborts execution with the provided message.'
  },
  'ref_of' => {
    signature: 'builtin ref_of(value) -> ref[T]',
    docs: '`ref_of(x)` borrows a safe lvalue as `ref[T]`.'
  },
  'const_ptr_of' => {
    signature: 'builtin const_ptr_of(value) -> const_ptr[T]',
    docs: '`const_ptr_of(x)` takes the address of a safe lvalue as `const_ptr[T]`.'
  },
  'ptr_of' => {
    signature: 'builtin ptr_of(value) -> ptr[T]',
    docs: '`ptr_of(x)` takes the address of a safe lvalue as `ptr[T]`.'
  },
  'read' => {
    signature: 'builtin read(value) -> T',
    docs: '`read(value)` projects a `ref[T]` or pointer-like value to its referent.'
  },
  'adapt' => {
    signature: 'builtin adapt[Interface](value) -> dyn[Interface]',
    docs: '`adapt[Interface](value)` constructs a `dyn[Interface]` runtime interface value. Compile-time verified: the value\'s type must implement the interface.'
  },
  'reinterpret' => {
    signature: 'builtin reinterpret[T](value) -> T',
    docs: '`reinterpret[T](value)` reinterprets the bits of `value` as type `T` without conversion. Requires `unsafe`.'
  },
  'array' => {
    signature: 'builtin array[T, N](elements...) -> array[T, N]',
    docs: '`array[T, N]` constructs a fixed-size array from elements. Omitted trailing elements default to zero.'
  },
  'span' => {
    signature: 'builtin span[T](data, len) -> span[T]',
    docs: '`span[T](data = ..., len = ...)` constructs a borrowed pointer-plus-length view over contiguous memory.'
  },
  'zero' => {
    signature: 'builtin zero[T] -> T',
    docs: '`zero[T]` returns a zero-initialized value of type `T`. All bits are set to zero.'
  },
  'default' => {
    signature: 'builtin default[T] -> T',
    docs: '`default[T]` calls `T.default()` to produce a semantic default value. Requires an accessible `default()` associated function on `T`.'
  },
  'get' => {
    signature: 'builtin get(collection, index) -> ptr[T]?',
    docs: '`get(collection, index)` performs recoverable bounds-checked indexing, returning `ptr[T]?` (null on out-of-bounds) instead of aborting.'
  },
  'attribute_arg' => {
    signature: 'builtin attribute_arg[T](handle) -> T',
    docs: '`attribute_arg[T](handle)` returns the `T`-typed argument of a resolved `attribute_handle`. The attribute must declare an argument of type `T`.'
  },
  'field_of' => {
    signature: 'builtin field_of(Type, field_name) -> field_handle',
    docs: '`field_of(Type, field_name)` returns a compile-time handle for the named field on a struct type.'
  },
  'callable_of' => {
    signature: 'builtin callable_of(name) -> callable_handle',
    docs: '`callable_of(name)` returns a compile-time handle for a callable declaration name.'
  },
  'has_attribute' => {
    signature: 'builtin has_attribute(target, attribute_name) -> bool',
    docs: '`has_attribute(target, attribute_name)` checks at compile time whether the resolved attribute is applied to the target.'
  },
  'attribute_of' => {
    signature: 'builtin attribute_of(target, attribute_name) -> attribute_handle',
    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.'
  },
  'fields_of' => {
    signature: 'builtin fields_of(Type) -> array[field_handle, N]',
    docs: '`fields_of(Type)` returns all struct fields as a compile-time `array[field_handle, N]`, in declaration order. Use with `inline for` for reflective field iteration.'
  },
  'members_of' => {
    signature: 'builtin members_of(Type) -> array[member_handle, N]',
    docs: '`members_of(Type)` returns all members of an enum or flags type as a compile-time `array[member_handle, N]`. Each handle exposes `.name` and `.value`.'
  },
  'attributes_of' => {
    signature: 'builtin attributes_of(target [, name]) -> array[attribute_handle, N]',
    docs: '`attributes_of(target)` returns all attributes applied to a type, field, or callable as a compile-time array. `attributes_of(target, name)` filters by attribute kind.'
  },
}.freeze
OPERATOR_TOKEN_TYPES =
%i[
  amp colon comma caret dot lparen rparen pipe lbracket rbracket question
  equal plus minus star slash percent less greater tilde
  arrow shift_left shift_right plus_equal minus_equal star_equal slash_equal percent_equal
  amp_equal pipe_equal caret_equal shift_left_equal shift_right_equal
  equal_equal bang_equal less_equal greater_equal ellipsis
  at dot_dot
].to_set.freeze
DIAGNOSTICS_WORKER_COUNT =
Integer(ENV.fetch('MILK_TEA_LSP_DIAGNOSTICS_WORKERS', '2')).clamp(1, 8)

Constants included from ServerUtilities

ServerUtilities::MESSAGE_TYPES

Constants included from ServerTypeHierarchy

ServerTypeHierarchy::TYPE_KIND_MAP

Constants included from ServerProgress

ServerProgress::PROGRESS_TOKEN_PREFIX

Constants included from ServerOnTypeFormatting

ServerOnTypeFormatting::BLOCK_INTRODUCING_KEYWORDS

Constants included from ServerLifecycle

ServerLifecycle::SEMANTIC_TOKENS_REFRESH_THROTTLE_MS

Constants included from ServerHover

ServerHover::BUILTIN_TYPE_DOCS, ServerHover::BUILTIN_TYPE_METHOD_SIGNATURES, ServerHover::KEYWORD_HOVER_INFO, ServerHover::LANGUAGE_KEYWORD_HOVER_INFO

Constants included from ServerFoldingRange

ServerFoldingRange::BLOCK_START_PATTERN, ServerFoldingRange::CONTINUATION_KEYWORDS

Constants included from ServerCompletion

ServerCompletion::COMPLETION_KEYWORDS, ServerCompletion::MAX_COMPLETION_ITEMS, ServerCompletion::TYPE_CONSTRUCTOR_KEYWORDS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ServerUtilities

#clear_cancelled_request, #collect_call_argument_starts, #current_word_prefix, #decode_client_char, #diagnostics_fingerprint, #elapsed_ms, #encode_char_for_client, #format_document_symbol, #format_symbol, #handle_did_change_watched_files, #hget, #invalidate_document_caches, #invalidate_document_caches_for, #library_uri?, #log_message, #log_perf_breakdown, #log_request_stage_breakdown, #measure_perf_stage, #monotonic_time, #new_perf_stages, #next_diagnostic_result_id, #next_non_trivia_token, #path_to_uri, #perf_breakdown_logging?, #perf_log_context, #perf_logging?, #perf_verbose?, #position_in_range?, #refresh_open_document_dependency_state, #request_cancelled?, #self_describing_argument_expression?, #shorten_uri, #show_message, #show_message_request, #simple_identifier_like_argument_expression?, #skip_expensive_source_fix_all?, #skip_expensive_work_reason, #summarize_lsp_params, #symbol_kind, #token_end_position, #token_to_range, #uri_to_path, #utf32_to_utf16_char, #utf8_to_utf16_char

Methods included from ServerTypeHierarchy

#handle_prepare_type_hierarchy, #handle_subtypes, #handle_supertypes

Methods included from ServerTextDocuments

#handle_did_change, #handle_did_close, #handle_did_open, #handle_did_save, #handle_document_context, #handle_will_save_wait_until

Methods included from ServerSignatureHelp

#build_signature_from_binding, #handle_signature_help, #resolve_this_receiver_type, #signature_help_bindings_for_name

Methods included from ServerSemanticTokens

#bare_builtin_specialization?, #bare_function_value_identifier_site?, #build_attribute_name_semantic_overrides, #build_semantic_token_entries, #callable_field_member_access?, #callable_parameter_declaration_token?, #callable_semantic_type?, #classify_name_semantic, #classify_semantic_token, #collect_generic_function_local_scopes, #compute_semantic_tokens_edits, #constructible_semantic_type?, #declaration_scope_end_line, #destructure_let_binding?, #dot_nested_type_member?, #dot_type_receiver_info, #embedded_heredoc_token?, #encode_semantic_tokens, #field_declaration_token?, #find_semantic_token_common_prefix, #find_semantic_token_common_suffix, #first_non_trivia_token_on_line?, #fstring_interpolation_entries, #generic_binding_scope, #generic_callable_lexical_scopes, #generic_function_binding_for_line, #generic_function_bindings, #generic_function_declaration?, #generic_function_lexical_binding_kind_at, #generic_function_lexical_binding_scopes, #generic_function_lexical_binding_semantic, #generic_function_lexical_scopes_for_declaration, #generic_method_lexical_scopes, #generic_statement_end_line, #generic_statement_list_end_line, #generic_type_parameter_header_token?, #generic_type_parameter_names_for_declaration, #generic_type_parameter_names_for_extending_block, #handle_semantic_tokens_delta, #handle_semantic_tokens_full, #handle_semantic_tokens_range, #identifier_in_type_argument_position?, #identifier_in_type_parameter_reference_position?, #identifier_in_type_reference_position?, #import_path_info_at, #imported_module_binding_for_member, #imported_module_function_value_member_access_site?, #known_type_name?, #lexically_declared_free_function_name?, #lexically_declared_free_function_names, #local_semantic_value_binding, #mark_attribute_name_override, #mark_attribute_reflection_name_overrides, #match_arm_binding_token?, #matching_closer_index, #matching_opener_index, #module_declaration_info_at, #module_declaration_path_token?, #named_argument_label_in_type_constructor?, #named_argument_label_token?, #namespace_keyword_token?, #nested_declaration_scope_end_line, #next_non_trivia_token_index, #next_semantic_token_result_id, #non_trivia_tokens_on_line, #parameter_declaration_token?, #parameter_list_opener_index, #previous_non_trivia_token, #previous_non_trivia_token_index, #resolve_receiver_value_type, #resolve_struct_type_name, #resolved_call_callee_semantic, #semantic_modifiers_bitset, #semantic_token_entry_equal?, #semantic_value_binding_entry, #simple_type_parameter_name_from_type_argument, #specialized_call_with_type_args?, #static_type_member_access?, #struct_event_declaration_token?, #token_semantic_entries, #type_name_member_access?, #type_name_receiver_token?, #type_parameter_declaration_info_on_line, #type_parameter_declaration_token?, #type_parameter_names_in_scope, #type_parameter_reference_token?, #type_parameter_scopes_for_declaration, #variant_enum_member_declaration?, #variant_payload_field_declaration_token?, #walk_ast_nodes

Methods included from ServerSelectionRange

#build_selection_range, #find_enclosing_block_range, #find_statement_range, #handle_selection_range, #token_bounds_at

Methods included from ServerRename

#allow_hover_last_good_fallback?, #ast_name_range, #collect_struct_field_changes, #declaration_name_range, #definition_identity_key, #each_ast_node, #enum_member_rename_changes, #find_declaration_ast_node_at, #find_extending_type_for_method, #find_identifier_ast_node_at, #find_method_at_position, #find_struct_containing_field, #handle_prepare_rename, #handle_rename, #import_alias_rename_changes, #lexical_rename_changes_in_document, #local_binding_declaration_node?, #local_binding_id?, #method_rename_changes, #name_like_token?, #rename_target_binding_id, #scoped_binding_occurrence_ranges, #scoped_local_reference_locations, #scoped_rename_changes, #skip_trivia_forward, #struct_field_cursor_context?, #struct_field_edit_context?, #struct_field_rename_changes, #workspace_symbol_identity_rename_changes

Methods included from ServerLinkedEditingRange

#handle_linked_editing_range

Methods included from ServerExecuteCommand

#handle_execute_command

Methods included from ServerReferences

#declaration_name_range_fallback, #find_member_column_in_line, #get_content_line, #handle_document_highlight, #handle_document_link, #handle_document_link_resolve, #handle_references, #handle_workspace_symbol, #module_level_ast_name?, #module_level_declaration_node?, #module_level_name?, #module_level_name_kind, #module_level_reference_locations, #path_like_string_literal?, #resolve_document_link_path, #resource_document_link

Methods included from ServerProgress

#create_progress, #create_progress_handle, #next_progress_token_id

Methods included from ServerOnTypeFormatting

#handle_on_type_formatting

Methods included from ServerLifecycle

#handle_cancel_request, #handle_did_change_configuration, #handle_did_change_workspace_folders, #handle_exit, #handle_initialize, #handle_initialized, #handle_shutdown, #handle_will_rename_files, #negotiate_position_encoding, #refresh_client_semantic_tokens

Methods included from ServerInlayHints

#collect_function_defs, #collect_inferred_return_hints, #collect_inferred_type_hints, #collect_local_decls, #collect_parameter_name_hints, #describe_type_for_hint, #handle_inlay_hint

Methods included from ServerHover

#append_structured_doc_comment_markdown, #attribute_target_token?, #builtin_call_hover_info, #builtin_hover_info, #builtin_in_member_access_context?, #builtin_keyword_hover_info, #builtin_specialized_call_hover_info, #builtin_type_constructor_hover_info, #builtin_value_specialization_info, #call_argument_token?, #completion_function_documentation, #declared_generic_local_hover_binding, #doc_tag_param_descriptions, #doc_tag_return_description, #embedded_heredoc_or_format_heredoc_token?, #enclosing_completion_frame, #field_declaration_receiver_info, #field_hover_signature, #find_member_in_types, #find_method_entry, #find_nested_type_by_short_name, #find_variant_arm_in_types, #for_binding_context_at, #format_params, #fstring_interpolation_token_context, #handle_hover, #hover_definition_entry_from_location, #hover_doc_comment_data_for_definition, #hover_doc_comment_for_definition, #hover_source_label, #hover_source_label_for_definition, #hover_source_label_from_location, #hover_source_line_for_definition, #hover_source_line_from_location, #hover_source_uri_for_definition, #hover_source_uri_from_location, #interface_method_signature, #interface_signature, #interpolation_expression_tokens, #keyword_member_access_token?, #latest_completion_snapshot, #member_access_chain_at, #member_access_token?, #member_method_info_for_receiver_type, #method_binding_at_token, #method_signature, #named_argument_callee_name, #named_argument_label_receiver_info, #named_argument_param_result, #named_argument_parameter_info, #param_doc_for_name, #parameter_declaration_token?, #render_builtin_specialization, #render_hover_markdown, #resolve_as_binding_declaration_hover_binding, #resolve_dot_receiver_value_type, #resolve_enum_member_access_info, #resolve_enum_member_definition_location, #resolve_enum_member_hover_info, #resolve_field_declaration_hover_info, #resolve_for_binding_hover_info, #resolve_hover_info, #resolve_lexical_local_hover_signature, #resolve_local_hover_binding, #resolve_local_hover_type, #resolve_member_access_hover_info, #resolve_named_argument_label_hover_info, #same_line_future_completion_snapshot, #shorten_qualified_types, #signature_help_doc_comment_for_call, #signature_help_markdown_for_doc_comment, #token_contains_position?, #token_context_at, #type_hover_signature, #value_hover_signature

Methods included from ServerFormatting

#child_event_symbol, #child_field_symbol, #child_member_symbol, #child_method_symbol, #child_nested_struct_symbol, #child_parent_name, #child_symbols_for, #child_variant_arm_symbol, #collect_local_decls, #collect_nested_type_names, #descendant_names, #enrich_with_children, #expand_generic_type_params, #handle_document_symbols, #handle_formatting, #handle_range_formatting, #local_decl_symbol, #proc_signature_detail, #resolve_outline_module_name, #symbol_line, #type_detail_string, #type_param_child_symbol, #wrap_in_module_hierarchy

Methods included from ServerFoldingRange

#block_start?, #compute_block_folds, #compute_comment_folds, #compute_import_folds, #continuation_start?, #folding_range, #handle_folding_range, #strip_comment, #trim_trailing_blank_lines

Methods included from ServerDiagnosticsScheduling

#cancel_diagnostics, #collect_diagnostics_for_content, #dependency_export_surface_fingerprint, #dependency_import_fingerprint, #dependency_refresh_required_for_edit?, #drain_diagnostics_queue, #lint_tier_rank, #more_strict_lint_tier, #notify_diagnostic_errors, #process_diagnostics_for_uri, #schedule_diagnostics, #semantic_tokens_allow_last_good_fallback?, #start_diagnostics_workers, #stop_diagnostics_workers

Methods included from ServerDefinition

#definition_file_ast, #definition_file_mtime_key, #definition_file_tokens, #definition_lookup_tokens, #enum_member_definition_location, #enum_member_value_text, #field_definition_location, #find_definition_token_in_file, #find_enum_member_token_in_body, #find_enum_member_token_in_file, #find_field_token_in_body, #find_field_token_in_type, #find_local_decl_node, #find_local_declaration_ast_node, #handle_declaration, #handle_definition, #handle_definition_request, #handle_implementation, #handle_type_definition, #imported_module_name_from_ast, #interface_implementation_locations, #interface_method_implementation_locations, #interface_receiver_definition_location, #is_builtin_name?, #local_type_definition_token, #module_definition_location, #module_member_binding_location, #module_member_definition_location, #module_path_for_name, #receiver_module_name, #resolve_definition_location, #resolve_field_member_definition_location, #resolve_implementation_locations, #resolve_interface_binding_at_position, #resolve_interface_method_target_at_token, #resolve_type_definition_location, #same_interface_binding?

Methods included from ServerConfiguration

#apply_configuration_settings, #apply_dependency_resolution_mode, #apply_platform_override, #apply_strict_current_root_diagnostics, #dependency_resolution_mode_from_settings, #formatter_mode_from_settings, #platform_override_from_settings, #pull_client_configuration, #strict_current_root_diagnostics_from_settings

Methods included from ServerCompletion

#attribute_completions, #completion_data, #completion_items_for_type_receiver, #completion_items_for_value_receiver, #field_owner_type, #field_type_from_struct_definition, #format_string_completions, #handle_completion, #handle_completion_resolve, #import_completions, #label_details_for_params, #location_matches_reference?, #method_dispatch_receiver_type_for_completion, #methods_for_receiver_type, #module_dir_contains_mt?, #module_member_access_info, #named_argument_completions, #pointer_type_name?, #project_field_receiver_type_for_completion, #project_method_receiver_type_for_completion, #project_receiver_type_for_completion, #ref_type_name?, #resolve_nested_type_binding, #resolve_static_type_receiver_method, #resolve_static_type_reference_target, #resolve_type_receiver_info, #same_location?, #snippet_for_callable, #specialization_completions, #static_method_binding_at_token, #static_method_binding_for_receiver, #static_type_method_reference?, #static_type_method_references, #value_chain_completions

Methods included from ServerDebugInfo

#handle_debug_info

Methods included from ServerCodeLens

#handle_code_lens, #handle_code_lens_resolve, #static_method_code_lens_count

Methods included from ServerCodeActions

#find_match_end_line, #handle_code_action, #handle_document_diagnostic, #handle_workspace_diagnostic, #refresh_workspace_diagnostics

Methods included from ServerCallHierarchy

#handle_incoming_calls, #handle_outgoing_calls, #handle_prepare_call_hierarchy

Constructor Details

#initialize(protocol: Protocol) ⇒ Server

Returns a new instance of Server.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/milk_tea/lsp/server.rb', line 197

def initialize(protocol: Protocol)
  @protocol = protocol
  @workspace = Workspace.new
  @format_mode = :tidy
  @dependency_resolution_mode = :auto
  @platform_override = nil
  @position_encoding = 'utf-16'
  @handlers = {}
  @diagnostic_report_cache = {}
  @workspace_diagnostic_cache = {}
  @semantic_tokens_cache = {}
  @semantic_tokens_delta_cache = {}
  @fixall_cache = {}
  @definition_file_token_cache = {}
  @definition_file_ast_cache = {}
  @diagnostics_perf = {
    scheduled: 0,
    skipped_unchanged: 0,
    cancelled: 0,
    dequeued: 0,
    collected_for_pull: 0,
    published: 0,
    dropped_stale: 0,
    requeued: 0,
    queue_peak: 0,
  }
  @diagnostics_mutex = Mutex.new
  @diagnostics_pending = {}
  @diagnostics_enqueued = Set.new
  @diagnostics_generation = Hash.new(0)
  @diagnostics_last_scheduled_hash = {}
  @diagnostics_queue = Queue.new
  @diagnostics_workers = []
  @cancelled_requests_mutex = Mutex.new
  @cancelled_request_ids = Set.new
  @_initialize_called = false
  @_indexing_thread = nil
  Diagnostics.protocol = @protocol
  Diagnostics.position_encoding = @position_encoding
  register_handlers
  start_diagnostics_workers
end

Class Method Details

.semantic_tokens_for_path(path, module_roots: nil, package_graph: nil) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/milk_tea/lsp/server.rb', line 156

def self.semantic_tokens_for_path(path, module_roots: nil, package_graph: nil)
  expanded_path = File.expand_path(path)
  roots = module_roots || MilkTea::ModuleRoots.roots_for_path(expanded_path)
  source = File.read(expanded_path)
  tokens = MilkTea::Lexer.lex(source, path: expanded_path)
  facts = MilkTea::ModuleLoader.new(
    module_roots: roots,
    package_graph: package_graph || load_package_graph(expanded_path),
  ).check_file(expanded_path)

  helper = allocate
  entries = helper.send(:build_semantic_token_entries, tokens, facts)
  data = helper.send(:encode_semantic_tokens, entries)

  {
    path: expanded_path,
    moduleName: facts.module_name,
    legend: {
      tokenTypes: SEMANTIC_TOKEN_TYPES,
      tokenModifiers: SEMANTIC_TOKEN_MODIFIERS,
    },
    data: data,
    entries: entries.map do |entry|
      {
        line: entry[:line],
        startChar: entry[:start_char],
        length: entry[:length],
        tokenType: entry[:type].to_s,
        modifiers: entry[:modifiers].map(&:to_s),
      }
    end,
  }
end

Instance Method Details

#handle_notification(method_name, params) ⇒ Object



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/milk_tea/lsp/server.rb', line 406

def handle_notification(method_name, params)
  handler = @handlers[method_name]
  return unless handler

  begin
    t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    handler.call(params)
    elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
    if perf_logging? && (perf_verbose? || elapsed_ms > Workspace::PERF_LOG_THRESHOLD_MS)
      detail = perf_log_context(method_name, params, verbose: perf_verbose?)
      warn "[LSP perf] ntf #{method_name} #{elapsed_ms}ms#{detail}"
    end
  rescue StandardError => e
    log_message(:warning, "Error in notification handler for #{method_name}: #{e.message}")
  end
end

#handle_request(method_name, params, id) ⇒ Object



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
# File 'lib/milk_tea/lsp/server.rb', line 369

def handle_request(method_name, params, id)
  if request_cancelled?(id)
    clear_cancelled_request(id)
    @protocol.write_error(id, -32_800, 'Request cancelled')
    return
  end

  handler = @handlers[method_name]
  if handler.nil?
    @protocol.write_error(id, -32_601, 'Method not found')
    return
  end

  begin
    @current_request_id = id
    t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    result = handler.call(params)
    elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
    if perf_logging? && (perf_verbose? || elapsed_ms > Workspace::PERF_LOG_THRESHOLD_MS)
      detail = perf_log_context(method_name, params, verbose: perf_verbose?)
      warn "[LSP perf] req #{method_name} #{elapsed_ms}ms id=#{id}#{detail}"
    end
    if request_cancelled?(id)
      clear_cancelled_request(id)
      @protocol.write_error(id, -32_800, 'Request cancelled')
    else
      @protocol.write_response(id, result)
    end
  rescue StandardError => e
    log_message(:warning, "Error in handler for #{method_name}: #{e.message}")
    @protocol.write_error(id, -32_603, "Internal error: #{e.message}")
  ensure
    clear_cancelled_request(id)
    @current_request_id = nil
  end
end

#named_argument_definition_location(uri, name, tokens, token_index, stages: nil) ⇒ Object



741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
# File 'lib/milk_tea/lsp/server/definition.rb', line 741

def named_argument_definition_location(uri, name, tokens, token_index, stages: nil)
  return nil unless named_argument_label_token?(tokens, token_index)

  callee_name = named_argument_callee_name(tokens, token_index)
  return nil unless callee_name

  facts = measure_perf_stage(stages, 'definition_facts') { @workspace.get_facts(uri) }
  return nil unless facts

  func = facts.functions[callee_name]
  unless func
    facts.methods.each_value do |methods|
      method = methods[callee_name] || methods["static:#{callee_name}"]
      if method
        func = method
        break
      end
    end
  end
  return nil unless func

  param = func.ast.params.find { |p| p.name == name }
  return nil unless param

  range = ast_name_range(name, param.line, param.column)
  return nil unless range

  {
    uri: uri,
    range: {
      start: { line: range[:line] - 1, character: range[:column] - 1 },
      end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
    },
  }
end

#named_argument_label_references(uri, name, tokens, token_index, include_declaration:, stages: nil) ⇒ Object



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/milk_tea/lsp/server/references.rb', line 401

def named_argument_label_references(uri, name, tokens, token_index, include_declaration:, stages: nil)
  return nil unless named_argument_label_token?(tokens, token_index)

  callee_name = named_argument_callee_name(tokens, token_index)
  return nil unless callee_name

  facts = measure_perf_stage(stages, 'nav_ref_facts') { @workspace.get_facts(uri) }
  return nil unless facts

  func = facts.functions[callee_name]
  unless func
    facts.methods.each_value do |methods|
      method = methods[callee_name] || methods["static:#{callee_name}"]
      if method
        func = method
        break
      end
    end
  end
  return nil unless func

  param = func.ast.params.find { |p| p.name == name }
  return nil unless param

  range = declaration_name_range(param)
  unless range
    range = ast_name_range(name, param.line, param.column)
  end
  return [] unless range

  declaration_location = {
    uri: uri,
    range: {
      start: { line: range[:line] - 1, character: range[:column] - 1 },
      end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
    },
  }

  refs = if include_declaration
           [declaration_location]
         else
           []
         end

  mock_token = Struct.new(:lexeme, :type, :line, :column).new(name, :identifier, param.line, param.column)
  scoped_refs = scoped_local_reference_locations(uri, mock_token, param.line - 1, param.column - 1, facts, include_declaration: false)
  if scoped_refs
    refs.concat(scoped_refs)
  else
    refs
  end
end

#process_message(message) ⇒ Object

── Message dispatch ─────────────────────────────────────────────────────



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/milk_tea/lsp/server.rb', line 352

def process_message(message)
  method_name = message['method']
  params = message['params'] || {}
  id     = message['id']

  if message.key?('id') && !message.key?('method')
    Protocol.handle_response(message)
  elsif message.key?('id')
    handle_request(method_name, params, id)
  else
    handle_notification(method_name, params)
  end
rescue StandardError => e
  log_message(:warning, "Error processing message: #{e.message}")
  @protocol.write_error(id, -32_603, 'Internal server error') if id && message.key?('method')
end

#register_handlersObject

── Handler registration ─────────────────────────────────────────────────



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/milk_tea/lsp/server.rb', line 286

def register_handlers
  # Lifecycle
  @handlers['initialize']  = method(:handle_initialize)
  @handlers['initialized'] = method(:handle_initialized)
  @handlers['shutdown']    = method(:handle_shutdown)
  @handlers['exit']        = method(:handle_exit)
  @handlers['$/cancelRequest'] = method(:handle_cancel_request)

  # Text document sync
  @handlers['milkTea/documentContext'] = method(:handle_document_context)
  @handlers['textDocument/didOpen']   = method(:handle_did_open)
  @handlers['textDocument/didChange'] = method(:handle_did_change)
  @handlers['textDocument/didClose']  = method(:handle_did_close)
  @handlers['textDocument/didSave']   = method(:handle_did_save)
  @handlers['textDocument/willSaveWaitUntil'] = method(:handle_will_save_wait_until)

  # IDE features
  @handlers['textDocument/hover']             = method(:handle_hover)
  @handlers['textDocument/definition']        = method(:handle_definition)
  @handlers['textDocument/declaration']       = method(:handle_declaration)
  @handlers['textDocument/typeDefinition']    = method(:handle_type_definition)
  @handlers['textDocument/implementation']    = method(:handle_implementation)
  @handlers['textDocument/references']        = method(:handle_references)
  @handlers['milkTea/debugInfo']             = method(:handle_debug_info)
  @handlers['textDocument/documentLink']      = method(:handle_document_link)
  @handlers['documentLink/resolve']           = method(:handle_document_link_resolve)
  @handlers['textDocument/documentHighlight'] = method(:handle_document_highlight)
  @handlers['textDocument/documentSymbol']    = method(:handle_document_symbols)
  @handlers['textDocument/formatting']        = method(:handle_formatting)
  @handlers['textDocument/rangeFormatting']   = method(:handle_range_formatting)
  @handlers['textDocument/onTypeFormatting']   = method(:handle_on_type_formatting)
  @handlers['textDocument/foldingRange']       = method(:handle_folding_range)
  @handlers['textDocument/completion']        = method(:handle_completion)
  @handlers['completionItem/resolve']         = method(:handle_completion_resolve)
  @handlers['textDocument/codeAction']        = method(:handle_code_action)
  @handlers['textDocument/codeLens']           = method(:handle_code_lens)
  @handlers['codeLens/resolve']                 = method(:handle_code_lens_resolve)
  @handlers['textDocument/prepareCallHierarchy'] = method(:handle_prepare_call_hierarchy)
  @handlers['callHierarchy/incomingCalls']     = method(:handle_incoming_calls)
  @handlers['callHierarchy/outgoingCalls']     = method(:handle_outgoing_calls)
  @handlers['textDocument/inlayHint']         = method(:handle_inlay_hint)
  @handlers['textDocument/semanticTokens/full'] = method(:handle_semantic_tokens_full)
  @handlers['textDocument/semanticTokens/full/delta'] = method(:handle_semantic_tokens_delta)
  @handlers['textDocument/semanticTokens/range'] = method(:handle_semantic_tokens_range)
  @handlers['textDocument/selectionRange']     = method(:handle_selection_range)
  @handlers['textDocument/diagnostic']         = method(:handle_document_diagnostic)
  @handlers['workspace/diagnostic']            = method(:handle_workspace_diagnostic)
  @handlers['textDocument/signatureHelp']     = method(:handle_signature_help)
  @handlers['textDocument/prepareTypeHierarchy'] = method(:handle_prepare_type_hierarchy)
  @handlers['typeHierarchy/supertypes']        = method(:handle_supertypes)
  @handlers['typeHierarchy/subtypes']          = method(:handle_subtypes)
  @handlers['textDocument/prepareRename']     = method(:handle_prepare_rename)
  @handlers['textDocument/rename']            = method(:handle_rename)
  @handlers['textDocument/linkedEditingRange'] = method(:handle_linked_editing_range)

  # Workspace
  @handlers['workspace/symbol'] = method(:handle_workspace_symbol)
  @handlers['workspace/executeCommand'] = method(:handle_execute_command)
  @handlers['workspace/didChangeWorkspaceFolders'] = method(:handle_did_change_workspace_folders)
  @handlers['workspace/didChangeConfiguration'] = method(:handle_did_change_configuration)
  @handlers['workspace/didChangeWatchedFiles'] = method(:handle_did_change_watched_files)
  @handlers['workspace/willRenameFiles'] = method(:handle_will_rename_files)
end

#runObject



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/milk_tea/lsp/server.rb', line 240

def run
  if perf_logging?
    mode = perf_verbose? ? 'verbose' : 'threshold'
    warn "[LSP perf] enabled mode=#{mode} threshold_ms=#{Workspace::PERF_LOG_THRESHOLD_MS}"
  end

  loop do
    message = @protocol.read_message
    break if message.nil?
    next if message.equal?(Protocol::INVALID_MESSAGE)

    process_message(message)
  end
rescue StandardError => e
  warn "Server error: #{e.message}"
  raise
end