From 7a3cf91a6601ff4ae296d5774c5ae5678ae902f5 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Fri, 9 Feb 2024 21:36:30 +0100 Subject: [PATCH] address todos --- .../lib/language_server/ast_utils.ex | 41 +++++++++++-------- .../providers/selection_ranges.ex | 14 ++++--- .../lib/language_server/server.ex | 12 +++++- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/apps/language_server/lib/language_server/ast_utils.ex b/apps/language_server/lib/language_server/ast_utils.ex index 3463cde67..9702705d1 100644 --- a/apps/language_server/lib/language_server/ast_utils.ex +++ b/apps/language_server/lib/language_server/ast_utils.ex @@ -5,9 +5,10 @@ defmodule ElixirLS.LanguageServer.AstUtils do @binary_operators ~w[| . ** * / + - ++ -- +++ --- .. <> in |> <<< >>> <<~ ~>> <~ ~> <~> < > <= >= == != === !== =~ && &&& and || ||| or = => :: when <- -> \\]a @unary_operators ~w[@ + - ! ^ not &]a - def node_range(atom) when is_atom(atom), do: nil + def node_range(node, options \\ []) + def node_range(atom, _options) when is_atom(atom), do: nil - def node_range([{{:__block__, _, [_]} = first, _} | _] = list) do + def node_range([{{:__block__, _, [_]} = first, _} | _] = list, _options) do case List.last(list) do {_, last} -> case {node_range(first), node_range(last)} do @@ -23,9 +24,9 @@ defmodule ElixirLS.LanguageServer.AstUtils do end end - def node_range(list) when is_list(list), do: nil + def node_range(list, _options) when is_list(list), do: nil - def node_range({:__block__, meta, args} = _ast) do + def node_range({:__block__, meta, args} = _ast, _options) do line = Keyword.get(meta, :line) column = Keyword.get(meta, :column) @@ -85,10 +86,10 @@ defmodule ElixirLS.LanguageServer.AstUtils do end # interpolated charlist AST is too complicated to handle via the generic algorithm - def node_range({{:., _, [List, :to_charlist]}, meta, _args} = ast) do + def node_range({{:., _, [List, :to_charlist]}, meta, _args} = ast, options) do line = Keyword.get(meta, :line) - 1 column = Keyword.get(meta, :column) - 1 - {end_line, end_column} = get_eoe_by_formatting(ast, {line, column}) + {end_line, end_column} = get_eoe_by_formatting(ast, {line, column}, options) # on elixir 1.15+ formatter changes charlist '' to ~c"" sigil so we need to correct columns # if charlist is single line correction = @@ -102,14 +103,14 @@ defmodule ElixirLS.LanguageServer.AstUtils do end # interpolated atom AST is too complicated to handle via the generic algorithm - def node_range({{:., _, [:erlang, :binary_to_atom]}, meta, _args} = ast) do + def node_range({{:., _, [:erlang, :binary_to_atom]}, meta, _args} = ast, options) do line = Keyword.get(meta, :line) - 1 column = Keyword.get(meta, :column) - 1 - {end_line, end_column} = get_eoe_by_formatting(ast, {line, column}) + {end_line, end_column} = get_eoe_by_formatting(ast, {line, column}, options) range(line, column, end_line, end_column) end - def node_range({form, meta, args} = ast) do + def node_range({form, meta, args} = ast, options) do line = Keyword.get(meta, :line) column = Keyword.get(meta, :column) @@ -218,7 +219,7 @@ defmodule ElixirLS.LanguageServer.AstUtils do {last[:line] - 1, last[:column] - 1 + last_length} else # last is nil on 1.12 - get_eoe_by_formatting(ast, {line, column}) + get_eoe_by_formatting(ast, {line, column}, options) end form == :% and match?([_, _], args) -> @@ -235,7 +236,7 @@ defmodule ElixirLS.LanguageServer.AstUtils do form == :<<>> or (is_atom(form) and String.starts_with?(to_string(form), "sigil_")) -> # interpolated string AST is too complicated # try to format it instead - get_eoe_by_formatting(ast, {line, column}) + get_eoe_by_formatting(ast, {line, column}, options) form == :& and match?([int] when is_integer(int), args) -> [int] = args @@ -317,7 +318,7 @@ defmodule ElixirLS.LanguageServer.AstUtils do end end - def node_range(_), do: nil + def node_range(_, _options), do: nil def get_literal_end(true, {line, column}, _), do: {line, column + 4} def get_literal_end(false, {line, column}, _), do: {line, column + 5} @@ -376,15 +377,19 @@ defmodule ElixirLS.LanguageServer.AstUtils do def get_delimiter_length("\"\"\""), do: 3 def get_delimiter_length("'''"), do: 3 - defp get_eoe_by_formatting(ast, {line, column}) do - # TODO pass line_length to format - # TODO locals_without_parens to quoted_to_algebra - # TODO pass comments to quoted_to_algebra + defp get_eoe_by_formatting(ast, {line, column}, options) do + formatter_opts = Keyword.get(options, :formatter_opts, []) + locals_without_parens = Keyword.get(formatter_opts, :locals_without_parens, []) + line_length = Keyword.get(formatter_opts, :line_length, 98) + code = if Version.match?(System.version(), ">= 1.13.0-dev") do ast - |> Code.quoted_to_algebra(escape: false) - |> Inspect.Algebra.format(:infinity) + |> Code.quoted_to_algebra( + escape: false, + locals_without_parens: locals_without_parens + ) + |> Inspect.Algebra.format(line_length) |> IO.iodata_to_binary() else Macro.to_string(ast) diff --git a/apps/language_server/lib/language_server/providers/selection_ranges.ex b/apps/language_server/lib/language_server/providers/selection_ranges.ex index f0ac178a1..59da94ed8 100644 --- a/apps/language_server/lib/language_server/providers/selection_ranges.ex +++ b/apps/language_server/lib/language_server/providers/selection_ranges.ex @@ -19,7 +19,7 @@ defmodule ElixirLS.LanguageServer.Providers.SelectionRanges do @stop_tokens [:",", :";", :eol, :eof, :pipe_op] - def selection_ranges(text, positions) do + def selection_ranges(text, positions, options \\ []) do lines = SourceFile.lines(text) full_file_range = full_range(lines) @@ -71,7 +71,7 @@ defmodule ElixirLS.LanguageServer.Providers.SelectionRanges do comment_block_ranges = comment_block_ranges(lines, comment_groups, line, character) - ast_node_ranges = ast_node_ranges(parse_result, line, character) + ast_node_ranges = ast_node_ranges(parse_result, line, character, options) surround_context_ranges = surround_context_ranges(text, line, character) @@ -334,7 +334,11 @@ defmodule ElixirLS.LanguageServer.Providers.SelectionRanges do @empty_node {:__block__, [], []} - def ast_node_ranges({:ok, ast}, line, character) do + def ast_node_ranges({:ok, ast}, line, character, options) do + node_range_options = [ + formatter_opts: Keyword.get(options, :formatter_opts, []) + ] + {_new_ast, {acc, [@empty_node]}} = Macro.traverse( ast, @@ -342,7 +346,7 @@ defmodule ElixirLS.LanguageServer.Providers.SelectionRanges do fn ast, {acc, [parent_ast_from_stack | _] = parent_ast} -> matching_range = - case AstUtils.node_range(ast) do + case AstUtils.node_range(ast, node_range_options) do range(start_line, start_character, end_line, end_character) -> start_character = if match?({:%{}, _, _}, ast) and match?({:%, _, _}, parent_ast_from_stack) and @@ -403,7 +407,7 @@ defmodule ElixirLS.LanguageServer.Providers.SelectionRanges do |> sort_ranges_widest_to_narrowest() end - def ast_node_ranges(_, _, _), do: [] + def ast_node_ranges(_, _, _, _), do: [] def surround_context_ranges(text, line, character) do case NormalizedCode.Fragment.surround_context(text, {line + 1, character + 1}) do diff --git a/apps/language_server/lib/language_server/server.ex b/apps/language_server/lib/language_server/server.ex index 745107e35..8f8ed1567 100644 --- a/apps/language_server/lib/language_server/server.ex +++ b/apps/language_server/lib/language_server/server.ex @@ -1224,7 +1224,17 @@ defmodule ElixirLS.LanguageServer.Server do fun = fn -> if String.ends_with?(uri, [".ex", ".exs"]) or source_file.language_id in ["elixir"] do - ranges = SelectionRanges.selection_ranges(source_file.text, positions) + formatter_opts = + case SourceFile.formatter_for(uri, state.project_dir, state.mix_project?) do + {:ok, {_, opts, _formatter_exs_dir}} -> opts + {:error, _} -> [] + end + + ranges = + SelectionRanges.selection_ranges(source_file.text, positions, + formatter_opts: formatter_opts + ) + {:ok, ranges} else # TODO no support for eex