diff options
author | José Valim <jose.valim@dashbit.co> | 2021-10-15 20:25:30 +0200 |
---|---|---|
committer | José Valim <jose.valim@dashbit.co> | 2021-10-15 20:25:30 +0200 |
commit | 5f6586db2ef0aa7d33e5cef2b3a4f9c32d6ae918 (patch) | |
tree | d8773b76a6ab504aa5640805ec5c1a4b793e602b | |
parent | f032aad45f94d175972c97747807ad734d91d65b (diff) | |
download | elixir-jv-cursor-to-quoted.tar.gz |
Rename to container_cursor_to_quotedjv-cursor-to-quoted
-rw-r--r-- | lib/elixir/lib/code/fragment.ex | 24 | ||||
-rw-r--r-- | lib/elixir/test/elixir/code_fragment_test.exs | 70 |
2 files changed, 50 insertions, 44 deletions
diff --git a/lib/elixir/lib/code/fragment.ex b/lib/elixir/lib/code/fragment.ex index 01ed99214..8d8bbb834 100644 --- a/lib/elixir/lib/code/fragment.ex +++ b/lib/elixir/lib/code/fragment.ex @@ -724,8 +724,10 @@ defmodule Code.Fragment do Receives a code fragment and returns a quoted expression with a cursor at the nearest argument position. - For example, take this code, which would be given as an - input string: + A container is any Elixir expression starting with `(`, + `{`, and `[`. This includes function calls, tuples, lists, + maps, and so on. For example, take this code, which would + be given as input: max(some_value, @@ -752,15 +754,19 @@ defmodule Code.Fragment do max(__cursor__()) - Operators and anonymous functions will be discarded. - All of the following will all return the same AST: + Calls without parenthesis are also supported, as we assume the + brackets are implicit. + + Operators and anonymous functions are not containers, and therefore + will be discarded. The following will all return the same AST: max(some_value, max(some_value, fn x -> x end max(some_value, 1 + another_val max(some_value, 1 |> some_fun() |> another_fun - On the other hand, tuples, lists, maps, etc are all retained: + On the other hand, tuples, lists, maps, etc all retain the + cursor position: max(some_value, [1, 2, @@ -768,7 +774,7 @@ defmodule Code.Fragment do max(some_value, [1, 2, __cursor__()]) - Keyword lists (and do-end blocks) are retained. The following: + Keyword lists (and do-end blocks) are also retained. The following: if(some_value, do: if(some_value, do: :token @@ -783,7 +789,7 @@ defmodule Code.Fragment do ## Examples - iex> Code.Fragment.argument_cursor_to_quoted("max(some_value, ") + iex> Code.Fragment.container_cursor_to_quoted("max(some_value, ") {:ok, {:max, [line: 1], [{:some_value, [line: 1], nil}, {:__cursor__, [line: 1], []}]}} ## Options @@ -802,9 +808,9 @@ defmodule Code.Fragment do """ @doc since: "1.13.0" - @spec argument_cursor_to_quoted(List.Chars.t(), keyword()) :: + @spec container_cursor_to_quoted(List.Chars.t(), keyword()) :: {:ok, Macro.t()} | {:error, {location :: keyword, binary | {binary, binary}, binary}} - def argument_cursor_to_quoted(fragment, opts \\ []) do + def container_cursor_to_quoted(fragment, opts \\ []) do file = Keyword.get(opts, :file, "nofile") line = Keyword.get(opts, :line, 1) column = Keyword.get(opts, :column, 1) diff --git a/lib/elixir/test/elixir/code_fragment_test.exs b/lib/elixir/test/elixir/code_fragment_test.exs index ddf12a3bb..097540643 100644 --- a/lib/elixir/test/elixir/code_fragment_test.exs +++ b/lib/elixir/test/elixir/code_fragment_test.exs @@ -802,64 +802,64 @@ defmodule CodeFragmentTest do end end - describe "argument_cursor_to_quoted/2" do + describe "container_cursor_to_quoted/2" do def s2q(arg), do: Code.string_to_quoted(arg) - def ac2q(arg), do: CF.argument_cursor_to_quoted(arg) + def cc2q(arg), do: CF.container_cursor_to_quoted(arg) # TODO: maps and structs and keywords (kw_identifier kw_identifier_safe kw_identifier_unsafe) test "completes terminators" do - assert ac2q("(") == s2q("(__cursor__())") - assert ac2q("[") == s2q("[__cursor__()]") - assert ac2q("{") == s2q("{__cursor__()}") - assert ac2q("<<") == s2q("<<__cursor__()>>") - assert ac2q("%{") == s2q("%{__cursor__()}") - assert ac2q("foo do") == s2q("foo do __cursor__() end") - assert ac2q("foo do true else") == s2q("foo do true else __cursor__() end") + assert cc2q("(") == s2q("(__cursor__())") + assert cc2q("[") == s2q("[__cursor__()]") + assert cc2q("{") == s2q("{__cursor__()}") + assert cc2q("<<") == s2q("<<__cursor__()>>") + assert cc2q("%{") == s2q("%{__cursor__()}") + assert cc2q("foo do") == s2q("foo do __cursor__() end") + assert cc2q("foo do true else") == s2q("foo do true else __cursor__() end") end test "inside interpolation" do - assert ac2q(~S|"foo #{(|) == s2q(~S|"foo #{(__cursor__())}"|) - assert ac2q(~S|"foo #{"bar #{{|) == s2q(~S|"foo #{"bar #{{__cursor__()}}"}"|) + assert cc2q(~S|"foo #{(|) == s2q(~S|"foo #{(__cursor__())}"|) + assert cc2q(~S|"foo #{"bar #{{|) == s2q(~S|"foo #{"bar #{{__cursor__()}}"}"|) end test "do-end blocks" do - assert ac2q("foo(bar do baz") == s2q("foo(bar do __cursor__() end)") - assert ac2q("foo(bar do baz ") == s2q("foo(bar do baz(__cursor__()) end)") - assert ac2q("foo(bar do baz(") == s2q("foo(bar do baz(__cursor__()) end)") - assert ac2q("foo(bar do baz bat,") == s2q("foo(bar do baz(bat, __cursor__()) end)") + assert cc2q("foo(bar do baz") == s2q("foo(bar do __cursor__() end)") + assert cc2q("foo(bar do baz ") == s2q("foo(bar do baz(__cursor__()) end)") + assert cc2q("foo(bar do baz(") == s2q("foo(bar do baz(__cursor__()) end)") + assert cc2q("foo(bar do baz bat,") == s2q("foo(bar do baz(bat, __cursor__()) end)") - assert {:error, {_, "syntax error before: ", "'end'"}} = ac2q("foo(bar do baz, bat") + assert {:error, {_, "syntax error before: ", "'end'"}} = cc2q("foo(bar do baz, bat") end test "removes tokens until opening" do - assert ac2q("(123") == s2q("(__cursor__())") - assert ac2q("[foo") == s2q("[__cursor__()]") - assert ac2q("{'foo'") == s2q("{__cursor__()}") - assert ac2q("<<1+2") == s2q("<<__cursor__()>>") - assert ac2q("foo do :atom") == s2q("foo do __cursor__() end") - assert ac2q("foo(:atom") == s2q("foo(__cursor__())") + assert cc2q("(123") == s2q("(__cursor__())") + assert cc2q("[foo") == s2q("[__cursor__()]") + assert cc2q("{'foo'") == s2q("{__cursor__()}") + assert cc2q("<<1+2") == s2q("<<__cursor__()>>") + assert cc2q("foo do :atom") == s2q("foo do __cursor__() end") + assert cc2q("foo(:atom") == s2q("foo(__cursor__())") end test "removes functions" do - assert ac2q("(fn") == s2q("(__cursor__())") - assert ac2q("(fn x") == s2q("(__cursor__())") - assert ac2q("(fn x ->") == s2q("(__cursor__())") - assert ac2q("(fn x -> x") == s2q("(__cursor__())") - assert ac2q("(fn x, y -> x + y") == s2q("(__cursor__())") - assert ac2q("(fn x, y -> x + y end") == s2q("(__cursor__())") + assert cc2q("(fn") == s2q("(__cursor__())") + assert cc2q("(fn x") == s2q("(__cursor__())") + assert cc2q("(fn x ->") == s2q("(__cursor__())") + assert cc2q("(fn x -> x") == s2q("(__cursor__())") + assert cc2q("(fn x, y -> x + y") == s2q("(__cursor__())") + assert cc2q("(fn x, y -> x + y end") == s2q("(__cursor__())") end test "removes captures" do - assert ac2q("[& &1") == s2q("[__cursor__()]") - assert ac2q("[&(&1") == s2q("[__cursor__()]") + assert cc2q("[& &1") == s2q("[__cursor__()]") + assert cc2q("[&(&1") == s2q("[__cursor__()]") end test "removes closed terminators" do - assert ac2q("foo([1, 2, 3] |>") == s2q("foo(__cursor__())") - assert ac2q("foo({1, 2, 3} |>") == s2q("foo(__cursor__())") - assert ac2q("foo((1, 2, 3) |>") == s2q("foo(__cursor__())") - assert ac2q("foo(<<1, 2, 3>> |>") == s2q("foo(__cursor__())") - assert ac2q("foo(bar do :done end |>") == s2q("foo(__cursor__())") + assert cc2q("foo([1, 2, 3] |>") == s2q("foo(__cursor__())") + assert cc2q("foo({1, 2, 3} |>") == s2q("foo(__cursor__())") + assert cc2q("foo((1, 2, 3) |>") == s2q("foo(__cursor__())") + assert cc2q("foo(<<1, 2, 3>> |>") == s2q("foo(__cursor__())") + assert cc2q("foo(bar do :done end |>") == s2q("foo(__cursor__())") end end end |