Class: Solargraph::LanguageServer::Host

Inherits:
Object
  • Object
show all
Includes:
UriHelpers
Defined in:
lib/solargraph/language_server/host.rb,
lib/solargraph/language_server/host/cataloger.rb,
lib/solargraph/language_server/host/diagnoser.rb

Overview

The language server protocol’s data provider. Hosts are responsible for querying the library and processing messages. They also provide thread safety for multi-threaded transports.

Defined Under Namespace

Classes: Cataloger, Diagnoser

Instance Method Summary collapse

Methods included from UriHelpers

file_to_uri, uri_to_file

Constructor Details

#initializeHost

Returns a new instance of Host.



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/solargraph/language_server/host.rb', line 16

def initialize
  @cancel_semaphore = Mutex.new
  @buffer_semaphore = Mutex.new
  @register_semaphore = Mutex.new
  @cancel = []
  @buffer = ''
  @stopped = false
  @next_request_id = 0
  @dynamic_capabilities = Set.new
  @registered_capabilities = Set.new
end

Instance Method Details

#allow_registration(method) ⇒ Object

Flag a method as available for dynamic registration.

Parameters:

  • method (String)

    The method name, e.g., ‘textDocument/completion’



283
284
285
286
287
# File 'lib/solargraph/language_server/host.rb', line 283

def allow_registration method
  @register_semaphore.synchronize do
    @dynamic_capabilities.add method
  end
end

#can_register?(method) ⇒ Boolean

Parameters:

  • method (String)

Returns:

  • (Boolean)


291
292
293
# File 'lib/solargraph/language_server/host.rb', line 291

def can_register? method
  @dynamic_capabilities.include?(method)
end

#cancel(id) ⇒ Object

Cancel the method with the specified ID.

Parameters:

  • id (Integer)


44
45
46
# File 'lib/solargraph/language_server/host.rb', line 44

def cancel id
  @cancel_semaphore.synchronize { @cancel.push id }
end

#cancel?(id) ⇒ Boolean

True if the host received a request to cancel the method with the specified ID.

Parameters:

  • id (Integer)

Returns:

  • (Boolean)


53
54
55
56
57
# File 'lib/solargraph/language_server/host.rb', line 53

def cancel? id
  result = false
  @cancel_semaphore.synchronize { result = @cancel.include? id }
  result
end

#catalogvoid

This method returns an undefined value.

Catalog the library.



464
465
466
# File 'lib/solargraph/language_server/host.rb', line 464

def catalog
  library.catalog
end

#change(params) ⇒ Object



157
158
159
160
161
162
# File 'lib/solargraph/language_server/host.rb', line 157

def change params
  updater = generate_updater(params)
  library.update updater
  cataloger.ping unless library.synchronized?
  diagnoser.schedule params['textDocument']['uri']
end

#changing?(file_uri) ⇒ Boolean

True if the specified file is in the process of changing.

Returns:

  • (Boolean)


306
307
308
# File 'lib/solargraph/language_server/host.rb', line 306

def changing? file_uri
  unsafe_changing?(file_uri)
end

#clear(id) ⇒ Object

Delete the specified ID from the list of cancelled IDs if it exists.

Parameters:

  • id (Integer)


62
63
64
# File 'lib/solargraph/language_server/host.rb', line 62

def clear id
  @cancel_semaphore.synchronize { @cancel.delete id }
end

#close(uri) ⇒ Object

Close the file specified by the URI.

Parameters:

  • uri (String)


134
135
136
137
# File 'lib/solargraph/language_server/host.rb', line 134

def close uri
  library.close uri_to_file(uri)
  diagnoser.schedule uri
end

#completions_at(filename, line, column) ⇒ Solargraph::ApiMap::Completion

Parameters:

  • filename (String)
  • line (Integer)
  • column (Integer)

Returns:

  • (Solargraph::ApiMap::Completion)


357
358
359
360
361
# File 'lib/solargraph/language_server/host.rb', line 357

def completions_at filename, line, column
  result = nil
  result = library.completions_at filename, line, column
  result
end

#configure(update) ⇒ Object

Update the configuration options with the provided hash.

Parameters:

  • update (Hash)


