Skip to content

Commit

Permalink
Consider surround context until end whenever possible
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jun 30, 2023
1 parent 2c78c73 commit a65dae9
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 37 deletions.
26 changes: 21 additions & 5 deletions lib/elixir/lib/code/fragment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -619,15 +619,15 @@ defmodule Code.Fragment do
{reversed_pre, post} = adjust_position(reversed_pre, post)

case take_identifier(post, []) do
{_, [], _} ->
:none ->
maybe_operator(reversed_pre, post, line, opts)

{:identifier, reversed_post, rest} ->
{rest, _} = strip_spaces(rest, 0)
reversed = reversed_post ++ reversed_pre

case codepoint_cursor_context(reversed, opts) do
{{:struct, acc}, offset} ->
{{:struct, acc}, offset} when acc != [] ->
build_surround({:struct, acc}, reversed, line, offset)

{{:alias, acc}, offset} ->
Expand Down Expand Up @@ -729,15 +729,31 @@ defmodule Code.Fragment do
do: take_identifier(t, [h | acc])

defp take_identifier(rest, acc) do
with {[?. | t], _} <- strip_spaces(rest, 0),
{stripped, _} = strip_spaces(rest, 0)

with [?. | t] <- stripped,
{[h | _], _} when h in ?A..?Z <- strip_spaces(t, 0) do
take_alias(rest, acc)
else
_ -> {:identifier, acc, rest}
# Consider it an identifier if we are at the end of line
# or if we have spaces not followed by . (call) or / (arity)
_ when acc == [] and (rest == [] or (hd(rest) in @space and hd(stripped) not in ~c"/.")) ->
{:identifier, acc, rest}

# If we are immediately followed by a container, we are still part of the identifier.
# We don't consider << as it _may_ be an operator.
_ when acc == [] and hd(stripped) in ~c"({[" ->
{:identifier, acc, rest}

_ when acc == [] ->
:none

_ ->
{:identifier, acc, rest}
end
end

defp take_alias([h | t], acc) when h not in @non_identifier,
defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..9 or h == ?_,
do: take_alias(t, [h | acc])

defp take_alias(rest, acc) do
Expand Down
96 changes: 64 additions & 32 deletions lib/elixir/test/elixir/code_fragment_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -428,49 +428,50 @@ defmodule CodeFragmentTest do
end

test "column out of range" do
assert CF.surround_context("hello", {1, 20}) == :none
assert CF.surround_context("hello", {1, 20}) ==
%{begin: {1, 1}, context: {:local_or_var, ~c"hello"}, end: {1, 6}}
end

