class YARP::LanguageServer
YARP
additionally ships with a language server conforming to the language server protocol. It can be invoked by running the yarp-lsp bin script (bin/yarp-lsp)
Constants
- GITHUB_TEMPLATE
Attributes
input[R]
output[R]
Public Class Methods
new( input: $stdin, output: $stdout )
click to toggle source
# File yarp/language_server.rb, line 31 def initialize( input: $stdin, output: $stdout ) @input = input.binmode @output = output.binmode end
Public Instance Methods
run()
click to toggle source
rubocop:disable Layout/LineLength
# File yarp/language_server.rb, line 40 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 in { method: "initialize", id: } store.clear write(id: id, result: { capabilities: capabilities }) in { method: "initialized" } # ignored in { method: "shutdown" } # tolerate missing ID to be a good citizen store.clear write(id: request[:id], result: {}) in { method: "exit"} return in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } } store[uri] = text in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } } store[uri] = text in { method: "textDocument/didClose", params: { textDocument: { uri: } } } store.delete(uri) in { method: "textDocument/diagnostic", id:, params: { textDocument: { uri: } } } contents = store[uri] write(id: id, result: contents ? diagnostics(contents) : nil) in { method: "textDocument/codeAction", id:, params: { textDocument: { uri: }, context: { diagnostics: }}} contents = store[uri] write(id: id, result: contents ? code_actions(contents, diagnostics) : nil) in { method: %r{\$/.+} } # ignored end end end
Private Instance Methods
capabilities()
click to toggle source
rubocop:enable Layout/LineLength
# File yarp/language_server.rb, line 84 def capabilities { codeActionProvider: { codeActionKinds: [ 'quickfix', ], }, diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false, }, textDocumentSync: { change: 1, openClose: true }, } end
code_actions(source, diagnostics)
click to toggle source
# File yarp/language_server.rb, line 102 def code_actions(source, diagnostics) diagnostics.map do |diagnostic| message = diagnostic[:message] issue_content = URI.encode_www_form_component(GITHUB_TEMPLATE % {error: message}) issue_link = "https://github.com/ruby/yarp/issues/new?&labels=Bug&body=#{issue_content}" { title: "Report incorrect error: `#{diagnostic[:message]}`", kind: "quickfix", diagnostics: [diagnostic], command: { title: "Report incorrect error", command: "vscode.open", arguments: [issue_link] } } end end
diagnostics(source)
click to toggle source
# File yarp/language_server.rb, line 121 def diagnostics(source) offsets = Hash.new do |hash, key| slice = source.byteslice(...key) lineno = slice.count("\n") char = slice.length newline = source.rindex("\n", [char - 1, 0].max) || -1 hash[key] = { line: lineno, character: char - newline - 1 } end parse_output = YARP.parse(source) { kind: "full", items: [ *parse_output.errors.map do |error| { range: { start: offsets[error.location.start_offset], end: offsets[error.location.end_offset], }, message: error.message, severity: 1, } end, *parse_output.warnings.map do |warning| { range: { start: offsets[warning.location.start_offset], end: offsets[warning.location.end_offset], }, message: warning.message, severity: 2, } end, ] } end
write(value)
click to toggle source
# File yarp/language_server.rb, line 160 def write(value) response = value.merge(jsonrpc: "2.0").to_json output.print("Content-Length: #{response.bytesize}\r\n\r\n#{response}") output.flush end