31
32
33
34
# File 'lib/solargraph/language_server/host.rb', line 31

def configure update
  return if update.nil?
  options.merge! update
end

#create(uri) ⇒ Object

Respond to a notification that a file was created in the workspace. The library will determine whether the file should be added to the workspace; see Solargraph::Library#create_from_disk.

Parameters:

  • uri (String)

    The file uri.



96
97
98
99
# File 'lib/solargraph/language_server/host.rb', line 96

def create uri
  filename = uri_to_file(uri)
  library.create_from_disk filename
end

#default_configurationHash{String => Object}

Returns:

  • (Hash{String => Object})


447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/solargraph/language_server/host.rb', line 447

def default_configuration
  {
    'completion' => true,
    'hover' => true,
    'symbols' => true,
    'definitions' => true,
    'rename' => true,
    'references' => true,
    'autoformat' => false,
    'diagnostics' => false,
    'formatting' => false
  }
end

#definitions_at(filename, line, column) ⇒ Array<Solargraph::Pin::Base>

Parameters:

  • filename (String)
  • line (Integer)
  • column (Integer)

Returns:



367
368
369
# File 'lib/solargraph/language_server/host.rb', line 367

def definitions_at filename, line, column
  library.definitions_at(filename, line, column)
end

#delete(uri) ⇒ Object

Delete the specified file from the library.

Parameters:

  • uri (String)

    The file uri.



104
105
106
107
108
109
110
111
# File 'lib/solargraph/language_server/host.rb', line 104

def delete uri
  filename = uri_to_file(uri)
  library.delete filename
  send_notification "textDocument/publishDiagnostics", {
    uri: uri,
    diagnostics: []
  }
end

#diagnose(uri) ⇒ Object

Parameters:

  • uri (String)


140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/solargraph/language_server/host.rb', line 140

def diagnose uri
  begin
    results = library.diagnose uri_to_file(uri)
    send_notification "textDocument/publishDiagnostics", {
      uri: uri,
      diagnostics: results
    }
  rescue DiagnosticsError => e
    STDERR.puts "Error in diagnostics: #{e.message}"
    options['diagnostics'] = false
    send_notification 'window/showMessage', {
      type: LanguageServer::MessageTypes::ERROR,
      message: "Error in diagnostics: #{e.message}"
    }
  end
end

#document(query) ⇒ String

Parameters:

  • query (String)

Returns:

  • (String)


402
403
404
# File 'lib/solargraph/language_server/host.rb', line 402

def document query
  library.document(query)
end

#document_symbols(uri) ⇒ Array<Solargraph::Pin::Base>

Parameters:

  • uri (String)

Returns:



408
409
410
# File 'lib/solargraph/language_server/host.rb', line 408

def document_symbols uri
  library.document_symbols(uri_to_file(uri))
end

#flushString

Clear the message buffer and return the most recent data.

Returns:

  • (String)

    The most recent data or an empty string.



176
177
178
179
180
181
182
183
# File 'lib/solargraph/language_server/host.rb', line 176

def flush
  tmp = nil
  @buffer_semaphore.synchronize do
    tmp = @buffer.clone
    @buffer.clear
  end
  tmp
end

#locate_pin(params) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/solargraph/language_server/host.rb', line 324

def locate_pin params
  pin = nil
  pin = nil
  unless params['data']['location'].nil?
    location = Location.new(
      params['data']['location']['filename'],
      Range.from_to(
        params['data']['location']['range']['start']['line'],
        params['data']['location']['range']['start']['character'],
        params['data']['location']['range']['end']['line'],
        params['data']['location']['range']['end']['character']
      )
    )
    pin = library.locate_pin(location)
  end
  # @todo Improve pin location
  if pin.nil? or pin.path != params['data']['path']
    pin = library.path_pins(params['data']['path']).first
  end
  pin
end

#open(uri, text, version) ⇒ Object

Open the specified file in the library.

Parameters:

  • uri (String)

    The file uri.

  • text (String)

    The contents of the file.

  • version (Integer)

    A version number.



118
119
120
121
# File 'lib/solargraph/language_server/host.rb', line 118

