diff --git a/lib/livebook/intellisense.ex b/lib/livebook/intellisense.ex index 02d5e88e7..1ee80b80d 100644 --- a/lib/livebook/intellisense.ex +++ b/lib/livebook/intellisense.ex @@ -277,7 +277,14 @@ defmodule Livebook.Intellisense do defp format_signatures([], _module), do: nil defp format_signatures(signatures, module) do - module_to_prefix(module) <> Enum.join(signatures, "\n") + signatures_string = Enum.join(signatures, "\n") + + # Don't add module prefix to operator signatures + if :binary.match(signatures_string, ["(", "/"]) != :nomatch do + module_to_prefix(module) <> signatures_string + else + signatures_string + end end defp module_to_prefix(mod) do diff --git a/lib/livebook/intellisense/completion.ex b/lib/livebook/intellisense/completion.ex index 45b2b9cb5..1e223903d 100644 --- a/lib/livebook/intellisense/completion.ex +++ b/lib/livebook/intellisense/completion.ex @@ -23,6 +23,9 @@ defmodule Livebook.Intellisense.Completion do @type signature :: String.t() @type spec :: tuple() | nil + @exact_matcher &Kernel.==/2 + @prefix_matcher &String.starts_with?/2 + @doc """ Returns a list of identifiers matching the given `hint` together with relevant information. @@ -40,7 +43,7 @@ defmodule Livebook.Intellisense.Completion do @spec get_completion_items(String.t(), Code.binding(), Macro.Env.t(), keyword()) :: list(completion_item()) def get_completion_items(hint, binding, env, opts \\ []) do - matcher = if opts[:exact], do: &Kernel.==/2, else: &String.starts_with?/2 + matcher = if opts[:exact], do: @exact_matcher, else: @prefix_matcher complete(hint, %{binding: binding, env: env, matcher: matcher}) end @@ -57,7 +60,7 @@ defmodule Livebook.Intellisense.Completion do complete_dot(path, List.to_string(hint), ctx) {:dot_arity, path, hint} -> - complete_dot(path, List.to_string(hint), ctx) + complete_dot(path, List.to_string(hint), %{ctx | matcher: @exact_matcher}) {:dot_call, _path, _hint} -> complete_default(ctx) @@ -69,11 +72,20 @@ defmodule Livebook.Intellisense.Completion do complete_local_or_var(List.to_string(local_or_var), ctx) {:local_arity, local} -> - complete_local(List.to_string(local), ctx) + complete_local(List.to_string(local), %{ctx | matcher: @exact_matcher}) {:local_call, _local} -> complete_default(ctx) + {:operator, operator} -> + complete_local_or_var(List.to_string(operator), ctx) + + {:operator_arity, operator} -> + complete_local(List.to_string(operator), %{ctx | matcher: @exact_matcher}) + + {:operator_call, _operator} -> + complete_default(ctx) + {:module_attribute, attribute} -> complete_module_attribute(List.to_string(attribute), ctx) diff --git a/test/livebook/intellisense_test.exs b/test/livebook/intellisense_test.exs index 7ddc633b4..18d211ee7 100644 --- a/test/livebook/intellisense_test.exs +++ b/test/livebook/intellisense_test.exs @@ -313,6 +313,11 @@ defmodule Livebook.IntellisenseTest do """, insert_text: "concat" } in Intellisense.get_completion_items("Enum.concat/", binding, env) + + assert [ + %{label: "count/1"}, + %{label: "count/2"} + ] = Intellisense.get_completion_items("Enum.count/", binding, env) end test "function completion same name with different arities" do @@ -410,6 +415,69 @@ defmodule Livebook.IntellisenseTest do ] = Intellisense.get_completion_items("mod.ve", binding, env) end + # TODO: Enable on Elixir 1.13 + + # test "operator completion" do + # {binding, env} = eval(do: nil) + + # assert [ + # %{ + # label: "++/2", + # kind: :function, + # detail: "left ++ right", + # documentation: """ + # List concatenation operator. Concatenates a proper list and a term, returning a list. + + # ``` + # @spec list() ++ term() :: + # maybe_improper_list() + # ```\ + # """, + # insert_text: "++" + # }, + # %{ + # label: "+/1", + # kind: :function, + # detail: "+value", + # documentation: """ + # Arithmetic positive unary operator. + + # ``` + # @spec +integer() :: integer() + # @spec +float() :: float() + # ```\ + # """, + # insert_text: "+" + # }, + # %{ + # label: "+/2", + # kind: :function, + # detail: "left + right", + # documentation: """ + # Arithmetic addition operator. + + # ``` + # @spec integer() + integer() :: + # integer() + # @spec float() + float() :: float() + # @spec integer() + float() :: float() + # @spec float() + integer() :: float() + # ```\ + # """, + # insert_text: "+" + # } + # ] = Intellisense.get_completion_items("+", binding, env) + + # assert [ + # %{label: "+/1"}, + # %{label: "+/2"} + # ] = Intellisense.get_completion_items("+/", binding, env) + + # assert [ + # %{label: "++/2"} + # ] = Intellisense.get_completion_items("++/", binding, env) + # end + test "map atom key completion" do {binding, env} = eval do @@ -692,6 +760,11 @@ defmodule Livebook.IntellisenseTest do %{label: "derive/2"}, %{label: "derive/3"} ] = Intellisense.get_completion_items("der", binding, env) + + assert [ + %{label: "count/1"}, + %{label: "count/2"} + ] = Intellisense.get_completion_items("count/", binding, env) end test "ignores quoted variables when performing variable completion" do