Skip to content

Commit 329442c

Browse files
committed
Provide more AST around cursor fragments, closes #14118
1 parent 01a88e7 commit 329442c

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

Diff for: lib/elixir/lib/code/fragment.ex

+23-11
Original file line numberDiff line numberDiff line change
@@ -1151,7 +1151,7 @@ defmodule Code.Fragment do
11511151
{rev_tokens, rev_terminators} =
11521152
with [close, open, {_, _, :__cursor__} = cursor | rev_tokens] <- rev_tokens,
11531153
{_, [_ | after_fn]} <- Enum.split_while(rev_terminators, &(elem(&1, 0) != :fn)),
1154-
true <- maybe_missing_stab?(rev_tokens),
1154+
true <- maybe_missing_stab?(rev_tokens, false),
11551155
[_ | rev_tokens] <- Enum.drop_while(rev_tokens, &(elem(&1, 0) != :fn)) do
11561156
{[close, open, cursor | rev_tokens], after_fn}
11571157
else
@@ -1165,14 +1165,22 @@ defmodule Code.Fragment do
11651165
tokens =
11661166
with {before_start, [_ | _] = after_start} <-
11671167
Enum.split_while(rev_terminators, &(elem(&1, 0) not in [:do, :fn])),
1168-
true <- maybe_missing_stab?(rev_tokens),
1168+
true <- maybe_missing_stab?(rev_tokens, true),
11691169
opts =
11701170
Keyword.put(opts, :check_terminators, {:cursor, before_start}),
11711171
{:error, {meta, _, ~c"end"}, _rest, _warnings, trailing_rev_tokens} <-
11721172
:elixir_tokenizer.tokenize(to_charlist(trailing_fragment), line, column, opts) do
11731173
trailing_tokens =
11741174
reverse_tokens(meta[:line], meta[:column], trailing_rev_tokens, after_start)
11751175

1176+
# If the cursor has its own line, then we do not trim new lines trailing tokens.
1177+
# Otherwise we want to drop any newline so we drop the next tokens after eol.
1178+
trailing_tokens =
1179+
case rev_tokens do
1180+
[_close, _open, {_, _, :__cursor__}, {:eol, _} | _] -> trailing_tokens
1181+
_ -> Enum.drop_while(trailing_tokens, &match?({:eol, _}, &1))
1182+
end
1183+
11761184
Enum.reverse(rev_tokens, drop_tokens(trailing_tokens, 0))
11771185
else
11781186
_ -> reverse_tokens(line, column, rev_tokens, rev_terminators)
@@ -1196,12 +1204,16 @@ defmodule Code.Fragment do
11961204
Enum.reverse(tokens, terminators)
11971205
end
11981206

1207+
# Otherwise we drop all tokens, trying to build a minimal AST
1208+
# for cursor completion.
11991209
defp drop_tokens([{:"}", _} | _] = tokens, 0), do: tokens
12001210
defp drop_tokens([{:"]", _} | _] = tokens, 0), do: tokens
12011211
defp drop_tokens([{:")", _} | _] = tokens, 0), do: tokens
12021212
defp drop_tokens([{:">>", _} | _] = tokens, 0), do: tokens
12031213
defp drop_tokens([{:end, _} | _] = tokens, 0), do: tokens
12041214
defp drop_tokens([{:",", _} | _] = tokens, 0), do: tokens
1215+
defp drop_tokens([{:";", _} | _] = tokens, 0), do: tokens
1216+
defp drop_tokens([{:eol, _} | _] = tokens, 0), do: tokens
12051217
defp drop_tokens([{:stab_op, _, :->} | _] = tokens, 0), do: tokens
12061218

12071219
defp drop_tokens([{:"}", _} | tokens], counter), do: drop_tokens(tokens, counter - 1)
@@ -1220,13 +1232,13 @@ defmodule Code.Fragment do
12201232
defp drop_tokens([_ | tokens], counter), do: drop_tokens(tokens, counter)
12211233
defp drop_tokens([], 0), do: []
12221234

1223-
defp maybe_missing_stab?([{:after, _} | _]), do: true
1224-
defp maybe_missing_stab?([{:do, _} | _]), do: true
1225-
defp maybe_missing_stab?([{:fn, _} | _]), do: true
1226-
defp maybe_missing_stab?([{:else, _} | _]), do: true
1227-
defp maybe_missing_stab?([{:catch, _} | _]), do: true
1228-
defp maybe_missing_stab?([{:rescue, _} | _]), do: true
1229-
defp maybe_missing_stab?([{:stab_op, _, :->} | _]), do: false
1230-
defp maybe_missing_stab?([_ | tail]), do: maybe_missing_stab?(tail)
1231-
defp maybe_missing_stab?([]), do: false
1235+
defp maybe_missing_stab?([{:after, _} | _], _stab_choice?), do: true
1236+
defp maybe_missing_stab?([{:do, _} | _], _stab_choice?), do: true
1237+
defp maybe_missing_stab?([{:fn, _} | _], _stab_choice?), do: true
1238+
defp maybe_missing_stab?([{:else, _} | _], _stab_choice?), do: true
1239+
defp maybe_missing_stab?([{:catch, _} | _], _stab_choice?), do: true
1240+
defp maybe_missing_stab?([{:rescue, _} | _], _stab_choice?), do: true
1241+
defp maybe_missing_stab?([{:stab_op, _, :->} | _], stab_choice?), do: stab_choice?
1242+
defp maybe_missing_stab?([_ | tail], stab_choice?), do: maybe_missing_stab?(tail, stab_choice?)
1243+
defp maybe_missing_stab?([], _stab_choice?), do: false
12321244
end

Diff for: lib/elixir/test/elixir/code_fragment_test.exs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1335,19 +1335,22 @@ defmodule CodeFragmentTest do
13351335

13361336
test "do -> end" do
13371337
assert cc2q!("if do\nx ->\n", trailing_fragment: "y\nz ->\nw\nend") ==
1338-
s2q!("if do\nx ->\n__cursor__()\nend")
1338+
s2q!("if do\nx ->\n__cursor__()\nz -> \nw\nend")
13391339

13401340
assert cc2q!("if do\nx ->\ny", trailing_fragment: "\nz ->\nw\nend") ==
1341-
s2q!("if do\nx ->\n__cursor__()\nend")
1341+
s2q!("if do\nx ->\n__cursor__()\nz -> \nw\nend")
13421342

13431343
assert cc2q!("if do\nx ->\ny\n", trailing_fragment: "\nz ->\nw\nend") ==
1344-
s2q!("if do\nx ->\ny\n__cursor__()\nend")
1344+
s2q!("if do\nx ->\ny\n__cursor__()\nz -> \nw\nend")
13451345

13461346
assert cc2q!("for x <- [], reduce: %{} do\ny, ", trailing_fragment: "-> :ok\nend") ==
13471347
s2q!("for x <- [], reduce: %{} do\ny, __cursor__() -> :ok\nend")
13481348

13491349
assert cc2q!("for x <- [], reduce: %{} do\ny, z when ", trailing_fragment: "-> :ok\nend") ==
13501350
s2q!("for x <- [], reduce: %{} do\ny, z when __cursor__() -> :ok\nend")
1351+
1352+
assert cc2q!("case do\na -> a\nb = ", trailing_fragment: "c -> c\nend") ==
1353+
s2q!("case do\na -> a\nb = __cursor__() -> c\nend")
13511354
end
13521355

13531356
test "removes tokens until opening" do

0 commit comments

Comments
 (0)