Class: Rigor::LanguageServer::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/language_server/server.rb

Overview

LSP server lifecycle state machine + JSON-RPC method dispatcher.

Slice 1 (this commit) ships:

  • State machine: ‘:uninitialized` → `:initialized` → `:shutdown` → `:exited`.

  • Three lifecycle handlers: ‘initialize`, `shutdown`, `exit`.

  • #dispatch which routes (method, params) to the matching handler and returns the response payload (or ‘nil` for notifications). Out-of-state requests return the spec-defined `InvalidRequest` (-32002) / `MethodNotFound` (-32601) error shapes.

Slice 2 wraps this dispatcher in a stdio JSON-RPC reader / writer so the CLI subcommand can serve real LSP clients. Slice 3+ adds document sync; slice 4+ adds publishDiagnostics; slice 5-8 add the rest of the v1 capability surface.

Constant Summary collapse

ERROR_PARSE_ERROR =

JSON-RPC error codes per LSP spec § “Response Message”.

-32_700
ERROR_INVALID_REQUEST =
-32_600
ERROR_METHOD_NOT_FOUND =
-32_601
ERROR_INVALID_PARAMS =
-32_602
ERROR_INTERNAL_ERROR =
-32_603
# LSP-specific reserved codes.
ERROR_SERVER_NOT_INITIALIZED =

LSP-specific reserved codes.

-32_002
ERROR_INVALID_REQUEST_AFTER_SHUTDOWN =
-32_600
TEXT_DOCUMENT_SYNC_FULL =

‘TextDocumentSyncKind::Full = 1`. Slice 10 (deferred) promotes to `Incremental = 2`.

1
PRE_INITIALIZE_METHODS =

Methods callable BEFORE ‘initialize`. Per LSP spec § 3 only `initialize` and `exit` are allowed pre-initialization; every other request returns `ServerNotInitialized`. We also accept `shutdown` so a sequence like `initialize → shutdown → exit` (the conformance harness) round-trips even when the client skips real work.

%w[initialize shutdown exit].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(buffer_table: BufferTable.new, publisher: nil, hover_provider: nil, document_symbol_provider: nil, completion_provider: nil, signature_help_provider: nil, folding_range_provider: nil, selection_range_provider: nil, project_context: nil) ⇒ Server

Returns a new instance of Server.

Parameters:

  • completion_provider (Rigor::LanguageServer::CompletionProvider, nil) (defaults to: nil)

    resolves ‘textDocument/completion`. Nil → `MethodNotFound`.

  • signature_help_provider (Rigor::LanguageServer::SignatureHelpProvider, nil) (defaults to: nil)

    resolves ‘textDocument/signatureHelp`. Nil → `MethodNotFound`.

  • project_context (Rigor::LanguageServer::ProjectContext, nil) (defaults to: nil)

    the per-session cache of ‘Environment` + `Cache::Store` the providers read on every request. When present, `workspace/didChangeWatchedFiles` and `workspace/didChangeConfiguration` invalidate the cache; nil means “no project context”, which is the slice 1-6 behaviour (each request rebuilds env from scratch).



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rigor/language_server/server.rb', line 63

def initialize(buffer_table: BufferTable.new, publisher: nil, # rubocop:disable Metrics/ParameterLists
               hover_provider: nil, document_symbol_provider: nil,
               completion_provider: nil, signature_help_provider: nil,
               folding_range_provider: nil, selection_range_provider: nil,
               project_context: nil)
  @state = :uninitialized
  @exit_code = nil
  @buffer_table = buffer_table
  @publisher = publisher
  @hover_provider = hover_provider
  @document_symbol_provider = document_symbol_provider
  @completion_provider = completion_provider
  @signature_help_provider = signature_help_provider
  @folding_range_provider = folding_range_provider
  @selection_range_provider = selection_range_provider
  @project_context = project_context
end

Instance Attribute Details

#buffer_tableObject (readonly)

Returns the value of attribute buffer_table.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def buffer_table
  @buffer_table
end

#completion_providerObject (readonly)

Returns the value of attribute completion_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def completion_provider
  @completion_provider
end

#document_symbol_providerObject (readonly)

Returns the value of attribute document_symbol_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def document_symbol_provider
  @document_symbol_provider
end

#exit_codeObject (readonly)

Returns the value of attribute exit_code.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def exit_code
  @exit_code
end

#folding_range_providerObject (readonly)

Returns the value of attribute folding_range_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def folding_range_provider
  @folding_range_provider
end

#hover_providerObject (readonly)

Returns the value of attribute hover_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def hover_provider
  @hover_provider
end

#project_contextObject (readonly)

Returns the value of attribute project_context.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def project_context
  @project_context
end

#publisherObject (readonly)

Returns the value of attribute publisher.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def publisher
  @publisher
end

#selection_range_providerObject (readonly)

Returns the value of attribute selection_range_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def selection_range_provider
  @selection_range_provider
end

#signature_help_providerObject (readonly)

Returns the value of attribute signature_help_provider.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def signature_help_provider
  @signature_help_provider
end

#stateObject (readonly)

Returns the value of attribute state.



47
48
49
# File 'lib/rigor/language_server/server.rb', line 47

def state
  @state
end

Instance Method Details

#dispatch(method, params = nil) ⇒ Hash?

Routes one LSP method call.

Parameters:

  • method (String)

    the LSP method name (e.g. “initialize”).

  • params (Hash, nil) (defaults to: nil)

    the LSP ‘params` payload (Hash for request / notification methods; nil for the empty case).

Returns:

  • (Hash, nil)

    one of:

    • the response result Hash for request methods,

    • nil for notification methods,

    • { error: { code:, message: } } for state / shape errors.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rigor/language_server/server.rb', line 97

def dispatch(method, params = nil) # rubocop:disable Metrics/CyclomaticComplexity
  return state_violation_response(method) unless method_allowed_in_state?(method)

  case method
  when "initialize"             then handle_initialize(params)
  when "initialized"            then handle_initialized
  when "shutdown"               then handle_shutdown
  when "exit"                   then handle_exit
  when "textDocument/didOpen"   then handle_did_open(params)
  when "textDocument/didChange" then handle_did_change(params)
  when "textDocument/didClose"  then handle_did_close(params)
  when "textDocument/hover"               then handle_hover(params)
  when "textDocument/documentSymbol"      then handle_document_symbol(params)
  when "textDocument/completion"          then handle_completion(params)
  when "textDocument/signatureHelp"       then handle_signature_help(params)
  when "textDocument/foldingRange"        then handle_folding_range(params)
  when "textDocument/selectionRange"      then handle_selection_range(params)
  when "workspace/didChangeWatchedFiles"  then handle_did_change_watched_files(params)
  when "workspace/didChangeConfiguration" then handle_did_change_configuration(params)
  else
    method_not_found(method)
  end
end

#exited?Boolean

Returns true once the client has called ‘exit` and the server has set its terminal exit code. The CLI loop reads this between dispatches to know when to stop.

Returns:

  • (Boolean)

    true once the client has called ‘exit` and the server has set its terminal exit code. The CLI loop reads this between dispatches to know when to stop.



84
85
86
# File 'lib/rigor/language_server/server.rb', line 84

def exited?
  @state == :exited
end