Class: MilkTea::LSP::Protocol

Inherits:
Object
  • Object
show all
Defined in:
lib/milk_tea/lsp/protocol.rb

Overview

Handles JSON-RPC 2.0 protocol encoding/decoding over stdin/stdout

Constant Summary collapse

INVALID_MESSAGE =
Object.new.freeze

Class Method Summary collapse

Class Method Details

.handle_response(message) ⇒ Object



30
31
32
33
34
35
36
37
38
# File 'lib/milk_tea/lsp/protocol.rb', line 30

def self.handle_response(message)
  id = message['id']
  callback = @outgoing_mutex.synchronize { outgoing_requests.delete(id) }
  return unless callback

  callback.call(message['result'], message['error'])
rescue StandardError => e
  warn "Protocol error handling response: #{e.message}"
end

.outgoing_requestsObject



15
16
17
# File 'lib/milk_tea/lsp/protocol.rb', line 15

def self.outgoing_requests
  @outgoing_requests ||= {}
end

.read_messageObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/milk_tea/lsp/protocol.rb', line 40

def self.read_message
  headers = {}
  loop do
    line = $stdin.gets
    return nil if line.nil?

    line = line.chomp
    break if line.empty?

    key, value = line.split(':', 2)
    headers[key&.strip] = value&.strip
  end

  content_length = headers['Content-Length']&.to_i
  return INVALID_MESSAGE if content_length.nil? || content_length <= 0

  content = $stdin.read(content_length)
  JSON.parse(content)
rescue StandardError => e
  warn "Protocol error reading message: #{e.message}"
  INVALID_MESSAGE
end

.send_request(method, params, &callback) ⇒ Object



19
20
21
22
23
24
25
26
27
28
# File 'lib/milk_tea/lsp/protocol.rb', line 19

def self.send_request(method, params, &callback)
  id = @outgoing_mutex.synchronize { i = @outgoing_id_counter; @outgoing_id_counter += 1; i }
  @outgoing_mutex.synchronize { outgoing_requests[id] = callback }
  write_message({ jsonrpc: '2.0', id: id, method: method, params: params })
  id
rescue StandardError => e
  @outgoing_mutex.synchronize { outgoing_requests.delete(id) }
  warn "Protocol error sending request #{method}: #{e.message}"
  nil
end

.write_error(id, code, message) ⇒ Object



92
93
94
95
96
97
98
99
100
101
# File 'lib/milk_tea/lsp/protocol.rb', line 92

def self.write_error(id, code, message)
  write_message({
    jsonrpc: '2.0',
    id: id,
    error: {
      code: code,
      message: message
    }
  })
end

.write_message(message) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/milk_tea/lsp/protocol.rb', line 63

def self.write_message(message)
  json = JSON.dump(message)
  length = json.bytesize
  header = "Content-Length: #{length}\r\n\r\n"
  @write_mutex.synchronize do
    $stdout.write(header)
    $stdout.write(json)
    $stdout.flush
  end
rescue StandardError => e
  warn "Protocol error writing message: #{e.message}"
end

.write_notification(method, params) ⇒ Object



76
77
78
79
80
81
82
# File 'lib/milk_tea/lsp/protocol.rb', line 76

def self.write_notification(method, params)
  write_message({
    jsonrpc: '2.0',
    method: method,
    params: params
  })
end

.write_response(id, result) ⇒ Object



84
85
86
87
88
89
90
# File 'lib/milk_tea/lsp/protocol.rb', line 84

def self.write_response(id, result)
  write_message({
    jsonrpc: '2.0',
    id: id,
    result: result
  })
end