From 5f0c73c4e93131ec97265afb3c6fb3d223ac3d64 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Mon, 19 Jun 2023 16:34:22 -0700 Subject: [PATCH] feat: formatting (#9) --- lib/next_ls.ex | 48 +++++++++++++++++++++++++---- lib/next_ls/runtime.ex | 4 +-- test/next_ls_test.exs | 68 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 8 deletions(-) diff --git a/lib/next_ls.ex b/lib/next_ls.ex index b93a5c18..d0f70ebe 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -17,16 +17,23 @@ defmodule NextLS do TextDocumentDidSave } - alias GenLSP.Requests.{Initialize, Shutdown} + alias GenLSP.Requests.{ + Initialize, + Shutdown, + TextDocumentFormatting + } alias GenLSP.Structures.{ DidOpenTextDocumentParams, InitializeParams, InitializeResult, + Position, + Range, SaveOptions, ServerCapabilities, TextDocumentItem, - TextDocumentSyncOptions + TextDocumentSyncOptions, + TextEdit } alias NextLS.Runtime @@ -80,12 +87,35 @@ defmodule NextLS do open_close: true, save: %SaveOptions{include_text: true}, change: TextDocumentSyncKind.full() - } + }, + document_formatting_provider: true }, server_info: %{name: "NextLS"} }, assign(lsp, root_uri: root_uri)} end + def handle_request(%TextDocumentFormatting{params: %{text_document: %{uri: uri}}}, lsp) do + document = lsp.assigns.documents[uri] + + working_dir = URI.parse(lsp.assigns.root_uri).path + {opts, _} = Code.eval_file(".formatter.exs", working_dir) + new_document = Code.format_string!(Enum.join(document, "\n"), opts) |> IO.iodata_to_binary() + + {:reply, + [ + %TextEdit{ + new_text: new_document, + range: %Range{ + start: %Position{line: 0, character: 0}, + end: %Position{ + line: length(document), + character: document |> List.last() |> String.length() |> Kernel.-(1) |> max(0) + } + } + } + ], lsp} + end + def handle_request(%Shutdown{}, lsp) do {:reply, nil, assign(lsp, exit_code: 0)} end @@ -172,13 +202,21 @@ defmodule NextLS do {:noreply, lsp} end - def handle_notification(%TextDocumentDidChange{}, lsp) do + def handle_notification( + %TextDocumentDidChange{ + params: %{ + text_document: %{uri: uri}, + content_changes: [%{text: text}] + } + }, + lsp + ) do for task <- Task.Supervisor.children(lsp.assigns.task_supervisor), task != lsp.assigns.runtime_task.pid do Process.exit(task, :kill) end - {:noreply, lsp} + {:noreply, put_in(lsp.assigns.documents[uri], String.split(text, "\n"))} end def handle_notification( diff --git a/lib/next_ls/runtime.ex b/lib/next_ls/runtime.ex index 9d7a00ce..03612c66 100644 --- a/lib/next_ls/runtime.ex +++ b/lib/next_ls/runtime.ex @@ -33,7 +33,7 @@ defmodule NextLS.Runtime do end def compile(server) do - GenServer.call(server, :compile) + GenServer.call(server, :compile, :infinity) end @impl GenServer @@ -111,8 +111,6 @@ defmodule NextLS.Runtime do def handle_call(:compile, _, %{node: node} = state) do {_, errors} = :rpc.call(node, :_next_ls_private_compiler, :compile, []) - foo = "foo" - Registry.dispatch(state.extension_registry, :extension, fn entries -> for {pid, _} <- entries do send(pid, {:compiler, errors}) diff --git a/test/next_ls_test.exs b/test/next_ls_test.exs index 76b8cbf6..548bd149 100644 --- a/test/next_ls_test.exs +++ b/test/next_ls_test.exs @@ -181,4 +181,72 @@ defmodule NextLSTest do # } # ) end + + test "formats", %{client: client} do + assert :ok == + notify(client, %{ + method: "initialized", + jsonrpc: "2.0", + params: %{} + }) + + notify client, %{ + method: "textDocument/didOpen", + jsonrpc: "2.0", + params: %{ + textDocument: %{ + uri: "file://lib/foo/bar.ex", + languageId: "elixir", + version: 1, + text: """ + defmodule Foo.Bar do + def run() do + + + :ok + end + end + """ + } + } + } + + request client, %{ + method: "textDocument/formatting", + id: 2, + jsonrpc: "2.0", + params: %{ + textDocument: %{ + uri: "file://lib/foo/bar.ex" + }, + options: %{ + insertSpaces: true, + tabSize: 2 + } + } + } + + new_text = + """ + defmodule Foo.Bar do + def run() do + :ok + end + end + """ + |> String.trim() + + assert_result( + 2, + [ + %{ + "newText" => ^new_text, + "range" => %{ + "start" => %{"character" => 0, "line" => 0}, + "end" => %{"character" => 0, "line" => 8} + } + } + ] + ) + end end