Class: SyntaxTree::LanguageServer

Inherits:
Object
  • Object
show all
Defined in:
lib/syntax_tree/language_server.rb,
lib/syntax_tree/language_server/inlay_hints.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



62
63
64
65
66
67
68
69
70
# File 'lib/syntax_tree/language_server.rb', line 62

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.



60
61
62
# File 'lib/syntax_tree/language_server.rb', line 60

def input
  @input
end

#outputObject (readonly)

Returns the value of attribute output.



60
61
62
# File 'lib/syntax_tree/language_server.rb', line 60

def output
  @output
end

Returns the value of attribute print_width.



60
61
62
# File 'lib/syntax_tree/language_server.rb', line 60

def print_width
  @print_width
end

Instance Method Details

#runObject

rubocop:disable Layout/LineLength



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/syntax_tree/language_server.rb', line 73

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
    else
      raise ArgumentError, "Unhandled: #{request}"
    end
  end
end