Class: TRuby::LSPServer

Inherits:
Object
  • Object
show all
Defined in:
lib/t_ruby/lsp_server.rb

Overview

LSP (Language Server Protocol) Server for T-Ruby Provides IDE integration with autocomplete, diagnostics, and navigation

Defined Under Namespace

Modules: CompletionItemKind, DiagnosticSeverity, ErrorCodes, SemanticTokenModifiers, SemanticTokenTypes

Constant Summary collapse

VERSION =
"0.1.0"
SEMANTIC_TOKEN_TYPES =

Token type names for capability registration

%w[
  namespace type class enum interface struct typeParameter
  parameter variable property enumMember event function method
  macro keyword modifier comment string number regexp operator
].freeze
SEMANTIC_TOKEN_MODIFIERS =

Token modifier names

%w[
  declaration definition readonly static deprecated
  abstract async modification documentation defaultLibrary
].freeze
BUILT_IN_TYPES =

Built-in types for completion

%w[String Integer Boolean Array Hash Symbol void nil].freeze
TYPE_KEYWORDS =

Type keywords for completion

%w[type interface def end].freeze

Instance Method Summary collapse

Constructor Details

#initialize(input: $stdin, output: $stdout) ⇒ LSPServer



118
119
120
121
122
123
124
125
126
127
# File 'lib/t_ruby/lsp_server.rb', line 118

def initialize(input: $stdin, output: $stdout)
  @input = input
  @output = output
  @documents = {}
  @initialized = false
  @shutdown_requested = false
  @type_alias_registry = TypeAliasRegistry.new
  # Use Compiler for unified diagnostics (same as CLI)
  @compiler = Compiler.new
end

Instance Method Details

#handle_message(message) ⇒ Object

Handle an incoming message



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/t_ruby/lsp_server.rb', line 189

def handle_message(message)
  return error_response(nil, ErrorCodes::PARSE_ERROR, "Parse error") if message["error"]

  method = message["method"]
  params = message["params"] || {}
  id = message["id"]

  # Check if server is initialized for non-init methods
  if !@initialized && method != "initialize" && method != "exit"
    return error_response(id, ErrorCodes::SERVER_NOT_INITIALIZED, "Server not initialized")
  end

  result = dispatch_method(method, params, id)

  # For notifications (no id), don't send a response
  return nil if id.nil?

  if result.is_a?(Hash) && result[:error]
    error_response(id, result[:error][:code], result[:error][:message])
  else
    success_response(id, result)
  end
end

#read_messageObject

Read a single LSP message from input



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/t_ruby/lsp_server.rb', line 141

def read_message
  # Read headers
  headers = {}
  loop do
    line = @input.gets
    return nil if line.nil?

    line = line.strip
    break if line.empty?

    if line =~ /^([^:]+):\s*(.+)$/
      headers[Regexp.last_match(1)] = Regexp.last_match(2)
    end
  end

  content_length = headers["Content-Length"]&.to_i
  return nil unless content_length&.positive?

  # Read content
  content = @input.read(content_length)
  return nil if content.nil?

  JSON.parse(content)
rescue JSON::ParserError => e
  { "error" => "Parse error: #{e.message}" }
end

#runObject

Main run loop for the LSP server



130
131
132
133
134
135
136
137
138
# File 'lib/t_ruby/lsp_server.rb', line 130

def run
  loop do
    message = read_message
    break if message.nil?

    response = handle_message(message)
    send_response(response) if response
  end
end

#send_notification(method, params) ⇒ Object

Send a notification (no response expected)



179
180
181
182
183
184
185
186
# File 'lib/t_ruby/lsp_server.rb', line 179

def send_notification(method, params)
  notification = {
    "jsonrpc" => "2.0",
    "method" => method,
    "params" => params,
  }
  send_response(notification)
end

#send_response(response) ⇒ Object

Send a response message



169
170
171
172
173
174
175
176
# File 'lib/t_ruby/lsp_server.rb', line 169

def send_response(response)
  return if response.nil?

  content = JSON.generate(response)
  message = "Content-Length: #{content.bytesize}\r\n\r\n#{content}"
  @output.write(message)
  @output.flush
end