Class: SyntaxTree::LanguageServer

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

Overview

Syntax Tree additionally ships with a language server conforming to the language server protocol. It can be invoked through the CLI by running:

stree lsp

Defined Under Namespace

Modules: Request Classes: InlayHints

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input: $stdin, output: $stdout, print_width: DEFAULT_PRINT_WIDTH) ⇒ LanguageServer

Returns a new instance of LanguageServer.



217
218
219
220
221
222
223
224
225
# File 'lib/syntax_tree/language_server.rb', line 217

def initialize(
  input: $stdin,
  output: $stdout,
  print_width: DEFAULT_PRINT_WIDTH
)
  @input = input.binmode
  @output = output.binmode
  @print_width = print_width
end

Instance Attribute Details

#inputObject (readonly)

Returns the value of attribute input.



215
216
217
# File 'lib/syntax_tree/language_server.rb', line 215

def input
  @input
end

#outputObject (readonly)

Returns the value of attribute output.



215
216
217
# File 'lib/syntax_tree/language_server.rb', line 215

def output
  @output
end

Returns the value of attribute print_width.



215
216
217
# File 'lib/syntax_tree/language_server.rb', line 215

def print_width
  @print_width
end

Instance Method Details

#runObject

rubocop:disable Layout/LineLength



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/syntax_tree/language_server.rb', line 228

def run
  store =
    Hash.new do |hash, uri|
      filepath = CGI.unescape(URI.parse(uri).path)
      File.exist?(filepath) ? (hash[uri] = File.read(filepath)) : nil
    end

  while (headers = input.gets("\r\n\r\n"))
    source = input.read(headers[/Content-Length: (\d+)/i, 1].to_i)
    request = JSON.parse(source, symbolize_names: true)

    # stree-ignore
    case request
    when Request[method: "initialize", id: :any]
      store.clear
      write(id: request[:id], result: { capabilities: capabilities })
    when Request[method: "initialized"]
      # ignored
    when Request[method: "shutdown"] # tolerate missing ID to be a good citizen
      store.clear
      write(id: request[:id], result: {})
      return
    when Request[method: "textDocument/didChange", params: { textDocument: { uri: :any }, contentChanges: [{ text: :any }] }]
      store[request.dig(:params, :textDocument, :uri)] = request.dig(:params, :contentChanges, 0, :text)
    when Request[method: "textDocument/didOpen", params: { textDocument: { uri: :any, text: :any } }]
      store[request.dig(:params, :textDocument, :uri)] = request.dig(:params, :textDocument, :text)
    when Request[method: "textDocument/didClose", params: { textDocument: { uri: :any } }]
      store.delete(request.dig(:params, :textDocument, :uri))
    when Request[method: "textDocument/formatting", id: :any, params: { textDocument: { uri: :any } }]
      uri = request.dig(:params, :textDocument, :uri)
      contents = store[uri]
      write(id: request[:id], result: contents ? format(contents, uri.split(".").last) : nil)
    when Request[method: "textDocument/inlayHint", id: :any, params: { textDocument: { uri: :any } }]
      uri = request.dig(:params, :textDocument, :uri)
      contents = store[uri]
      write(id: request[:id], result: contents ? inlay_hints(contents) : nil)
    when Request[method: "syntaxTree/visualizing", id: :any, params: { textDocument: { uri: :any } }]
      uri = request.dig(:params, :textDocument, :uri)
      write(id: request[:id], result: PP.pp(SyntaxTree.parse(store[uri]), +""))
    when Request[method: %r{\$/.+}]
      # ignored
    when Request[method: "textDocument/documentColor", params: { textDocument: { uri: :any } }]
      # ignored
    else
      raise ArgumentError, "Unhandled: #{request}"
    end
  end
end