Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion lib/elixir_sense/core/source.ex
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ defmodule ElixirSense.Core.Source do
def concat_module_parts([], _, _), do: :error

def bitstring_options(prefix) do
case Code.Fragment.container_cursor_to_quoted(prefix, columns: true) do
case bitstring_quoted(prefix) do
{:ok, quoted} ->
path = Macro.path(quoted, &match?({:__cursor__, _, []}, &1))

Expand Down Expand Up @@ -764,6 +764,17 @@ defmodule ElixirSense.Core.Source do
end
end

# A modifier prefix can be an Elixir reserved word (for example, `in`). In
# that case Code.Fragment fails before it has a chance to insert its cursor.
# Retrying with an identifier suffix makes the fragment parseable without
# changing the original text returned to the completion engine.
defp bitstring_quoted(prefix) do
case Code.Fragment.container_cursor_to_quoted(prefix, columns: true) do
{:error, _} -> Code.Fragment.container_cursor_to_quoted(prefix <> "_", columns: true)
result -> result
end
end

# Strip binary `-` operator wrappers introduced when typing `big-uns` etc.
# Mirrors remove_operators/2 from elixir-ls completion_engine.ex (commit 98e983d).
defp bitstring_remove_operators([{op, _, [_, previous]} = head | tail], previous)
Expand Down
13 changes: 12 additions & 1 deletion lib/elixir_sense/providers/completion/completion_engine.ex
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ defmodule ElixirSense.Providers.Completion.CompletionEngine do
end

defp container_context(code, env, metadata, cursor_position) do
case Code.Fragment.container_cursor_to_quoted(code) do
case container_cursor_to_quoted(code) do
{:ok, quoted} ->
case Macro.path(quoted, &match?({:__cursor__, _, []}, &1)) do
[cursor, {:%{}, _, pairs}, {:%, _, [struct_module_ast, _map]} | _] ->
Expand Down Expand Up @@ -1056,6 +1056,17 @@ defmodule ElixirSense.Providers.Completion.CompletionEngine do
end
end

# A modifier prefix can be an Elixir reserved word (for example, `in`). In
# that case Code.Fragment fails before it has a chance to insert its cursor.
# Retrying with an identifier suffix makes the fragment parseable without
# affecting the original completion hint.
defp container_cursor_to_quoted(code) do
case Code.Fragment.container_cursor_to_quoted(code) do
{:error, _} -> Code.Fragment.container_cursor_to_quoted(code ++ ~c"_")
result -> result
end
end

defp remove_operators([{op, _, [_, previous]} = head | tail], previous) when op in [:-],
do: remove_operators(tail, head)

Expand Down
4 changes: 4 additions & 0 deletions test/elixir_sense/core/source_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,10 @@ defmodule ElixirSense.Core.SourceTest do
assert "int" == bitstring_options(text)
end

test "single line reserved word prefix" do
assert "in" == bitstring_options("<<ident::in")
end

test "single line multiple" do
text = "<<ident::integer, some::"
assert "" == bitstring_options(text)
Expand Down
4 changes: 4 additions & 0 deletions test/elixir_sense/providers/completion/suggestions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4710,6 +4710,10 @@ defmodule ElixirSense.Providers.Completion.SuggestionTest do
end

test "bitstring options" do
assert [%{name: "integer", type: :bitstring_option}] =
ElixirSense.suggestions("<<x::in", 1, 8)
|> Enum.filter(&(&1.type == :bitstring_option))

buffer = """
defmodule ElixirSenseExample.OtherModule do
alias ElixirSenseExample.SameModule
Expand Down
Loading