test "local_or_var" do
for i <- 1..8 do
for i <- 1..9 do
assert CF.surround_context("hello_wo", {1, i}) == %{
context: {:local_or_var, ~c"hello_wo"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("hello_wo", {1, 9}) == :none
assert CF.surround_context("hello_wo ", {1, 10}) == :none

for i <- 2..9 do
for i <- 2..10 do
assert CF.surround_context(" hello_wo", {1, i}) == %{
context: {:local_or_var, ~c"hello_wo"},
begin: {1, 2},
end: {1, 10}
}
end

assert CF.surround_context(" hello_wo", {1, 10}) == :none
assert CF.surround_context(" hello_wo ", {1, 11}) == :none

for i <- 1..6 do
for i <- 1..7 do
assert CF.surround_context("hello!", {1, i}) == %{
context: {:local_or_var, ~c"hello!"},
begin: {1, 1},
end: {1, 7}
}
end

assert CF.surround_context("hello!", {1, 7}) == :none
assert CF.surround_context("hello! ", {1, 8}) == :none

for i <- 1..5 do
for i <- 1..6 do
assert CF.surround_context("안녕_세상", {1, i}) == %{
context: {:local_or_var, ~c"안녕_세상"},
begin: {1, 1},
end: {1, 6}
}
end

assert CF.surround_context("안녕_세상", {1, 6}) == :none
assert CF.surround_context("안녕_세상 ", {1, 6}) == :none

# Keywords are not local or var
for keyword <- ~w(do end after catch else rescue fn true false nil)c do
Expand All @@ -484,46 +485,77 @@ defmodule CodeFragmentTest do
end
end

test "local call" do
test "local + operator" do
for i <- 1..8 do
assert CF.surround_context("hello_wo+", {1, i}) == %{
context: {:local_or_var, ~c"hello_wo"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("hello_wo+", {1, 9}) == %{
begin: {1, 9},
context: {:operator, ~c"+"},
end: {1, 10}
}

for i <- 1..9 do
assert CF.surround_context("hello_wo +", {1, i}) == %{
context: {:local_or_var, ~c"hello_wo"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("hello_wo +", {1, 10}) == %{
begin: {1, 10},
context: {:operator, ~c"+"},
end: {1, 11}
}
end

test "local call" do
for i <- 1..9 do
assert CF.surround_context("hello_wo(", {1, i}) == %{
context: {:local_call, ~c"hello_wo"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("hello_wo(", {1, 9}) == :none
assert CF.surround_context("hello_wo(", {1, 10}) == :none

for i <- 1..8 do
for i <- 1..9 do
assert CF.surround_context("hello_wo (", {1, i}) == %{
context: {:local_call, ~c"hello_wo"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("hello_wo (", {1, 9}) == :none
assert CF.surround_context("hello_wo (", {1, 10}) == :none
assert CF.surround_context("hello_wo (", {1, 11}) == :none

for i <- 1..6 do
for i <- 1..7 do
assert CF.surround_context("hello!(", {1, i}) == %{
context: {:local_call, ~c"hello!"},
begin: {1, 1},
end: {1, 7}
}
end

assert CF.surround_context("hello!(", {1, 7}) == :none
assert CF.surround_context("hello!(", {1, 8}) == :none

for i <- 1..5 do
for i <- 1..6 do
assert CF.surround_context("안녕_세상(", {1, i}) == %{
context: {:local_call, ~c"안녕_세상"},
begin: {1, 1},
end: {1, 6}
}
end

assert CF.surround_context("안녕_세상(", {1, 6}) == :none
assert CF.surround_context("안녕_세상(", {1, 7}) == :none
end

test "local arity" do
Expand Down Expand Up @@ -651,47 +683,47 @@ defmodule CodeFragmentTest do
end

test "alias" do
for i <- 1..8 do
for i <- 1..9 do
assert CF.surround_context("HelloWor", {1, i}) == %{
context: {:alias, ~c"HelloWor"},
begin: {1, 1},
end: {1, 9}
}
end

assert CF.surround_context("HelloWor", {1, 9}) == :none
assert CF.surround_context("HelloWor ", {1, 10}) == :none

for i <- 2..9 do
for i <- 2..10 do
assert CF.surround_context(" HelloWor", {1, i}) == %{
context: {:alias, ~c"HelloWor"},
begin: {1, 2},
end: {1, 10}
}
end

assert CF.surround_context(" HelloWor", {1, 10}) == :none
assert CF.surround_context(" HelloWor ", {1, 11}) == :none

for i <- 1..9 do
for i <- 1..10 do
assert CF.surround_context("Hello.Wor", {1, i}) == %{
context: {:alias, ~c"Hello.Wor"},
begin: {1, 1},
end: {1, 10}
}
end

assert CF.surround_context("Hello.Wor", {1, 10}) == :none
assert CF.surround_context("Hello.Wor ", {1, 11}) == :none

for i <- 1..11 do
for i <- 1..12 do
assert CF.surround_context("Hello . Wor", {1, i}) == %{
context: {:alias, ~c"Hello.Wor"},
begin: {1, 1},
end: {1, 12}
}
end

assert CF.surround_context("Hello . Wor", {1, 12}) == :none
assert CF.surround_context("Hello . Wor ", {1, 13}) == :none

for i <- 1..15 do
for i <- 1..16 do
assert CF.surround_context("Foo . Bar . Baz", {1, i}) == %{
context: {:alias, ~c"Foo.Bar.Baz"},
begin: {1, 1},
Expand Down Expand Up @@ -858,15 +890,15 @@ defmodule CodeFragmentTest do
end: {1, 15}
}

for i <- 2..9 do
for i <- 2..10 do
assert CF.surround_context("%HelloWor", {1, i}) == %{
context: {:struct, ~c"HelloWor"},
begin: {1, 1},
end: {1, 10}
}
end

assert CF.surround_context("%HelloWor", {1, 10}) == :none
assert CF.surround_context("%HelloWor ", {1, 11}) == :none

# With dot
assert CF.surround_context("%Hello.Wor", {1, 1}) == %{
Expand All @@ -875,15 +907,15 @@ defmodule CodeFragmentTest do
end: {1, 11}
}

for i <- 2..10 do
for i <- 2..11 do
assert CF.surround_context("%Hello.Wor", {1, i}) == %{
context: {:struct, ~c"Hello.Wor"},
begin: {1, 1},
end: {1, 11}
}
end

assert CF.surround_context("%Hello.Wor", {1, 11}) == :none
assert CF.surround_context("%Hello.Wor ", {1, 12}) == :none

# With spaces
assert CF.surround_context("% Hello . Wor", {1, 1}) == %{
Expand All @@ -892,15 +924,15 @@ defmodule CodeFragmentTest do
end: {1, 14}
}

for i <- 2..13 do
for i <- 2..14 do
assert CF.surround_context("% Hello . Wor", {1, i}) == %{
context: {:struct, ~c"Hello.Wor"},
begin: {1, 1},
end: {1, 14}
}
end

assert CF.surround_context("% Hello . Wor", {1, 14}) == :none
assert CF.surround_context("% Hello . Wor ", {1, 15}) == :none
end

test "module attributes" do
Expand Down

0 comments on commit a65dae9

Please sign in to comment.