def open uri, text, version
  library.open uri_to_file(uri), text, version
  diagnoser.schedule uri
end

#open?(uri) ⇒ Boolean

True if the specified file is currently open in the library.

Parameters:

  • uri (String)

Returns:

  • (Boolean)


127
128
129
# File 'lib/solargraph/language_server/host.rb', line 127

def open? uri
  unsafe_open?(uri)
end

#optionsHash

Returns:

  • (Hash)


37
38
39
# File 'lib/solargraph/language_server/host.rb', line 37

def options
  @options ||= default_configuration
end

#pending_requestsArray<Integer>

Get a list of IDs for server requests that are waiting for responses from the client.

Returns:

  • (Array<Integer>)


442
443
444
# File 'lib/solargraph/language_server/host.rb', line 442

def pending_requests
  requests.keys
end

#prepare(directory) ⇒ Object

Prepare a library for the specified directory.

Parameters:

  • directory (String)


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/solargraph/language_server/host.rb', line 188

def prepare directory
  path = ''
  path = normalize_separators(directory) unless directory.nil?
  begin
    @library = Solargraph::Library.load(path)
  rescue WorkspaceTooLargeError => e
    send_notification 'window/showMessage', {
      'type' => Solargraph::LanguageServer::MessageTypes::WARNING,
      'message' => e.message
    }
    @library = Solargraph::Library.load
  end
  diagnoser.start
  cataloger.start
end

#query_symbols(query) ⇒ Array<Solargraph::Pin::Base>

Parameters:

  • query (String)

Returns:



390
391
392
# File 'lib/solargraph/language_server/host.rb', line 390

def query_symbols query
  library.query_symbols(query)
end

#queue(message) ⇒ Object

Queue a message to be sent to the client.

Parameters:

  • message (String)

    The message to send.



167
168
169
170
171
# File 'lib/solargraph/language_server/host.rb', line 167

def queue message
  @buffer_semaphore.synchronize do
    @buffer += message
  end
end

#read_text(uri) ⇒ String

Parameters:

  • uri (String)

Returns:

  • (String)


348
349
350
351
# File 'lib/solargraph/language_server/host.rb', line 348

def read_text uri
  filename = uri_to_file(uri)
  library.read_text(filename)
end

#references_from(filename, line, column, strip: true) ⇒ Array<Solargraph::Range>

Parameters:

  • filename (String)
  • line (Integer)
  • column (Integer)
  • strip (Boolean) (defaults to: true)

    Strip special characters from variable names

Returns:



384
385
386
# File 'lib/solargraph/language_server/host.rb', line 384

def references_from filename, line, column, strip: true
  result = library.references_from(filename, line, column, strip: strip)
end

#register_capabilities(methods) ⇒ Object

Register the methods as capabilities with the client. This method will avoid duplicating registrations and ignore methods that were not flagged for dynamic registration by the client.

Parameters:

  • methods (Array<String>)

    The methods to register



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/solargraph/language_server/host.rb', line 246

def register_capabilities methods
  @register_semaphore.synchronize do
    send_request 'client/registerCapability', {
      registrations: methods.select{|m| can_register?(m) and !registered?(m)}.map { |m|
        @registered_capabilities.add m
        {
          id: m,
          method: m,
          registerOptions: dynamic_capability_options[m]
        }
      }
    }
  end
end

#registered?(method) ⇒ Boolean

True if the specified method has been registered.

Parameters:

  • method (String)

    The method name, e.g., ‘textDocument/completion’

Returns:

  • (Boolean)


299
300
301
# File 'lib/solargraph/language_server/host.rb', line 299

def registered? method
  @registered_capabilities.include?(method)
end

#search(query) ⇒ Array<String>

Parameters:

  • query (String)

Returns:

  • (Array<String>)


396
397
398
# File 'lib/solargraph/language_server/host.rb', line 396

def search query
  library.search(query)
end

#send_notification(method, params) ⇒ Object

Send a notification to the client.

Parameters:

  • method (String)

    The message method

  • params (Hash)

    The method parameters



208
209
210
211
212
213
214
215
216
217
# File 'lib/solargraph/language_server/host.rb', line 208

