Skip to content

Commit 7d433e8

Browse files
author
José Valim
committed
Improve IEx autocomplete implementation
Signed-off-by: José Valim <[email protected]>
1 parent f9cb812 commit 7d433e8

File tree

2 files changed

+23
-46
lines changed

2 files changed

+23
-46
lines changed

lib/iex/lib/iex/autocomplete.ex

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ defmodule IEx.Autocomplete do
1010
h === ?. and t != []->
1111
expand_dot(reduce(t))
1212
h === ?: ->
13-
expand_erlang_modules
13+
expand_erlang_modules()
1414
identifier?(h) ->
1515
expand_expr(reduce(expr))
1616
(h == ?/) and t != [] and identifier?(hd(t)) ->
1717
expand_expr(reduce(t))
18-
h in '(+[' ->
18+
h in '([{' ->
1919
expand('')
2020
true ->
2121
no()
@@ -40,32 +40,26 @@ defmodule IEx.Autocomplete do
4040
defp expand_expr(expr) do
4141
case Code.string_to_quoted expr do
4242
{:ok, atom} when is_atom(atom) ->
43-
expand_erlang_modules Atom.to_string(atom)
43+
expand_erlang_modules(Atom.to_string(atom))
4444
{:ok, {atom, _, nil}} when is_atom(atom) ->
45-
expand_import Atom.to_string(atom)
45+
expand_import(Atom.to_string(atom))
4646
{:ok, {:__aliases__, _, [root]}} ->
47-
expand_elixir_modules [], Atom.to_string(root)
47+
expand_elixir_modules([], Atom.to_string(root))
4848
{:ok, {:__aliases__, _, [h|_] = list}} when is_atom(h) ->
4949
hint = Atom.to_string(List.last(list))
5050
list = Enum.take(list, length(list) - 1)
51-
expand_elixir_modules list, hint
51+
expand_elixir_modules(list, hint)
5252
{:ok, {{:., _, [mod, fun]}, _, []}} when is_atom(fun) ->
53-
expand_call mod, Atom.to_string(fun)
53+
expand_call(mod, Atom.to_string(fun))
5454
_ ->
5555
no()
5656
end
5757
end
5858

5959
defp reduce(expr) do
60-
last_token(Enum.reverse(expr), [' ', '(', '[', '+', '-'])
61-
end
62-
63-
defp last_token(s, []) do
64-
s
65-
end
66-
67-
defp last_token(s, [h|t]) do
68-
last_token(List.last(:string.tokens(s, h)), t)
60+
Enum.reverse Enum.reduce [' ', '(', '[', '{'], expr, fn token, acc ->
61+
hd(:string.tokens(acc, token))
62+
end
6963
end
7064

7165
defp yes(hint, entries) do
@@ -100,35 +94,16 @@ defmodule IEx.Autocomplete do
10094
end
10195
end
10296

103-
## Root Modules
104-
105-
defp root_modules do
106-
Enum.reduce :code.all_loaded, [], fn {m, _}, acc ->
107-
mod = Atom.to_string(m)
108-
case mod do
109-
"Elixir" <> _ ->
110-
tokens = String.split(mod, ".")
111-
if length(tokens) == 2 do
112-
[%{kind: :module, name: List.last(tokens), type: :elixir}|acc]
113-
else
114-
acc
115-
end
116-
_ ->
117-
[%{kind: :module, name: mod, type: :erlang}|acc]
118-
end
119-
end
120-
end
121-
12297
## Expand calls
12398

12499
# :atom.fun
125100
defp expand_call(mod, hint) when is_atom(mod) do
126-
expand_require mod, hint
101+
expand_require(mod, hint)
127102
end
128103

129104
# Elixir.fun
130105
defp expand_call({:__aliases__, _, list}, hint) do
131-
expand_require Module.concat(list), hint
106+
expand_require(Module.concat(list), hint)
132107
end
133108

134109
defp expand_call(_, _) do
@@ -152,22 +127,23 @@ defmodule IEx.Autocomplete do
152127
format_expansion match_erlang_modules(hint), hint
153128
end
154129

155-
defp match_erlang_modules("") do
156-
Enum.filter root_modules, fn m -> m.type === :erlang end
157-
end
158-
159130
defp match_erlang_modules(hint) do
160-
Enum.filter root_modules, fn m -> String.starts_with?(m.name, hint) end
131+
for {mod, _} <- :code.all_loaded,
132+
mod = Atom.to_string(mod),
133+
not match?("Elixir." <> _, mod),
134+
String.starts_with?(mod, hint) do
135+
%{kind: :module, name: mod, type: :erlang}
136+
end
161137
end
162138

163139
## Elixir modules
164140

165141
defp expand_elixir_modules(list, hint) do
166142
mod = Module.concat(list)
167-
format_expansion elixir_submodules(mod, hint, list == []) ++ module_funs(mod, hint), hint
143+
format_expansion elixir_aliases(mod, hint, list == []) ++ module_funs(mod, hint), hint
168144
end
169145

170-
defp elixir_submodules(mod, hint, root) do
146+
defp elixir_aliases(mod, hint, root) do
171147
modname = Atom.to_string(mod)
172148
depth = length(String.split(modname, ".")) + 1
173149
base = modname <> "." <> hint
@@ -225,7 +201,7 @@ defmodule IEx.Autocomplete do
225201

226202
for {fun, arities} <- list,
227203
name = Atom.to_string(fun),
228-
hint == "" or String.starts_with?(name, hint) do
204+
String.starts_with?(name, hint) do
229205
%{kind: :function, name: name, arities: arities}
230206
end
231207
_ ->

lib/iex/test/iex/autocomplete_test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ defmodule IEx.AutocompleteTest do
101101
end
102102

103103
test :completion_inside_expression do
104-
assert expand('1+En') == {:yes, 'um', []}
104+
assert expand('1 En') == {:yes, 'um', []}
105105
assert expand('Test(En') == {:yes, 'um', []}
106106
assert expand('Test :z') == {:yes, 'lib.', []}
107107
assert expand('[:z') == {:yes, 'lib.', []}
108+
assert expand('{:z') == {:yes, 'lib.', []}
108109
end
109110
end

0 commit comments

Comments
 (0)