1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
5%%
6%% The contents of this file are subject to the Erlang Public License,
7%% Version 1.1, (the "License"); you may not use this file except in
8%% compliance with the License. You should have received a copy of the
9%% Erlang Public License along with this software. If not, it can be
10%% retrieved online at http://www.erlang.org/.
11%%
12%% Software distributed under the License is distributed on an "AS IS"
13%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14%% the License for the specific language governing rights and limitations
15%% under the License.
16%%
17%% %CopyrightEnd%
18%%
19
20-module(lfe_edlin_expand).
21
22%% A default LFE expand function for edlin, expanding modules and
23%% functions. It knows about LFE symbol syntax but as yet only works
24%% for (mod:fun ...) and not (: mod fun ...)
25
26-export([expand/1, format_matches/1]).
27
28-import(lists, [reverse/1, nthtail/2, prefix/2]).
29
30%% expand(CurrentBefore) ->
31%%     {yes, Expansion, Matches} | {no, Expansion, Matches}
32%%  Try to expand the word before as either a module name or a
33%%  function name. CurrentBefore is reversed and over_word/3 reverses
34%%  the characters it finds. In certain cases possible expansions are
35%%  printed.
36
37expand(Bef0) ->
38    {Bef1,S1,_} = over_symbol(Bef0, [], 0),
39    case Bef1 of
40        [$:|Bef2] ->                            %After a ':'
41            {Bef3,S2,_} = over_symbol(Bef2, [], 0),
42            need_lparen(Bef3, fun () -> expand_function_name(S2, S1) end);
43        Bef2 ->
44            need_lparen(Bef2, fun () -> expand_module_name(S1) end)
45    end.
46
47need_lparen(Bef, Do) ->
48    case over_white(Bef, [], 0) of
49        {[$(|_],_,_} -> Do();
50        {_,_,_} -> {no,[],[]}
51    end.
52
53%% expand(Bef0) ->
54%%     {Bef1,Word,_} = edlin:over_word(Bef0, [], 0),
55%%     case over_white(Bef1, [], 0) of
56%%         {[$:|Bef2],_White,_Nwh} ->
57%%             {Bef3,_White1,_Nwh1} = over_white(Bef2, [], 0),
58%%             {_,Mod,_Nm} = edlin:over_word(Bef3, [], 0),
59%%             expand_function_name(Mod, Word);
60%%         {_,_,_} ->
61%%             expand_module_name(Word)
62%%     end.
63
64expand_module_name(Prefix) ->
65    match(Prefix, code:all_loaded(), ":").
66
67expand_function_name(ModStr, FuncPrefix) ->
68    case to_symbol(ModStr) of
69        {ok,Mod} ->
70            case erlang:module_loaded(Mod) of
71                true ->
72                    L = Mod:module_info(),
73                    case lists:keyfind(exports, 1, L) of
74                        {_, Exports} ->
75                            match(FuncPrefix, Exports, " ");
76                        _ ->
77                            {no,[],[]}
78                    end;
79                false ->
80                    {no,[],[]}
81            end;
82        error ->
83            {no,[],[]}
84    end.
85
86%% If it's a quoted symbol, atom_to_list/1 will do the wrong thing.
87to_symbol(Str) ->
88    case lfe_scan:string(Str) of
89        {ok,[{symbol,_,A}],_} -> {ok,A};
90        _ -> error
91    end.
92
93match(Prefix, Alts, Extra0) ->
94    Len = length(Prefix),
95    Matches = lists:sort([{S, A} || {H, A} <- Alts,
96                                    begin
97                                        S = hd(lfe_io:fwrite1("~w", [H])),
98                                        prefix(Prefix, S)
99                                    end]),
100    case longest_common_head([N || {N, _} <- Matches]) of
101        {partial, []} ->
102            {no, [], Matches};                  %format_matches(Matches)};
103        {partial, Str} ->
104            case nthtail(Len, Str) of
105                [] -> {yes,[],Matches};         %format_matches(Matches)};
106                Remain -> {yes,Remain,[]}
107            end;
108        {complete, Str} ->
109            Extra = case {Extra0,Matches} of
110                        {" ",[{Str,0}]} -> ")";
111                        {_,_} -> Extra0
112                    end,
113            {yes, nthtail(Len, Str) ++ Extra, []};
114        no ->
115            {no,[],[]}
116    end.
117
118%% Return the list of names L in multiple columns.
119format_matches(L) ->
120    S = format_col(lists:sort(L), []),
121    ["\n" | S].
122
123format_col([], _) -> [];
124format_col(L, Acc) -> format_col(L, field_width(L), 0, Acc).
125
126format_col(X, Width, Len, Acc) when Width + Len > 79 ->
127    format_col(X, Width, 0, ["\n" | Acc]);
128format_col([A|T], Width, Len, Acc0) ->
129    H = case A of
130            %% If it's a tuple {string(), integer()}, we assume it's an
131            %% arity, and meant to be printed.
132            {H0, I} when is_integer(I) ->
133                H0 ++ "/" ++ integer_to_list(I);
134            {H1, _} -> H1;
135            H2 -> H2
136        end,
137    Acc = [io_lib:format("~-*s", [Width,H]) | Acc0],
138    format_col(T, Width, Len+Width, Acc);
139format_col([], _, _, Acc) ->
140    lists:reverse(Acc, "\n").
141
142field_width(L) -> field_width(L, 0).
143
144field_width([{H,_}|T], W) ->
145    case length(H) of
146        L when L > W -> field_width(T, L);
147        _ -> field_width(T, W)
148    end;
149field_width([H|T], W) ->
150    case length(H) of
151        L when L > W -> field_width(T, L);
152        _ -> field_width(T, W)
153    end;
154field_width([], W) when W < 40 ->
155    W + 4;
156field_width([], _) ->
157    40.
158
159longest_common_head([]) ->
160    no;
161longest_common_head(LL) ->
162    longest_common_head(LL, []).
163
164longest_common_head([[]|_], L) ->
165    {partial, reverse(L)};
166longest_common_head(LL, L) ->
167    case same_head(LL) of
168        true ->
169            [[H|_]|_] = LL,
170            LL1 = all_tails(LL),
171            case all_nil(LL1) of
172                true ->
173                    {complete, reverse([H|L])};
174                false ->
175                    longest_common_head(LL1, [H|L])
176            end;
177        false ->
178            {partial, reverse(L)}
179    end.
180
181same_head([[H|_]|T1]) -> same_head(H, T1).
182
183same_head(H, [[H|_]|T]) -> same_head(H, T);
184same_head(_, [])        -> true;
185same_head(_, _)         -> false.
186
187all_tails(LL) -> all_tails(LL, []).
188
189all_tails([[_|T]|T1], L) -> all_tails(T1, [T|L]);
190all_tails([], L)         -> L.
191
192all_nil([]) -> true;
193all_nil([[] | Rest]) -> all_nil(Rest);
194all_nil(_) -> false.
195
196%% over_symbol(Chars, InitialStack, InitialCount) ->
197%%      {RemainingChars,CharStack,Count}
198%% over_non_symbol(Chars, InitialStack, InitialCount) ->
199%%      {RemainingChars,CharStack,Count}
200%%  Step over symbol/non-symbol characters pushing the stepped over
201%%  ones on the stack.
202
203over_symbol(Cs, Stack, N) ->
204    L = length([1 || $| <- Cs]),
205    case L rem 2 of
206        0 -> over_symbol1(Cs, Stack, N);
207        1 -> until_quote(Cs, Stack, N)
208    end.
209
210until_quote([$||Cs], Stack, N) ->
211    {Cs, [$||Stack], N+1};
212until_quote([C|Cs], Stack, N) ->
213    until_quote(Cs, [C|Stack], N+1).
214
215over_symbol1([$||Cs], Stack, N) ->
216    until_quote(Cs, [$||Stack], N+1);
217over_symbol1(Cs, Stack, N) ->
218    over_symbol2(Cs, Stack, N).
219
220over_symbol2([C|Cs], Stack, N) ->
221    case symbol_char(C) of
222        true -> over_symbol2(Cs, [C|Stack], N+1);
223        false -> {[C|Cs],Stack,N}
224    end;
225over_symbol2([], Stack, N) when is_integer(N) ->
226    {[],Stack,N}.
227
228%% over_non_symbol([C|Cs], Stack, N) ->
229%%     case symbol_char(C) of
230%%         true -> {[C|Cs],Stack,N};
231%%         false -> over_non_symbol(Cs, [C|Stack], N+1)
232%%     end;
233%% over_non_symbol([], Stack, N) ->
234%%     {[],Stack,N}.
235
236symbol_char($:) -> false;                       %We want to separate on this
237symbol_char(C) -> lfe_scan:symbol_char(C).
238
239%% over_white(Chars, InitialStack, InitialCount) ->
240%%    {RemainingChars,CharStack,Count}.
241
242over_white([$\s|Cs], Stack, N) ->
243    over_white(Cs, [$\s|Stack], N+1);
244over_white([$\t|Cs], Stack, N) ->
245    over_white(Cs, [$\t|Stack], N+1);
246over_white(Cs, Stack, N) when is_list(Cs) ->
247    {Cs,Stack,N}.
248