Add completion for bitstring modifiers (#1291)

Co-authored-by: José Valim <jose.valim@gmail.com>
Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
ByeongUk Choi 2022-08-29 20:14:13 +09:00 committed by GitHub
parent 895373e08b
commit 648edf23e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 118 additions and 2 deletions

View file

@ -266,6 +266,23 @@ defmodule Livebook.Intellisense do
insert_text: Atom.to_string(name)
}
defp format_completion_item(%{kind: :bitstring_modifier, name: name, arity: arity}) do
insert_text =
if arity == 0 do
Atom.to_string(name)
else
"#{name}($0)"
end
%{
label: Atom.to_string(name),
kind: :bitstring_option,
detail: "bitstring option",
documentation: nil,
insert_text: insert_text
}
end
defp keyword_macro?(name) do
def? = name |> Atom.to_string() |> String.starts_with?("def")
@ -346,7 +363,17 @@ defmodule Livebook.Intellisense do
end
end
@ordered_kinds [:keyword, :field, :variable, :module, :struct, :interface, :function, :type]
@ordered_kinds [
:keyword,
:field,
:variable,
:module,
:struct,
:interface,
:function,
:type,
:bitstring_option
]
defp completion_item_priority(%{kind: :struct, detail: "exception"} = completion_item) do
{length(@ordered_kinds), completion_item.label}

View file

@ -65,6 +65,11 @@ defmodule Livebook.Intellisense.IdentifierMatcher do
name: name(),
documentation: Docs.documentation()
}
| %{
kind: :bitstring_modifier,
name: name(),
arity: integer()
}
@type name :: atom()
@type display_name :: String.t()
@ -72,6 +77,23 @@ defmodule Livebook.Intellisense.IdentifierMatcher do
@exact_matcher &Kernel.==/2
@prefix_matcher &String.starts_with?/2
@bitstring_modifiers [
{:big, 0},
{:binary, 0},
{:bitstring, 0},
{:integer, 0},
{:float, 0},
{:little, 0},
{:native, 0},
{:signed, 0},
{:size, 1},
{:unit, 1},
{:unsigned, 0},
{:utf8, 0},
{:utf16, 0},
{:utf32, 0}
]
@doc """
Returns a list of identifiers matching the given `hint`
together with relevant information.
@ -151,7 +173,10 @@ defmodule Livebook.Intellisense.IdentifierMatcher do
match_default(ctx)
{:local_or_var, local_or_var} ->
match_in_struct_fields_or_local_or_var(List.to_string(local_or_var), ctx)
hint = List.to_string(local_or_var)
match_container_context(ctx.fragment, hint) ||
match_in_struct_fields_or_local_or_var(hint, ctx)
{:local_arity, local} ->
match_local(List.to_string(local), %{ctx | matcher: @exact_matcher})
@ -162,6 +187,10 @@ defmodule Livebook.Intellisense.IdentifierMatcher do
:locate -> match_local(List.to_string(local), %{ctx | matcher: @exact_matcher})
end
{:operator, operator} when operator in ~w(:: -)c ->
match_container_context(ctx.fragment, "") ||
match_local_or_var(List.to_string(operator), ctx)
{:operator, operator} ->
match_local_or_var(List.to_string(operator), ctx)
@ -297,6 +326,34 @@ defmodule Livebook.Intellisense.IdentifierMatcher do
end
end
defp match_container_context(code, hint) do
case container_context(code) do
:bitstring_modifier ->
existing = code |> String.split("::") |> List.last() |> String.split("-")
for {modifier, arity} <- @bitstring_modifiers,
name = Atom.to_string(modifier),
String.starts_with?(name, hint) and name not in existing,
do: %{kind: :bitstring_modifier, name: modifier, arity: arity}
_ ->
nil
end
end
defp container_context(code) do
case Code.Fragment.container_cursor_to_quoted(code) do
{:ok, quoted} ->
case Macro.path(quoted, &match?({:__cursor__, _, []}, &1)) do
[cursor, {:"::", _, [_, cursor]}, {:<<>>, _, [_ | _]} | _] -> :bitstring_modifier
_ -> nil
end
{:error, _} ->
nil
end
end
defp expand_struct_fields(ctx) do
with {:ok, quoted} <- Code.Fragment.container_cursor_to_quoted(ctx.fragment),
{aliases, pairs} <- find_struct_fields(quoted) do

View file

@ -1158,6 +1158,38 @@ defmodule Livebook.IntellisenseTest do
}
] = Intellisense.get_completion_items("__EN", context)
end
test "Elixir bitstring modifiers" do
context = eval(do: nil)
assert [
%{
detail: "bitstring option",
documentation: nil,
insert_text: "integer",
kind: :bitstring_option,
label: "integer"
}
] = Intellisense.get_completion_items("<<a::intege", context)
assert [
%{
detail: "bitstring option",
documentation: nil,
insert_text: "size($0)",
kind: :bitstring_option,
label: "size"
}
] = Intellisense.get_completion_items("<<a::siz", context)
assert %{
detail: "bitstring option",
documentation: nil,
insert_text: "integer",
kind: :bitstring_option,
label: "integer"
} in Intellisense.get_completion_items("<<a::", context)
end
end
describe "get_details/3" do