def send_notification method, params
  response = {
    jsonrpc: "2.0",
    method: method,
    params: params
  }
  json = response.to_json
  envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
  queue envelope
end

#send_request(method, params) {|The| ... } ⇒ Object

Send a request to the client and execute the provided block to process the response. If an ID is not provided, the host will use an auto- incrementing integer.

Parameters:

  • method (String)

    The message method

  • params (Hash)

    The method parameters

  • id (String)

    An optional ID

Yield Parameters:

  • The (Hash)

    result sent by the client



227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/solargraph/language_server/host.rb', line 227

def send_request method, params, &block
  message = {
    jsonrpc: "2.0",
    method: method,
    params: params,
    id: @next_request_id
  }
  json = message.to_json
  requests[@next_request_id] = Request.new(@next_request_id, &block)
  envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
  queue envelope
  @next_request_id += 1
end

#show_message(text, type = LanguageServer::MessageTypes::INFO) ⇒ Object

Send a notification to the client.

Parameters:

  • text (String)
  • type (Integer) (defaults to: LanguageServer::MessageTypes::INFO)

    A MessageType constant



416
417
418
419
420
421
# File 'lib/solargraph/language_server/host.rb', line 416

def show_message text, type = LanguageServer::MessageTypes::INFO
  send_notification 'window/showMessage', {
    type: type,
    message: text
  }
end

#show_message_request(text, type, actions) {|The| ... } ⇒ Object

Send a notification with optional responses.

Parameters:

  • text (String)
  • type (Integer)

    A MessageType constant

  • actions (Array<String>)

    Response options for the client

  • &block

    The block that processes the response

Yield Parameters:

  • The (String)

    action received from the client



430
431
432
433
434
435
436
# File 'lib/solargraph/language_server/host.rb', line 430

def show_message_request text, type, actions, &block
  send_request 'window/showMessageRequest', {
    type: type,
    message: text,
    actions: actions
  }, &block
end

#signatures_at(filename, line, column) ⇒ Array<Solargraph::Pin::Base>

Parameters:

  • filename (String)
  • line (Integer)
  • column (Integer)

Returns:



375
376
377
# File 'lib/solargraph/language_server/host.rb', line 375

def signatures_at filename, line, column
  library.signatures_at(filename, line, column)
end

#start(request) ⇒ Solargraph::LanguageServer::Message::Base

Start processing a request from the client. After the message is processed, the transport is responsible for sending the response.

Parameters:

  • request (Hash)

    The contents of the message.

Returns:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/solargraph/language_server/host.rb', line 71

def start request
  if request['method']
    message = Message.select(request['method']).new(self, request)
    begin
      message.process
    rescue Exception => e
      STDERR.puts e.message
      STDERR.puts e.backtrace
      message.set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, "[#{e.class}] #{e.message}"
    end
    message
  elsif request['id']
    # @todo What if the id is invalid?
    requests[request['id']].process(request['result'])
    requests.delete request['id']
  else
    STDERR.puts "Invalid message received."
  end
end

#stopObject



314
315
316
317
318
# File 'lib/solargraph/language_server/host.rb', line 314

def stop
  @stopped = true
  cataloger.stop
  diagnoser.stop
end

#stopped?Boolean

Returns:

  • (Boolean)


320
321
322
# File 'lib/solargraph/language_server/host.rb', line 320

def stopped?
  @stopped
end

#synchronizing?Boolean

Returns:

  • (Boolean)


310
311
312
# File 'lib/solargraph/language_server/host.rb', line 310

def synchronizing?
  cataloger.synchronizing?
end

#unregister_capabilities(methods) ⇒ Object

Unregister the methods with the client. This method will avoid duplicating unregistrations and ignore methods that were not flagged for dynamic registration by the client.

Parameters:

  • methods (Array<String>)

    The methods to unregister



266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/solargraph/language_server/host.rb', line 266

def unregister_capabilities methods
  @register_semaphore.synchronize do
    send_request 'client/unregisterCapability', {
      unregisterations: methods.select{|m| registered?(m)}.map{ |m|
        @registered_capabilities.delete m
        {
          id: m,
          method: m
        }
      }
    }
  end
end