summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@dashbit.co>2021-10-15 20:25:30 +0200
committerJosé Valim <jose.valim@dashbit.co>2021-10-15 20:25:30 +0200
commit5f6586db2ef0aa7d33e5cef2b3a4f9c32d6ae918 (patch)
treed8773b76a6ab504aa5640805ec5c1a4b793e602b
parentf032aad45f94d175972c97747807ad734d91d65b (diff)
downloadelixir-jv-cursor-to-quoted.tar.gz
Rename to container_cursor_to_quotedjv-cursor-to-quoted
-rw-r--r--lib/elixir/lib/code/fragment.ex24
-rw-r--r--lib/elixir/test/elixir/code_fragment_test.exs70
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