diff --git a/lib/syntax_tree/formatter.rb b/lib/syntax_tree/formatter.rb index 56de6a4a..6efad8d8 100644 --- a/lib/syntax_tree/formatter.rb +++ b/lib/syntax_tree/formatter.rb @@ -68,7 +68,9 @@ def format(node, stackable: true) # going to just print out the node as it was seen in the source. doc = if leading.last&.ignore? - text(source[node.location.start_char...node.location.end_char]) + range = source[node.location.start_char...node.location.end_char] + separator = -> { breakable(indent: false, force: true) } + seplist(range.split(/\r?\n/, -1), separator) { |line| text(line) } else node.format(self) end diff --git a/lib/syntax_tree/language_server.rb b/lib/syntax_tree/language_server.rb index e42c77fd..2eb8228b 100644 --- a/lib/syntax_tree/language_server.rb +++ b/lib/syntax_tree/language_server.rb @@ -20,66 +20,51 @@ def initialize(input: $stdin, output: $stdout) @output = output.binmode end + # rubocop:disable Layout/LineLength def run store = Hash.new do |hash, uri| - hash[uri] = File.binread(CGI.unescape(URI.parse(uri).path)) + 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" + in { method: "initialized" } # ignored - in method: "shutdown" # tolerate missing ID to be a good citizen + in { method: "shutdown" } # tolerate missing ID to be a good citizen store.clear write(id: request[:id], result: {}) return - in { - method: "textDocument/didChange", - params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } - } + in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } } store[uri] = text - in { - method: "textDocument/didOpen", - params: { textDocument: { uri:, text: } } - } + in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } } store[uri] = text - in { - method: "textDocument/didClose", params: { textDocument: { uri: } } - } + in { method: "textDocument/didClose", params: { textDocument: { uri: } } } store.delete(uri) - in { - method: "textDocument/formatting", - id:, - params: { textDocument: { uri: } } - } - write(id: id, result: [format(store[uri])]) - in { - # official RPC in LSP spec 3.17 - method: "textDocument/inlayHint", - id:, - params: { textDocument: { uri: } } - } - write(id: id, result: inlay_hints(store[uri])) - in { - method: "syntaxTree/visualizing", - id:, - params: { textDocument: { uri: } } - } + in { method: "textDocument/formatting", id:, params: { textDocument: { uri: } } } + contents = store[uri] + write(id: id, result: contents ? [format(store[uri])] : nil) + in { method: "textDocument/inlayHint", id:, params: { textDocument: { uri: } } } + contents = store[uri] + write(id: id, result: contents ? inlay_hints(store[uri]) : nil) + in { method: "syntaxTree/visualizing", id:, params: { textDocument: { uri: } } } write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +"")) - in method: %r{\$/.+} + in { method: %r{\$/.+} } # ignored else raise ArgumentError, "Unhandled: #{request}" end end end + # rubocop:enable Layout/LineLength private diff --git a/test/language_server_test.rb b/test/language_server_test.rb index 0ea8b955..fc26054d 100644 --- a/test/language_server_test.rb +++ b/test/language_server_test.rb @@ -201,6 +201,23 @@ def test_clean_shutdown end end + def test_file_that_does_not_exist + messages = [ + Initialize.new(1), + TextDocumentFormatting.new(2, "file:///path/to/file.rb"), + Shutdown.new(3) + ] + + case run_server(messages) + in [ + { id: 1, result: { capabilities: Hash } }, + { id: 2, result: nil }, + { id: 3, result: {} } + ] + assert_equal(true, true) + end + end + private def write(content)