diff --git a/config/dev.exs b/config/dev.exs index 5bff00b5..4639d6c2 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1,5 +1,7 @@ import Config +config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:id] + config :logger, :default_handler, config: [ file: ~c".elixir-tools/next-ls.log", @@ -9,5 +11,3 @@ config :logger, :default_handler, max_no_files: 5, compress_on_rotate: true ] - -config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:id] diff --git a/config/prod.exs b/config/prod.exs index 5bff00b5..4639d6c2 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,5 +1,7 @@ import Config +config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:id] + config :logger, :default_handler, config: [ file: ~c".elixir-tools/next-ls.log", @@ -9,5 +11,3 @@ config :logger, :default_handler, max_no_files: 5, compress_on_rotate: true ] - -config :logger, :default_formatter, format: "\n$time $metadata[$level] $message\n", metadata: [:id] diff --git a/config/test.exs b/config/test.exs index 26b604c5..434b09bf 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,5 +1,5 @@ import Config -config :logger, :default_handler, config: [type: :standard_error] - config :gen_lsp, :exit_on_end, false + +config :logger, :default_handler, config: [type: :standard_error] diff --git a/lib/next_ls.ex b/lib/next_ls.ex index 89982c83..81a4dcd5 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -5,7 +5,11 @@ defmodule NextLS do import NextLS.DB.Query alias GenLSP.Enumerations.CodeActionKind + alias GenLSP.Enumerations.CompletionItemKind alias GenLSP.Enumerations.ErrorCodes + alias GenLSP.Enumerations.FileChangeType + alias GenLSP.Enumerations.MessageType + alias GenLSP.Enumerations.SymbolKind alias GenLSP.Enumerations.TextDocumentSyncKind alias GenLSP.ErrorResponse alias GenLSP.Notifications.Exit @@ -38,6 +42,7 @@ defmodule NextLS do alias GenLSP.Structures.InitializeParams alias GenLSP.Structures.InitializeResult alias GenLSP.Structures.Location + alias GenLSP.Structures.MessageActionItem alias GenLSP.Structures.Position alias GenLSP.Structures.Range alias GenLSP.Structures.SaveOptions @@ -50,11 +55,14 @@ defmodule NextLS do alias GenLSP.Structures.TextEdit alias GenLSP.Structures.WorkspaceEdit alias GenLSP.Structures.WorkspaceFoldersChangeEvent + alias NextLS.ASTHelpers.Variables + alias NextLS.Commands.Pipe alias NextLS.DB alias NextLS.Definition alias NextLS.DiagnosticCache alias NextLS.Progress alias NextLS.Runtime + alias NextLS.Runtime.BundledElixir require NextLS.Runtime @@ -134,9 +142,7 @@ defmodule NextLS do mix_home = if init_opts.experimental.completions.enable do - NextLS.Runtime.BundledElixir.mix_home(lsp.assigns.bundle_base) - else - nil + BundledElixir.mix_home(lsp.assigns.bundle_base) end {:reply, @@ -156,8 +162,6 @@ defmodule NextLS do trigger_characters: [".", "@", "&", "%", "^", ":", "!", "-", "~", "/", "{"], resolve_provider: true } - else - nil end, document_formatting_provider: true, execute_command_provider: %GenLSP.Structures.ExecuteCommandOptions{ @@ -224,10 +228,7 @@ defmodule NextLS do pid ) do nil -> - case NextLS.ASTHelpers.Variables.get_variable_definition( - URI.parse(uri).path, - {position.line + 1, position.character + 1} - ) do + case Variables.get_variable_definition(URI.parse(uri).path, {position.line + 1, position.character + 1}) do {_name, {startl..endl//_, startc..endc//_}} -> %Location{ uri: "file://#{URI.parse(uri).path}", @@ -283,8 +284,6 @@ defmodule NextLS do GenLSP.error(lsp, Exception.format(:error, e, __STACKTRACE__)) nil end - else - nil end {:reply, symbols, lsp} @@ -345,7 +344,7 @@ defmodule NextLS do :unknown -> file - |> NextLS.ASTHelpers.Variables.list_variable_references({line, col}) + |> Variables.list_variable_references({line, col}) |> Enum.map(fn {_name, {startl..endl//_, startc..endc//_}} -> [file, startl, endl, startc, endc] end) @@ -420,7 +419,8 @@ defmodule NextLS do "function" -> NextLS.Docs.function(doc, fn name, a, documentation, _other -> - to_string(name) == reference.identifier and documentation != :hidden and a >= reference.arity + to_string(name) == reference.identifier and documentation != :hidden and + a >= reference.arity end) _ -> @@ -487,10 +487,10 @@ defmodule NextLS do symbol <- symbols.(pid), score = fuzzy_match(symbol.name, query, case_sensitive?) do name = - if symbol.type not in ["defstruct", "attribute"] do - "#{symbol.type} #{symbol.name}" - else + if symbol.type in ["defstruct", "attribute"] do "#{symbol.name}" + else + "#{symbol.type} #{symbol.name}" end {%SymbolInformation{ @@ -521,68 +521,85 @@ defmodule NextLS do def handle_request(%TextDocumentFormatting{params: %{text_document: %{uri: uri}}}, lsp) do document = lsp.assigns.documents[uri] - [resp] = - if is_list(document) do - dispatch(lsp.assigns.registry, :runtimes, fn entries -> - for {runtime, %{uri: wuri}} <- entries, String.starts_with?(uri, wuri) do - with {:ok, {formatter, _}} <- - Runtime.call( - runtime, - {:_next_ls_private_formatter, :formatter_for_file, [URI.parse(uri).path]} - ), - {:ok, response} when is_binary(response) or is_list(response) <- - Runtime.call( - runtime, - {Kernel, :apply, [formatter, [Enum.join(document, "\n")]]} - ) do - {:reply, - [ - %TextEdit{ - new_text: IO.iodata_to_binary(response), - range: %Range{ - start: %Position{line: 0, character: 0}, - end: %Position{ - line: length(document), - character: document |> List.last() |> String.length() |> Kernel.-(1) |> max(0) - } - } + if is_list(document) do + dispatch_to_workspace(lsp.assigns.registry, uri, fn runtime, %{uri: wuri} -> + with {:ok, {formatter, _}} <- + Runtime.call( + runtime, + {:_next_ls_private_formatter, :formatter_for_file, [URI.parse(uri).path]} + ), + {:ok, response} when is_binary(response) or is_list(response) <- + Runtime.call( + runtime, + {Kernel, :apply, [formatter, [Enum.join(document, "\n")]]} + ) do + {:reply, + [ + %TextEdit{ + new_text: IO.iodata_to_binary(response), + range: %Range{ + start: %Position{line: 0, character: 0}, + end: %Position{ + line: length(document), + character: document |> List.last() |> String.length() |> Kernel.-(1) |> max(0) } - ], lsp} - else - {:error, :not_ready} -> - GenLSP.notify(lsp, %WindowShowMessage{ - params: %ShowMessageParams{ - type: GenLSP.Enumerations.MessageType.info(), - message: "The NextLS runtime is still initializing!" - } - }) + } + } + ], lsp} + else + {:error, :not_ready} -> + GenLSP.notify(lsp, %WindowShowMessage{ + params: %ShowMessageParams{ + type: MessageType.info(), + message: "The NextLS runtime is still initializing!" + } + }) - {:reply, nil, lsp} + {:reply, nil, lsp} - _ -> - NextLS.Logger.warning(lsp.assigns.logger, "Failed to format the file: #{uri}") + e -> + case e do + {:ok, {:badrpc, {:EXIT, {%{description: description, file: file}, _stacktrace}}}} -> + file = Path.relative_to(file, URI.parse(wuri).path) + + NextLS.Logger.show_message( + lsp.assigns.logger, + :error, + "Failed to format #{file}: #{description}" + ) - {:reply, nil, lsp} + NextLS.Logger.warning( + lsp.assigns.logger, + "Failed to format #{file}: #{description}" + ) + + _ -> + abs_file = URI.parse(uri).path + root_dir = URI.parse(wuri).path + file = Path.relative_to(abs_file, root_dir) + NextLS.Logger.show_message(lsp.assigns.logger, :error, "Failed to format #{file}") + NextLS.Logger.warning(lsp.assigns.logger, "Failed to format #{file}") end - end - end) - else - NextLS.Logger.warning( - lsp.assigns.logger, - "The file #{uri} was not found in the server's process state. Something must have gone wrong when opening, changing, or saving the file." - ) - [{:reply, nil, lsp}] - end + {:reply, nil, lsp} + end + end) + else + NextLS.Logger.warning( + lsp.assigns.logger, + "The file #{uri} was not found in the server's process state. Something must have gone wrong when opening, changing, or saving the file." + ) - resp + {:reply, nil, lsp} + end end def handle_request(%GenLSP.Requests.CompletionItemResolve{params: completion_item}, lsp) do completion_item = - with nil <- completion_item.data do - completion_item - else + case completion_item.data do + nil -> + completion_item + %{"uri" => uri, "data" => data} -> data = data |> Base.decode64!() |> :erlang.binary_to_term() @@ -593,7 +610,7 @@ defmodule NextLS do end result = - dispatch_to_workspace(lsp.assigns.registry, uri, fn runtime, _wuri -> + dispatch_to_workspace(lsp.assigns.registry, uri, fn runtime, _entry -> Runtime.call(runtime, {Code, :fetch_docs, [module]}) end) @@ -640,7 +657,7 @@ defmodule NextLS do end {root_path, entries} = - dispatch_to_workspace(lsp.assigns.registry, uri, fn runtime, wuri -> + dispatch_to_workspace(lsp.assigns.registry, uri, fn runtime, %{uri: wuri} -> {:ok, {_, _, _, macro_env}} = Runtime.expand(runtime, with_cursor, Path.basename(uri)) @@ -663,37 +680,37 @@ defmodule NextLS do {label, kind, docs} = case kind do :struct -> - {name, GenLSP.Enumerations.CompletionItemKind.struct(), ""} + {name, CompletionItemKind.struct(), ""} :function -> - {"#{name}/#{symbol.arity}", GenLSP.Enumerations.CompletionItemKind.function(), symbol[:docs] || ""} + {"#{name}/#{symbol.arity}", CompletionItemKind.function(), symbol[:docs] || ""} :module -> - {name, GenLSP.Enumerations.CompletionItemKind.module(), symbol[:docs] || ""} + {name, CompletionItemKind.module(), symbol[:docs] || ""} :variable -> - {to_string(name), GenLSP.Enumerations.CompletionItemKind.variable(), ""} + {to_string(name), CompletionItemKind.variable(), ""} :dir -> - {name, GenLSP.Enumerations.CompletionItemKind.folder(), ""} + {name, CompletionItemKind.folder(), ""} :file -> - {name, GenLSP.Enumerations.CompletionItemKind.file(), ""} + {name, CompletionItemKind.file(), ""} :reserved -> - {name, GenLSP.Enumerations.CompletionItemKind.keyword(), ""} + {name, CompletionItemKind.keyword(), ""} :keyword -> - {name, GenLSP.Enumerations.CompletionItemKind.field(), ""} + {name, CompletionItemKind.field(), ""} :attribute -> - {name, GenLSP.Enumerations.CompletionItemKind.property(), ""} + {name, CompletionItemKind.property(), ""} :sigil -> - {name, GenLSP.Enumerations.CompletionItemKind.function(), ""} + {name, CompletionItemKind.function(), ""} _ -> - {name, GenLSP.Enumerations.CompletionItemKind.text(), ""} + {name, CompletionItemKind.text(), ""} end completion_item = @@ -705,8 +722,6 @@ defmodule NextLS do data: if symbol[:data] do %{uri: uri, data: symbol[:data] |> :erlang.term_to_binary() |> Base.encode64()} - else - nil end } @@ -745,7 +760,7 @@ defmodule NextLS do position = arguments["position"] text = lsp.assigns.documents[uri] - NextLS.Commands.Pipe.from(%{ + Pipe.from(%{ uri: uri, text: text, position: position @@ -758,7 +773,7 @@ defmodule NextLS do position = arguments["position"] text = lsp.assigns.documents[uri] - NextLS.Commands.Pipe.to(%{ + Pipe.to(%{ uri: uri, text: text, position: position @@ -829,13 +844,17 @@ defmodule NextLS do @impl true def handle_notification(%Initialized{}, lsp) do NextLS.Logger.log(lsp.assigns.logger, "NextLS v#{version()} has initialized!") - NextLS.Logger.log(lsp.assigns.logger, "Log file located at #{Path.join(File.cwd!(), ".elixir-tools/next-ls.log")}") + + NextLS.Logger.log( + lsp.assigns.logger, + "Log file located at #{Path.join(File.cwd!(), ".elixir-tools/next-ls.log")}" + ) with opts when is_list(opts) <- lsp.assigns.auto_update do {:ok, _} = DynamicSupervisor.start_child( lsp.assigns.dynamic_supervisor, - {NextLS.Updater, Keyword.merge(opts, logger: lsp.assigns.logger)} + {NextLS.Updater, Keyword.put(opts, :logger, lsp.assigns.logger)} ) end @@ -880,7 +899,7 @@ defmodule NextLS do }) end - NextLS.Runtime.BundledElixir.install(lsp.assigns.bundle_base, lsp.assigns.logger) + BundledElixir.install(lsp.assigns.bundle_base, lsp.assigns.logger) NextLS.Logger.log(lsp.assigns.logger, "Booting runtimes...") parent = self() @@ -891,7 +910,7 @@ defmodule NextLS do lsp.assigns.init_opts.elixir_bin_path lsp.assigns.init_opts.experimental.completions.enable -> - NextLS.Runtime.BundledElixir.binpath(lsp.assigns.bundle_base) + BundledElixir.binpath(lsp.assigns.bundle_base) true -> "elixir" |> System.find_executable() |> Path.dirname() @@ -937,7 +956,10 @@ defmodule NextLS do send(parent, {:runtime_failed, name, status}) - NextLS.Logger.error(lsp.assigns.logger, "Runtime for folder #{name} failed to initialize") + NextLS.Logger.error( + lsp.assigns.logger, + "Runtime for folder #{name} failed to initialize" + ) end end, logger: lsp.assigns.logger @@ -948,35 +970,34 @@ defmodule NextLS do {:noreply, assign(lsp, elixir_bin_path: elixir_bin_path)} end - def handle_notification(%TextDocumentDidSave{}, %{assigns: %{ready: false}} = lsp) do - {:noreply, lsp} - end - # TODO: add some test cases for saving files in multiple workspaces def handle_notification( %TextDocumentDidSave{ params: %GenLSP.Structures.DidSaveTextDocumentParams{text: text, text_document: %{uri: uri}} }, - %{assigns: %{ready: true}} = lsp + lsp ) do - for task <- Task.Supervisor.children(lsp.assigns.task_supervisor) do - Process.exit(task, :kill) - end - refresh_refs = - dispatch(lsp.assigns.registry, :runtimes, fn entries -> - for {pid, %{name: name, uri: wuri}} <- entries, - String.starts_with?(uri, wuri), - into: %{} do - token = Progress.token() - Progress.start(lsp, token, "Compiling #{name}...") + if lsp.assigns.ready do + for task <- Task.Supervisor.children(lsp.assigns.task_supervisor) do + Process.exit(task, :kill) + end - ref = make_ref() - Runtime.compile(pid, caller_ref: ref) + # dispatching to all workspaces + dispatch(lsp.assigns.registry, :runtimes, fn entries -> + for {pid, %{name: name, uri: wuri}} <- entries, + String.starts_with?(uri, wuri), + into: %{} do + token = Progress.token() + Progress.start(lsp, token, "Compiling #{name}...") - {ref, {token, "Compiled #{name}!"}} - end - end) + ref = make_ref() + Runtime.compile(pid, caller_ref: ref) + + {ref, {token, "Compiled #{name}!"}} + end + end) + end lsp = lsp @@ -986,16 +1007,14 @@ defmodule NextLS do {:noreply, lsp} end - def handle_notification(%TextDocumentDidChange{}, %{assigns: %{ready: false}} = lsp) do - {:noreply, lsp} - end - def handle_notification( %TextDocumentDidChange{params: %{text_document: %{uri: uri}, content_changes: [%{text: text}]}}, lsp ) do - for task <- Task.Supervisor.children(lsp.assigns.task_supervisor) do - Process.exit(task, :kill) + if lsp.assigns.ready do + for task <- Task.Supervisor.children(lsp.assigns.task_supervisor) do + Process.exit(task, :kill) + end end lsp = put_in(lsp.assigns.documents[uri], String.split(text, "\n")) @@ -1059,7 +1078,10 @@ defmodule NextLS do send(parent, {:runtime_failed, name, status}) - NextLS.Logger.error(lsp.assigns.logger, "Runtime for folder #{name} failed to initialize") + NextLS.Logger.error( + lsp.assigns.logger, + "Runtime for folder #{name} failed to initialize" + ) end end, logger: lsp.assigns.logger @@ -1085,22 +1107,22 @@ defmodule NextLS do file = URI.parse(uri).path cond do - type == GenLSP.Enumerations.FileChangeType.created() -> - with {:ok, text} <- File.read(file) do - put_in(lsp.assigns.documents[uri], String.split(text, "\n")) - else + type == FileChangeType.created() -> + case File.read(file) do + {:ok, text} -> put_in(lsp.assigns.documents[uri], String.split(text, "\n")) _ -> lsp end - type == GenLSP.Enumerations.FileChangeType.changed() -> - with {:ok, text} <- File.read(file) do - put_in(lsp.assigns.documents[uri], String.split(text, "\n")) - else + type == FileChangeType.changed() -> + case File.read(file) do + {:ok, text} -> put_in(lsp.assigns.documents[uri], String.split(text, "\n")) _ -> lsp end - type == GenLSP.Enumerations.FileChangeType.deleted() -> - if not File.exists?(file) do + type == FileChangeType.deleted() -> + if File.exists?(file) do + lsp + else dispatch(lsp.assigns.registry, :databases, fn entries -> for {pid, _} <- entries do NextLS.DB.query( @@ -1123,9 +1145,7 @@ defmodule NextLS do end end) - update_in(lsp.assigns.documents, &Map.drop(&1, [uri])) - else - lsp + update_in(lsp.assigns.documents, &Map.delete(&1, uri)) end end end @@ -1237,11 +1257,11 @@ defmodule NextLS do %GenLSP.Requests.WindowShowMessageRequest{ id: System.unique_integer([:positive]), params: %GenLSP.Structures.ShowMessageRequestParams{ - type: GenLSP.Enumerations.MessageType.error(), + type: MessageType.error(), message: "The NextLS runtime failed with errors on dependencies. Would you like to re-fetch them?", actions: [ - %GenLSP.Structures.MessageActionItem{title: "yes"}, - %GenLSP.Structures.MessageActionItem{title: "no"} + %MessageActionItem{title: "yes"}, + %MessageActionItem{title: "no"} ] } }, @@ -1249,7 +1269,7 @@ defmodule NextLS do ) case resp do - %GenLSP.Structures.MessageActionItem{title: "yes"} -> + %MessageActionItem{title: "yes"} -> NextLS.Logger.info( lsp.assigns.logger, "Running `mix deps.get` in directory #{init_arg[:runtime][:working_dir]}" @@ -1289,7 +1309,10 @@ defmodule NextLS do end else unless lsp.assigns.client_capabilities.window.show_message do - NextLS.Logger.info(lsp.assigns.logger, "Client does not support window/showMessageRequest") + NextLS.Logger.info( + lsp.assigns.logger, + "Client does not support window/showMessageRequest" + ) end end @@ -1320,12 +1343,11 @@ defmodule NextLS do end end - defp elixir_kind_to_lsp_kind("defmodule"), do: GenLSP.Enumerations.SymbolKind.module() - defp elixir_kind_to_lsp_kind("defstruct"), do: GenLSP.Enumerations.SymbolKind.struct() - defp elixir_kind_to_lsp_kind("attribute"), do: GenLSP.Enumerations.SymbolKind.property() + defp elixir_kind_to_lsp_kind("defmodule"), do: SymbolKind.module() + defp elixir_kind_to_lsp_kind("defstruct"), do: SymbolKind.struct() + defp elixir_kind_to_lsp_kind("attribute"), do: SymbolKind.property() - defp elixir_kind_to_lsp_kind(kind) when kind in ["def", "defp", "defmacro", "defmacrop"], - do: GenLSP.Enumerations.SymbolKind.function() + defp elixir_kind_to_lsp_kind(kind) when kind in ["def", "defp", "defmacro", "defmacrop"], do: SymbolKind.function() # NOTE: this is only possible because the registry is not partitioned # if it is partitioned, then the callback is called multiple times @@ -1354,8 +1376,8 @@ defmodule NextLS do Registry.dispatch(registry, :runtimes, fn entries -> [result] = - for {runtime, %{uri: wuri}} <- entries, String.starts_with?(uri, wuri) do - callback.(runtime, wuri) + for {runtime, %{uri: wuri} = entry} <- entries, String.starts_with?(uri, wuri) do + callback.(runtime, entry) end send(me, {ref, result}) @@ -1539,11 +1561,14 @@ defmodule NextLS do @moduledoc false import Schematic + alias NextLS.InitOpts.Experimental + alias NextLS.InitOpts.Extensions + defstruct mix_target: "host", mix_env: "dev", elixir_bin_path: nil, - experimental: %NextLS.InitOpts.Experimental{}, - extensions: %NextLS.InitOpts.Extensions{} + experimental: %Experimental{}, + extensions: %Extensions{} def validate(opts) do schematic = @@ -1554,14 +1579,14 @@ defmodule NextLS do optional(:mix_env) => str(), optional(:elixir_bin_path) => str(), optional(:experimental) => - schema(NextLS.InitOpts.Experimental, %{ + schema(Experimental, %{ optional(:completions) => map(%{ {"enable", :enable} => bool() }) }), optional(:extensions) => - schema(NextLS.InitOpts.Extensions, %{ + schema(Extensions, %{ optional(:credo) => schema(NextLS.InitOpts.Extensions.Credo, %{ optional(:enable) => bool(), diff --git a/lib/next_ls/autocomplete.ex b/lib/next_ls/autocomplete.ex index 5e1048ac..b119bdd2 100644 --- a/lib/next_ls/autocomplete.ex +++ b/lib/next_ls/autocomplete.ex @@ -659,12 +659,10 @@ defmodule NextLS.Autocomplete do end defp get_module_funs(mod, runtime) do - cond do - not ensure_loaded?(mod, runtime) -> - [] - - true -> - exports(mod, runtime) + if ensure_loaded?(mod, runtime) do + exports(mod, runtime) + else + [] end end diff --git a/lib/next_ls/db.ex b/lib/next_ls/db.ex index 1af61022..ef4a0be1 100644 --- a/lib/next_ls/db.ex +++ b/lib/next_ls/db.ex @@ -4,6 +4,7 @@ defmodule NextLS.DB do import __MODULE__.Query + alias NextLS.DB.Activity alias OpenTelemetry.Tracer require OpenTelemetry.Tracer @@ -56,7 +57,7 @@ defmodule NextLS.DB do try do Tracer.with_span :"db.query receive", %{attributes: %{query: query}} do {:message_queue_len, count} = Process.info(self(), :message_queue_len) - NextLS.DB.Activity.update(s.activity, count) + Activity.update(s.activity, count) opts = if Keyword.keyword?(args_or_opts), do: args_or_opts, else: [args: args_or_opts] query = @@ -84,7 +85,7 @@ defmodule NextLS.DB do def handle_cast({:insert_symbol, symbol}, %{conn: conn} = s) do {:message_queue_len, count} = Process.info(self(), :message_queue_len) - NextLS.DB.Activity.update(s.activity, count) + Activity.update(s.activity, count) %{ module: mod, @@ -173,7 +174,7 @@ defmodule NextLS.DB do def handle_cast({:insert_reference, reference}, %{conn: conn} = s) do {:message_queue_len, count} = Process.info(self(), :message_queue_len) - NextLS.DB.Activity.update(s.activity, count) + Activity.update(s.activity, count) %{ meta: meta, @@ -209,7 +210,7 @@ defmodule NextLS.DB do def handle_cast({:clean_references, filename}, %{conn: conn} = s) do {:message_queue_len, count} = Process.info(self(), :message_queue_len) - NextLS.DB.Activity.update(s.activity, count) + Activity.update(s.activity, count) __query__( {conn, s.logger}, @@ -246,12 +247,10 @@ defmodule NextLS.DB do end defp cast(arg) do - cond do - is_atom(arg) and String.starts_with?(to_string(arg), "Elixir.") -> - Macro.to_string(arg) - - true -> - arg + if is_atom(arg) and String.starts_with?(to_string(arg), "Elixir.") do + Macro.to_string(arg) + else + arg end end end diff --git a/lib/next_ls/db/activity.ex b/lib/next_ls/db/activity.ex index 26d34099..e60fd032 100644 --- a/lib/next_ls/db/activity.ex +++ b/lib/next_ls/db/activity.ex @@ -10,7 +10,7 @@ defmodule NextLS.DB.Activity do end def start_link(args) do - :gen_statem.start_link({:local, Keyword.get(args, :name)}, __MODULE__, Keyword.drop(args, [:name]), []) + :gen_statem.start_link({:local, Keyword.get(args, :name)}, __MODULE__, Keyword.delete(args, :name), []) end def update(statem, count), do: :gen_statem.cast(statem, count) diff --git a/lib/next_ls/definition.ex b/lib/next_ls/definition.ex index 03503114..13cb595d 100644 --- a/lib/next_ls/definition.ex +++ b/lib/next_ls/definition.ex @@ -68,8 +68,6 @@ defmodule NextLS.Definition do if args do DB.query(db, query, args) - else - nil end end end diff --git a/lib/next_ls/document_symbol.ex b/lib/next_ls/document_symbol.ex index 40e0f097..2523d763 100644 --- a/lib/next_ls/document_symbol.ex +++ b/lib/next_ls/document_symbol.ex @@ -1,6 +1,7 @@ defmodule NextLS.DocumentSymbol do @moduledoc false + alias GenLSP.Enumerations.SymbolKind alias GenLSP.Structures.DocumentSymbol alias GenLSP.Structures.Position alias GenLSP.Structures.Range @@ -53,7 +54,7 @@ defmodule NextLS.DocumentSymbol do %DocumentSymbol{ name: name, - kind: GenLSP.Enumerations.SymbolKind.module(), + kind: SymbolKind.module(), children: List.flatten(for(child <- children, sym = walker(child, name), sym != nil, do: sym)), range: %Range{ start: %Position{line: meta[:line] - 1, character: meta[:column] - 1}, @@ -71,7 +72,7 @@ defmodule NextLS.DocumentSymbol do %DocumentSymbol{ name: name, - kind: GenLSP.Enumerations.SymbolKind.class(), + kind: SymbolKind.class(), children: List.flatten(for(child <- children, sym = walker(child, mod), sym != nil, do: sym)), range: %Range{ start: %Position{line: meta[:line] - 1, character: meta[:column] - 1}, @@ -107,7 +108,7 @@ defmodule NextLS.DocumentSymbol do %DocumentSymbol{ name: name, children: [], - kind: GenLSP.Enumerations.SymbolKind.field(), + kind: SymbolKind.field(), range: %Range{ start: %Position{ line: start_line, @@ -172,7 +173,7 @@ defmodule NextLS.DocumentSymbol do %DocumentSymbol{ name: String.replace("#{type} #{Macro.to_string(unliteral(name))}", "\n", ""), children: [], - kind: GenLSP.Enumerations.SymbolKind.constructor(), + kind: SymbolKind.constructor(), range: %Range{ start: %Position{ line: meta[:line] - 1, @@ -226,9 +227,9 @@ defmodule NextLS.DocumentSymbol do end) end - defp elixir_kind_to_lsp_kind(:defstruct), do: GenLSP.Enumerations.SymbolKind.struct() - defp elixir_kind_to_lsp_kind(:@), do: GenLSP.Enumerations.SymbolKind.property() + defp elixir_kind_to_lsp_kind(:defstruct), do: SymbolKind.struct() + defp elixir_kind_to_lsp_kind(:@), do: SymbolKind.property() defp elixir_kind_to_lsp_kind(kind) when kind in [:def, :defp, :defmacro, :defmacrop, :test, :describe], - do: GenLSP.Enumerations.SymbolKind.function() + do: SymbolKind.function() end diff --git a/lib/next_ls/extensions/credo_extension.ex b/lib/next_ls/extensions/credo_extension.ex index b3200af6..e139768b 100644 --- a/lib/next_ls/extensions/credo_extension.ex +++ b/lib/next_ls/extensions/credo_extension.ex @@ -61,7 +61,9 @@ defmodule NextLS.CredoExtension do {state, refs} -> # determine the existence of Credo and memoize the result state = - if not Map.has_key?(state.runtimes, runtime) do + if Map.has_key?(state.runtimes, runtime) do + state + else case Runtime.call(runtime, {Code, :ensure_loaded?, [Credo]}) do {:ok, true} -> :next_ls @@ -77,8 +79,6 @@ defmodule NextLS.CredoExtension do _ -> state end - else - state end # if runtime has Credo diff --git a/lib/next_ls/extensions/elixir_extension.ex b/lib/next_ls/extensions/elixir_extension.ex index 0d161e3e..902f6386 100644 --- a/lib/next_ls/extensions/elixir_extension.ex +++ b/lib/next_ls/extensions/elixir_extension.ex @@ -2,6 +2,8 @@ defmodule NextLS.ElixirExtension do @moduledoc false use GenServer + alias GenLSP.Enumerations.DiagnosticSeverity + alias GenLSP.Structures.Position alias NextLS.DiagnosticCache def start_link(args) do @@ -46,18 +48,18 @@ defmodule NextLS.ElixirExtension do {:noreply, state} end - defp severity(:error), do: GenLSP.Enumerations.DiagnosticSeverity.error() - defp severity(:warning), do: GenLSP.Enumerations.DiagnosticSeverity.warning() - defp severity(:info), do: GenLSP.Enumerations.DiagnosticSeverity.information() - defp severity(:hint), do: GenLSP.Enumerations.DiagnosticSeverity.hint() + defp severity(:error), do: DiagnosticSeverity.error() + defp severity(:warning), do: DiagnosticSeverity.warning() + defp severity(:info), do: DiagnosticSeverity.information() + defp severity(:hint), do: DiagnosticSeverity.hint() defp range({start_line, start_col, end_line, end_col}, _) do %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: clamp(start_line - 1), character: start_col - 1 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: clamp(end_line - 1), character: end_col - 1 } @@ -66,11 +68,11 @@ defmodule NextLS.ElixirExtension do defp range({startl, startc}, {endl, endc}) do %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: clamp(startl - 1), character: startc - 1 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: clamp(endl - 1), character: endc - 1 } @@ -79,11 +81,11 @@ defmodule NextLS.ElixirExtension do defp range({line, col}, nil) do %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: clamp(line - 1), character: col - 1 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: clamp(line - 1), character: 999 } @@ -92,11 +94,11 @@ defmodule NextLS.ElixirExtension do defp range(line, _) do %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: clamp(line - 1), character: 0 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: clamp(line - 1), character: 999 } diff --git a/lib/next_ls/extensions/elixir_extension/code_action/require.ex b/lib/next_ls/extensions/elixir_extension/code_action/require.ex index 1f5ddcdf..1c7a9daf 100644 --- a/lib/next_ls/extensions/elixir_extension/code_action/require.ex +++ b/lib/next_ls/extensions/elixir_extension/code_action/require.ex @@ -16,10 +16,11 @@ defmodule NextLS.ElixirExtension.CodeAction.Require do with {:ok, require_module} <- get_edit(diagnostic.message), {:ok, ast} <- parse_ast(text), - {:ok, defm} <- ASTHelpers.get_surrounding_module(ast, range.start), - indentation <- get_indent(text, defm), - nearest <- find_nearest_node_for_require(defm), - range <- get_edit_range(nearest) do + {:ok, defm} <- ASTHelpers.get_surrounding_module(ast, range.start) do + indentation = get_indent(text, defm) + nearest = find_nearest_node_for_require(defm) + range = get_edit_range(nearest) + [ %CodeAction{ title: "Add missing require for #{require_module}", diff --git a/lib/next_ls/helpers/ast_helpers.ex b/lib/next_ls/helpers/ast_helpers.ex index 7b5b9981..7843755b 100644 --- a/lib/next_ls/helpers/ast_helpers.ex +++ b/lib/next_ls/helpers/ast_helpers.ex @@ -173,9 +173,8 @@ defmodule NextLS.ASTHelpers do node_range = Sourceror.Range.get_range(node) is_inside = - with nil <- node_range do - false - else + case node_range do + nil -> false _ -> sourceror_inside?(node_range, position) end diff --git a/lib/next_ls/helpers/ast_helpers/variables.ex b/lib/next_ls/helpers/ast_helpers/variables.ex index 6fff47cd..7757d652 100644 --- a/lib/next_ls/helpers/ast_helpers/variables.ex +++ b/lib/next_ls/helpers/ast_helpers/variables.ex @@ -21,7 +21,7 @@ defmodule NextLS.ASTHelpers.Variables do ) Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} -> - if position_in_range?(position, ref_range), do: {name, range}, else: nil + if position_in_range?(position, ref_range), do: {name, range} end) _error -> @@ -58,7 +58,7 @@ defmodule NextLS.ASTHelpers.Variables do symbol = Enum.find_value(vars, fn %{name: name, sym_range: range, ref_range: ref_range} -> - if position_in_range?(position, ref_range), do: {name, range}, else: nil + if position_in_range?(position, ref_range), do: {name, range} end) position = diff --git a/lib/next_ls/helpers/edit_helpers.ex b/lib/next_ls/helpers/edit_helpers.ex index ae729dee..b4cbd115 100644 --- a/lib/next_ls/helpers/edit_helpers.ex +++ b/lib/next_ls/helpers/edit_helpers.ex @@ -12,19 +12,19 @@ defmodule NextLS.EditHelpers do def add_indent_to_edit(text, indent) do [first | rest] = String.split(text, "\n") - if rest != [] do + if rest == [] do + first + else indented = Enum.map_join(rest, "\n", fn line -> - if line not in @blank_lines do - indent <> line - else + if line in @blank_lines do line + else + indent <> line end end) first <> "\n" <> indented - else - first end end diff --git a/lib/next_ls/progress.ex b/lib/next_ls/progress.ex index 45709f6b..8df4a5ad 100644 --- a/lib/next_ls/progress.ex +++ b/lib/next_ls/progress.ex @@ -1,6 +1,9 @@ defmodule NextLS.Progress do @moduledoc false + alias GenLSP.Notifications.DollarProgress + alias GenLSP.Structures.ProgressParams + def start(lsp, token, msg) do Task.start(fn -> if lsp.assigns.client_capabilities.window.work_done_progress do @@ -12,8 +15,8 @@ defmodule NextLS.Progress do }) end - GenLSP.notify(lsp, %GenLSP.Notifications.DollarProgress{ - params: %GenLSP.Structures.ProgressParams{ + GenLSP.notify(lsp, %DollarProgress{ + params: %ProgressParams{ token: token, value: %GenLSP.Structures.WorkDoneProgressBegin{kind: "begin", title: msg} } @@ -22,8 +25,8 @@ defmodule NextLS.Progress do end def stop(lsp, token, msg \\ nil) do - GenLSP.notify(lsp, %GenLSP.Notifications.DollarProgress{ - params: %GenLSP.Structures.ProgressParams{ + GenLSP.notify(lsp, %DollarProgress{ + params: %ProgressParams{ token: token, value: %GenLSP.Structures.WorkDoneProgressEnd{ kind: "end", diff --git a/lib/next_ls/runtime.ex b/lib/next_ls/runtime.ex index 749122d6..10366a69 100644 --- a/lib/next_ls/runtime.ex +++ b/lib/next_ls/runtime.ex @@ -135,149 +135,152 @@ defmodule NextLS.Runtime do path_minus_bindir2 = path_minus_bindir |> String.split(":") |> List.delete(bindir) |> Enum.join(":") new_path = elixir_bin_path <> ":" <> path_minus_bindir2 - with dir when is_list(dir) <- :code.priv_dir(:next_ls) do - exe = - dir - |> Path.join("cmd") - |> Path.absname() - - env = - [ - {~c"LSP", ~c"nextls"}, - {~c"NEXTLS_PARENT_PID", parent}, - {~c"MIX_ENV", ~c"#{mix_env}"}, - {~c"MIX_TARGET", ~c"#{mix_target}"}, - {~c"MIX_BUILD_ROOT", ~c".elixir-tools/_build"}, - {~c"ROOTDIR", false}, - {~c"BINDIR", false}, - {~c"RELEASE_ROOT", false}, - {~c"RELEASE_SYS_CONFIG", false}, - {~c"PATH", String.to_charlist(new_path)} - ] ++ - if mix_home do - [{~c"MIX_HOME", ~c"#{mix_home}"}] - else - [] - end - - args = - [elixir_exe] ++ - if @env == :test do - ["--erl", "-kernel prevent_overlapping_partitions false"] - else - [] - end ++ - [ - "--no-halt", - "--sname", - sname, - "--cookie", - Node.get_cookie(), - "-S", - "mix", - "loadpaths", - "--no-compile" - ] - - NextLS.Logger.info(logger, """ - Booting runtime for #{name}. - - - elixir: #{elixir_exe} - - zombie wrapper script: #{exe} - - working_dir: #{working_dir} - - command: #{Enum.join(args, " ")} - - Environment: - - #{Enum.map_join(env, "\n", fn {k, v} -> "#{k}=#{v}" end)} - """) - - port = - Port.open( - {:spawn_executable, exe}, + case :code.priv_dir(:next_ls) do + dir when is_list(dir) -> + exe = + dir + |> Path.join("cmd") + |> Path.absname() + + env = [ - :use_stdio, - :stderr_to_stdout, - :binary, - :stream, - cd: working_dir, - env: env, - args: args - ] - ) - - Port.monitor(port) - - me = self() - - Task.Supervisor.async_nolink(task_supervisor, fn -> - ref = Process.monitor(me) - - receive do - {:DOWN, ^ref, :process, ^me, reason} -> - case reason do - :shutdown -> - NextLS.Logger.info(logger, "The runtime for #{name} has successfully shut down.") - - reason -> - NextLS.Logger.error(logger, "The runtime for #{name} has crashed with reason: #{inspect(reason)}") + {~c"LSP", ~c"nextls"}, + {~c"NEXTLS_PARENT_PID", parent}, + {~c"MIX_ENV", ~c"#{mix_env}"}, + {~c"MIX_TARGET", ~c"#{mix_target}"}, + {~c"MIX_BUILD_ROOT", ~c".elixir-tools/_build"}, + {~c"ROOTDIR", false}, + {~c"BINDIR", false}, + {~c"RELEASE_ROOT", false}, + {~c"RELEASE_SYS_CONFIG", false}, + {~c"PATH", String.to_charlist(new_path)} + ] ++ + if mix_home do + [{~c"MIX_HOME", ~c"#{mix_home}"}] + else + [] end - end - end) - - Task.start_link(fn -> - with {:ok, host} = :inet.gethostname(), - node = :"#{sname}@#{host}", - true <- connect(node, port, 120) do - NextLS.Logger.info(logger, "Connected to node #{node}") - - result = - :next_ls - |> :code.priv_dir() - |> Path.join("monkey/_next_ls_private_compiler.ex") - |> then(fn path -> - if await_config_table(node, 5) do - :rpc.call(node, Code, :compile_file, [path]) - else - {:badrpc, "internal ets table not found"} - end - end) - |> then(fn - {:badrpc, error} -> - NextLS.Logger.error(logger, "Bad RPC call to node #{node}: #{inspect(error)}") - send(me, {:cancel, error}) - :error - - _ -> - :ok - end) - if result == :ok do - {:ok, _} = :rpc.call(node, :_next_ls_private_compiler, :start, []) + args = + [elixir_exe] ++ + if @env == :test do + ["--erl", "-kernel prevent_overlapping_partitions false"] + else + [] + end ++ + [ + "--no-halt", + "--sname", + sname, + "--cookie", + Node.get_cookie(), + "-S", + "mix", + "loadpaths", + "--no-compile" + ] + + NextLS.Logger.info(logger, """ + Booting runtime for #{name}. + + - elixir: #{elixir_exe} + - zombie wrapper script: #{exe} + - working_dir: #{working_dir} + - command: #{Enum.join(args, " ")} + + Environment: + + #{Enum.map_join(env, "\n", fn {k, v} -> "#{k}=#{v}" end)} + """) + + port = + Port.open( + {:spawn_executable, exe}, + [ + :use_stdio, + :stderr_to_stdout, + :binary, + :stream, + cd: working_dir, + env: env, + args: args + ] + ) + + Port.monitor(port) + + me = self() + + Task.Supervisor.async_nolink(task_supervisor, fn -> + ref = Process.monitor(me) + + receive do + {:DOWN, ^ref, :process, ^me, reason} -> + case reason do + :shutdown -> + NextLS.Logger.info(logger, "The runtime for #{name} has successfully shut down.") + + reason -> + NextLS.Logger.error(logger, "The runtime for #{name} has crashed with reason: #{inspect(reason)}") + end + end + end) + + Task.start_link(fn -> + {:ok, host} = :inet.gethostname() + node = :"#{sname}@#{host}" + + case connect(node, port, 120) do + true -> + NextLS.Logger.info(logger, "Connected to node #{node}") + + result = + :next_ls + |> :code.priv_dir() + |> Path.join("monkey/_next_ls_private_compiler.ex") + |> then(fn path -> + if await_config_table(node, 5) do + :rpc.call(node, Code, :compile_file, [path]) + else + {:badrpc, "internal ets table not found"} + end + end) + |> then(fn + {:badrpc, error} -> + NextLS.Logger.error(logger, "Bad RPC call to node #{node}: #{inspect(error)}") + send(me, {:cancel, error}) + :error + + _ -> + :ok + end) + + if result == :ok do + {:ok, _} = :rpc.call(node, :_next_ls_private_compiler, :start, []) + + send(me, {:node, node}) + end - send(me, {:node, node}) + error -> + send(me, {:cancel, error}) end - else - error -> - send(me, {:cancel, error}) - end - end) - - {:ok, - %{ - name: name, - working_dir: working_dir, - compiler_refs: %{}, - port: port, - task_supervisor: task_supervisor, - logger: logger, - lsp_pid: lsp_pid, - parent: parent, - errors: nil, - registry: registry, - on_initialized: on_initialized - }} - else + end) + + {:ok, + %{ + name: name, + working_dir: working_dir, + compiler_refs: %{}, + port: port, + task_supervisor: task_supervisor, + logger: logger, + lsp_pid: lsp_pid, + parent: parent, + errors: nil, + registry: registry, + on_initialized: on_initialized + }} + _ -> NextLS.Logger.error(logger, "Either failed to find the private cmd wrapper script") diff --git a/lib/next_ls/runtime/sidecar.ex b/lib/next_ls/runtime/sidecar.ex index fe6d0886..70517c33 100644 --- a/lib/next_ls/runtime/sidecar.ex +++ b/lib/next_ls/runtime/sidecar.ex @@ -7,7 +7,7 @@ defmodule NextLS.Runtime.Sidecar do alias NextLS.DB def start_link(args) do - GenServer.start_link(__MODULE__, Keyword.drop(args, [:name]), Keyword.take(args, [:name])) + GenServer.start_link(__MODULE__, Keyword.delete(args, :name), Keyword.take(args, [:name])) end def init(args) do diff --git a/lib/next_ls/snippet.ex b/lib/next_ls/snippet.ex index 2dc6db89..926aaaba 100644 --- a/lib/next_ls/snippet.ex +++ b/lib/next_ls/snippet.ex @@ -1,12 +1,15 @@ defmodule NextLS.Snippet do @moduledoc false + alias GenLSP.Enumerations.CompletionItemKind + alias GenLSP.Enumerations.InsertTextFormat + def get(label, trigger_character, opts \\ []) def get("do", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ do $0 @@ -26,8 +29,8 @@ defmodule NextLS.Snippet do end %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defmodule ${1:#{modulename}} do $0 @@ -38,8 +41,8 @@ defmodule NextLS.Snippet do def get("defstruct/1", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defstruct [${1:field}: ${2:default}] """ @@ -48,8 +51,8 @@ defmodule NextLS.Snippet do def get("defprotocol/2", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defprotocol ${1:ProtocolName} do def ${2:function_name}(${3:parameter_name}) @@ -60,8 +63,8 @@ defmodule NextLS.Snippet do def get("defimpl/2", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defimpl ${1:ProtocolName} do def ${2:function_name}(${3:parameter_name}) do @@ -74,8 +77,8 @@ defmodule NextLS.Snippet do def get("defimpl/3", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defimpl ${1:ProtocolName}, for: ${2:StructName} do def ${3:function_name}(${4:parameter_name}) do @@ -88,8 +91,8 @@ defmodule NextLS.Snippet do def get("def/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ def ${1:function_name}(${2:parameter_1}) do $0 @@ -100,8 +103,8 @@ defmodule NextLS.Snippet do def get("defp/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defp ${1:function_name}(${2:parameter_1}) do $0 @@ -112,8 +115,8 @@ defmodule NextLS.Snippet do def get("defmacro/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defmacro ${1:macro_name}(${2:parameter_1}) do quote do @@ -126,8 +129,8 @@ defmodule NextLS.Snippet do def get("defmacrop/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ defmacrop ${1:macro_name}(${2:parameter_1}) do quote do @@ -140,8 +143,8 @@ defmodule NextLS.Snippet do def get("for/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ for ${2:item} <- ${1:enumerable} do $0 @@ -152,8 +155,8 @@ defmodule NextLS.Snippet do def get("with/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ with ${2:match} <- ${1:argument} do $0 @@ -164,8 +167,8 @@ defmodule NextLS.Snippet do def get("case/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ case ${1:argument} do ${2:match} -> @@ -180,8 +183,8 @@ defmodule NextLS.Snippet do def get("cond/" <> _, nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ cond do ${1:condition} -> @@ -196,8 +199,8 @@ defmodule NextLS.Snippet do def get("fn/1", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ fn $1 -> $0 @@ -208,8 +211,8 @@ defmodule NextLS.Snippet do def get("test/2", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ test "$1" do $0 @@ -220,8 +223,8 @@ defmodule NextLS.Snippet do def get("test/3", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ test "$1", %{$2: $3} do $0 @@ -232,8 +235,8 @@ defmodule NextLS.Snippet do def get("describe/2", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ describe "$1" do $0 @@ -244,8 +247,8 @@ defmodule NextLS.Snippet do def get("setup/1", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ setup do $0 @@ -256,8 +259,8 @@ defmodule NextLS.Snippet do def get("setup/2", nil, _opts) do %{ - kind: GenLSP.Enumerations.CompletionItemKind.snippet(), - insert_text_format: GenLSP.Enumerations.InsertTextFormat.snippet(), + kind: CompletionItemKind.snippet(), + insert_text_format: InsertTextFormat.snippet(), insert_text: """ setup ${1:context} do $0 diff --git a/mix.exs b/mix.exs index 68a0a194..ebc4b7fa 100644 --- a/mix.exs +++ b/mix.exs @@ -80,7 +80,7 @@ defmodule NextLS.MixProject do {:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7.6", only: [:dev, :test], runtime: false}, {:ex_doc, ">= 0.0.0", only: :dev}, - {:styler, "~> 0.8", only: :dev} + {:styler, "~> 1.0-rc.0", only: :dev} ] end diff --git a/mix.lock b/mix.lock index ecb363f0..8e0a7932 100644 --- a/mix.lock +++ b/mix.lock @@ -49,7 +49,7 @@ "sourceror": {:hex, :sourceror, "1.0.3", "111711c147f4f1414c07a67b45ad0064a7a41569037355407eda635649507f1d", [:mix], [], "hexpm", "56c21ef146c00b51bc3bb78d1f047cb732d193256a7c4ba91eaf828d3ae826af"}, "spitfire": {:git, "https://github.com/elixir-tools/spitfire.git", "178b00becd55b33e080f23c9ed0d1126d57574be", []}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "styler": {:hex, :styler, "0.8.1", "f3c0f65023e4bfbf7e7aa752d128b8475fdabfd30f96ee7314b84480cc56e788", [:mix], [], "hexpm", "1aa48d3aa689a639289af3d8254d40e068e98c083d6e5e3d1a695e71a147b344"}, + "styler": {:hex, :styler, "1.0.0-rc.0", "977c702b91b11e86ae1995f0f699a372a43e8df175f4878d7e9cc1678d0d7513", [:mix], [], "hexpm", "031624294295d47af7859ef43595092f33b861f0a88e44fae6366a54f1736a1a"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry_registry": {:hex, :telemetry_registry, "0.3.1", "14a3319a7d9027bdbff7ebcacf1a438f5f5c903057b93aee484cca26f05bdcba", [:mix, :rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6d0ca77b691cf854ed074b459a93b87f4c7f5512f8f7743c635ca83da81f939e"}, "tls_certificate_check": {:hex, :tls_certificate_check, "1.20.0", "1ac0c53f95e201feb8d398ef9d764ae74175231289d89f166ba88a7f50cd8e73", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "ab57b74b1a63dc5775650699a3ec032ec0065005eff1f020818742b7312a8426"}, diff --git a/priv/monkey/_next_ls_private_compiler.ex b/priv/monkey/_next_ls_private_compiler.ex index a2ff9a6f..7864281d 100644 --- a/priv/monkey/_next_ls_private_compiler.ex +++ b/priv/monkey/_next_ls_private_compiler.ex @@ -282,7 +282,7 @@ defmodule :_next_ls_private_formatter do defp load_plugins({formatter_opts, subs}) do plugins = Keyword.get(formatter_opts, :plugins, []) - if not is_list(plugins) do + unless is_list(plugins) do Mix.raise("Expected :plugins to return a list of modules, got: #{inspect(plugins)}") end @@ -360,11 +360,11 @@ defmodule :_next_ls_private_formatter do deps = Keyword.get(formatter_opts, :import_deps, []) subs = Keyword.get(formatter_opts, :subdirectories, []) - if not is_list(deps) do + unless is_list(deps) do Mix.raise("Expected :import_deps to return a list of dependencies, got: #{inspect(deps)}") end - if not is_list(subs) do + unless is_list(subs) do Mix.raise("Expected :subdirectories to return a list of directories, got: #{inspect(subs)}") end @@ -591,7 +591,7 @@ defmodule :_next_ls_private_formatter do ext in List.wrap(plugin.features(formatter_opts)[:extensions]) end) - if plugins != [], do: plugins, else: nil + unless plugins == [], do: plugins end defp find_formatter_and_opts_for_file(file, formatter_opts_and_subs) do diff --git a/test/next_ls/autocomplete_test.exs b/test/next_ls/autocomplete_test.exs index 290cc9cc..0ca95dbf 100644 --- a/test/next_ls/autocomplete_test.exs +++ b/test/next_ls/autocomplete_test.exs @@ -343,7 +343,7 @@ defmodule NextLS.AutocompleteTest do expand( runtime, ~c"~", - Map.merge(base_env(), %{macros: [{Kernel, [sigil_c: 1, sigil_C: 1, sigil_r: 1, foo: 1]}]}) + Map.put(base_env(), :macros, [{Kernel, [sigil_c: 1, sigil_C: 1, sigil_r: 1, foo: 1]}]) ) assert %{name: "C", kind: :sigil} in sigils diff --git a/test/next_ls/commands/pipe_test.exs b/test/next_ls/commands/pipe_test.exs index f276faae..2eefbdc5 100644 --- a/test/next_ls/commands/pipe_test.exs +++ b/test/next_ls/commands/pipe_test.exs @@ -1,6 +1,7 @@ defmodule NextLS.Commands.PipeTest do use ExUnit.Case, async: true + alias GenLSP.Structures.Position alias GenLSP.Structures.TextEdit alias GenLSP.Structures.WorkspaceEdit alias NextLS.Commands.Pipe @@ -177,8 +178,8 @@ defmodule NextLS.Commands.PipeTest do %TextEdit{ new_text: "foo |> Enum.to_list()", range: %GenLSP.Structures.Range{ - end: %GenLSP.Structures.Position{character: 20, line: 2}, - start: %GenLSP.Structures.Position{character: 4, line: 2} + end: %Position{character: 20, line: 2}, + start: %Position{character: 4, line: 2} } } ] @@ -509,8 +510,8 @@ defmodule NextLS.Commands.PipeTest do %GenLSP.Structures.TextEdit{ new_text: "Enum.to_list(map)", range: %GenLSP.Structures.Range{ - end: %GenLSP.Structures.Position{character: 21, line: 4}, - start: %GenLSP.Structures.Position{character: 4, line: 3} + end: %Position{character: 21, line: 4}, + start: %Position{character: 4, line: 3} } } ] diff --git a/test/next_ls/extensions/elixir_extension/code_action/require_test.exs b/test/next_ls/extensions/elixir_extension/code_action/require_test.exs index e960119c..253089f5 100644 --- a/test/next_ls/extensions/elixir_extension/code_action/require_test.exs +++ b/test/next_ls/extensions/elixir_extension/code_action/require_test.exs @@ -2,6 +2,7 @@ defmodule NextLS.ElixirExtension.RequireTest do use ExUnit.Case, async: true alias GenLSP.Structures.CodeAction + alias GenLSP.Structures.Diagnostic alias GenLSP.Structures.Position alias GenLSP.Structures.Range alias GenLSP.Structures.TextEdit @@ -23,7 +24,7 @@ defmodule NextLS.ElixirExtension.RequireTest do start = %Position{character: 11, line: 2} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{"namespace" => "elixir", "type" => "require"}, message: "you must require Logger before invoking the macro Logger.info/1", source: "Elixir", @@ -70,7 +71,7 @@ defmodule NextLS.ElixirExtension.RequireTest do start = %Position{character: 0, line: 2} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{"namespace" => "elixir", "type" => "require"}, message: "you must require Logger before invoking the macro Logger.info/1", source: "Elixir", @@ -117,7 +118,7 @@ defmodule NextLS.ElixirExtension.RequireTest do start = %Position{character: 0, line: 4} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{"namespace" => "elixir", "type" => "require"}, message: "you must require Logger before invoking the macro Logger.info/1", source: "Elixir", @@ -173,7 +174,7 @@ defmodule NextLS.ElixirExtension.RequireTest do start = %Position{character: 0, line: 11} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{"namespace" => "elixir", "type" => "require"}, message: "you must require Logger before invoking the macro Logger.info/1", source: "Elixir", diff --git a/test/next_ls/extensions/elixir_extension/code_action/undefined_function_test.exs b/test/next_ls/extensions/elixir_extension/code_action/undefined_function_test.exs index 4dc3180e..c1a54693 100644 --- a/test/next_ls/extensions/elixir_extension/code_action/undefined_function_test.exs +++ b/test/next_ls/extensions/elixir_extension/code_action/undefined_function_test.exs @@ -2,6 +2,7 @@ defmodule NextLS.ElixirExtension.UndefinedFunctionTest do use ExUnit.Case, async: true alias GenLSP.Structures.CodeAction + alias GenLSP.Structures.Diagnostic alias GenLSP.Structures.Position alias GenLSP.Structures.Range alias GenLSP.Structures.TextEdit @@ -35,7 +36,7 @@ defmodule NextLS.ElixirExtension.UndefinedFunctionTest do start = %Position{character: 4, line: 8} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{ "namespace" => "elixir", "type" => "undefined-function", @@ -123,7 +124,7 @@ defmodule NextLS.ElixirExtension.UndefinedFunctionTest do start = %Position{character: 6, line: 3} - diagnostic = %GenLSP.Structures.Diagnostic{ + diagnostic = %Diagnostic{ data: %{ "namespace" => "elixir", "type" => "undefined-function", diff --git a/test/next_ls/extensions/elixir_extension_test.exs b/test/next_ls/extensions/elixir_extension_test.exs index 8bfd96f6..1cc1654d 100644 --- a/test/next_ls/extensions/elixir_extension_test.exs +++ b/test/next_ls/extensions/elixir_extension_test.exs @@ -1,6 +1,7 @@ defmodule NextLS.ElixirExtensionTest do use ExUnit.Case, async: true + alias GenLSP.Structures.Position alias NextLS.DiagnosticCache alias NextLS.ElixirExtension @@ -72,11 +73,11 @@ defmodule NextLS.ElixirExtensionTest do " is undefined (module " <> "ElixirExtension" <> " is not available or is yet to be defined)", source: "Elixir", range: %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: 28, character: 0 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: 28, character: 999 } @@ -90,11 +91,11 @@ defmodule NextLS.ElixirExtensionTest do message: "kind of bad", source: "Elixir", range: %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: 1, character: 0 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: 1, character: 999 } @@ -108,11 +109,11 @@ defmodule NextLS.ElixirExtensionTest do message: "nothing works", source: "Elixir", range: %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: 3, character: 6 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: 3, character: 999 } @@ -126,11 +127,11 @@ defmodule NextLS.ElixirExtensionTest do message: "here's a hint", source: "Elixir", range: %GenLSP.Structures.Range{ - start: %GenLSP.Structures.Position{ + start: %Position{ line: 3, character: 6 }, - end: %GenLSP.Structures.Position{ + end: %Position{ line: 7, character: 2 } diff --git a/test/next_ls_test.exs b/test/next_ls_test.exs index 23769f07..cc63a6de 100644 --- a/test/next_ls_test.exs +++ b/test/next_ls_test.exs @@ -186,6 +186,11 @@ defmodule NextLSTest do } assert_result 2, nil + + assert_notification "window/showMessage", %{ + "message" => "Failed to format lib/foo/bar.ex: missing terminator: end", + "type" => 1 + } end test "workspace symbols", %{client: client, cwd: cwd} = context do