aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorUlf Wiger <[email protected]>2009-12-17 23:54:51 +0100
committerBjörn Gustavsson <[email protected]>2010-01-07 10:13:49 +0100
commit1c3a6c89c4f8d25dc0a3f71f4967ad80729cdbfe (patch)
tree9b46e2dbc05b66b97d06e472bda7c8af2511adc5 /lib
parentebaa01050deb2bbc4cf4e1279e3d8ffe220080d9 (diff)
downloadotp-1c3a6c89c4f8d25dc0a3f71f4967ad80729cdbfe.tar.gz
otp-1c3a6c89c4f8d25dc0a3f71f4967ad80729cdbfe.tar.bz2
otp-1c3a6c89c4f8d25dc0a3f71f4967ad80729cdbfe.zip
Shell tab completion now works for quoted module and function names
While quoted atoms in module and function names are not common, they are allowed, and sometimes quite useful. In OTP, they are commonplace in ORBER, and can also be found in XMERL. Tab completion needs to recognize quoted atoms and act accordingly. This patch includes changes in edlin:over_word/1. It should be noted that these changes also affect the 'kill word', 'forward word' and 'backward word' commands in the line editor. The author thinks that the changes are for the better. There are also minor changes in edlin_expand.erl - mainly in regard to the conversion between atoms and strings. Another change is that the list of matches is now sorted, partly to simplify testing, but also because it seems sensible to present the matches that way. A test suite, edlin_expand_SUITE, has been added to the stdlib test suites. (amended 2009-12-18 to actually include the modified files too and again to rename the capitalized test modules for portability.)
Diffstat (limited to 'lib')
-rw-r--r--lib/stdlib/src/edlin.erl26
-rw-r--r--lib/stdlib/src/edlin_expand.erl39
-rw-r--r--lib/stdlib/test/ExpandTestCaps.erl32
-rw-r--r--lib/stdlib/test/ExpandTestCaps1.erl44
-rw-r--r--lib/stdlib/test/Makefile5
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl156
-rw-r--r--lib/stdlib/test/expand_test.erl32
-rw-r--r--lib/stdlib/test/expand_test1.erl44
8 files changed, 363 insertions, 15 deletions
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index 31a653bda0..d5ab294fb8 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -281,12 +281,32 @@ do_op(_, Bef, Aft, Rs) ->
%% Step over word/non-word characters pushing the stepped over ones on
%% the stack.
-over_word([C|Cs], Stack, N) ->
+
+over_word(Cs, Stack, N) ->
+ L = length([1 || $\' <- Cs]),
+ case L rem 2 of
+ 0 ->
+ over_word1(Cs, Stack, N);
+ 1 ->
+ until_quote(Cs, Stack, N)
+ end.
+
+until_quote([$\'|Cs], Stack, N) ->
+ {Cs, [$\'|Stack], N+1};
+until_quote([C|Cs], Stack, N) ->
+ until_quote(Cs, [C|Stack], N+1).
+
+over_word1([$\'=C|Cs], Stack, N) ->
+ until_quote(Cs, [C|Stack], N+1);
+over_word1(Cs, Stack, N) ->
+ over_word2(Cs, Stack, N).
+
+over_word2([C|Cs], Stack, N) ->
case word_char(C) of
- true -> over_word(Cs, [C|Stack], N+1);
+ true -> over_word2(Cs, [C|Stack], N+1);
false -> {[C|Cs],Stack,N}
end;
-over_word([], Stack, N) when is_integer(N) ->
+over_word2([], Stack, N) when is_integer(N) ->
{[],Stack,N}.
over_non_word([C|Cs], Stack, N) ->
diff --git a/lib/stdlib/src/edlin_expand.erl b/lib/stdlib/src/edlin_expand.erl
index 7ed76a6b09..a38e118e68 100644
--- a/lib/stdlib/src/edlin_expand.erl
+++ b/lib/stdlib/src/edlin_expand.erl
@@ -46,23 +46,38 @@ expand_module_name(Prefix) ->
match(Prefix, code:all_loaded(), ":").
expand_function_name(ModStr, FuncPrefix) ->
- Mod = list_to_atom(ModStr),
- case erlang:module_loaded(Mod) of
- true ->
- L = Mod:module_info(),
- case lists:keyfind(exports, 1, L) of
- {_, Exports} ->
- match(FuncPrefix, Exports, "(");
- _ ->
- {no, [], []}
- end;
- false ->
+ case to_atom(ModStr) of
+ {ok, Mod} ->
+ case erlang:module_loaded(Mod) of
+ true ->
+ L = Mod:module_info(),
+ case lists:keyfind(exports, 1, L) of
+ {_, Exports} ->
+ match(FuncPrefix, Exports, "(");
+ _ ->
+ {no, [], []}
+ end;
+ false ->
+ {no, [], []}
+ end;
+ error ->
{no, [], []}
end.
+%% if it's a quoted atom, atom_to_list/1 will do the wrong thing.
+to_atom(Str) ->
+ case erl_scan:string(Str) of
+ {ok, [{atom,_,A}], _} ->
+ {ok, A};
+ _ ->
+ error
+ end.
+
match(Prefix, Alts, Extra) ->
Len = length(Prefix),
- Matches = [{S, A} || {H, A} <- Alts, prefix(Prefix, S=atom_to_list(H))],
+ Matches = lists:sort(
+ [{S, A} || {H, A} <- Alts,
+ prefix(Prefix, S=hd(io_lib:fwrite("~w",[H])))]),
case longest_common_head([N || {N, _} <- Matches]) of
{partial, []} ->
{no, [], Matches}; % format_matches(Matches)};
diff --git a/lib/stdlib/test/ExpandTestCaps.erl b/lib/stdlib/test/ExpandTestCaps.erl
new file mode 100644
index 0000000000..15da7dbeff
--- /dev/null
+++ b/lib/stdlib/test/ExpandTestCaps.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module('ExpandTestCaps').
+
+-export([a_fun_name/1,
+ a_less_fun_name/1,
+ b_comes_after_a/1]).
+
+a_fun_name(X) ->
+ X.
+
+a_less_fun_name(X) ->
+ X.
+
+b_comes_after_a(X) ->
+ X.
diff --git a/lib/stdlib/test/ExpandTestCaps1.erl b/lib/stdlib/test/ExpandTestCaps1.erl
new file mode 100644
index 0000000000..b934f4b9cc
--- /dev/null
+++ b/lib/stdlib/test/ExpandTestCaps1.erl
@@ -0,0 +1,44 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module('ExpandTestCaps1').
+
+-export([a_fun_name/1,
+ a_less_fun_name/1,
+ b_comes_after_a/1,
+ 'Quoted_fun_name'/0,
+ 'Quoted_fun_too'/0,
+ '#weird-fun-name'/0]).
+
+a_fun_name(X) ->
+ X.
+
+a_less_fun_name(X) ->
+ X.
+
+b_comes_after_a(X) ->
+ X.
+
+'Quoted_fun_name'() ->
+ whoopee.
+
+'Quoted_fun_too'() ->
+ too.
+
+'#weird-fun-name'() ->
+ weird.
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 7a87eef5f3..ac8cbba375 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -18,6 +18,7 @@ MODULES= \
digraph_utils_SUITE \
dummy1_h \
dummy_h \
+ edlin_expand_SUITE \
epp_SUITE \
erl_eval_helper \
erl_eval_SUITE \
@@ -29,6 +30,10 @@ MODULES= \
escript_SUITE \
ets_SUITE \
ets_tough_SUITE \
+ expand_test \
+ expand_test1 \
+ ExpandTestCaps \
+ ExpandTestCaps1 \
filelib_SUITE \
file_sorter_SUITE \
filename_SUITE \
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
new file mode 100644
index 0000000000..d757230016
--- /dev/null
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(edlin_expand_SUITE).
+-export([all/1]).
+
+-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]).
+
+-export([init_per_testcase/2, fin_per_testcase/2]).
+
+-include("test_server.hrl").
+
+% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+fin_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+all(doc) ->
+ ["Test cases for edlin_expand."];
+all(suite) ->
+ [normal, quoted_fun, quoted_module, quoted_both].
+
+normal(doc) ->
+ [""];
+normal(suite) ->
+ [];
+normal(Config) when is_list(Config) ->
+ ?line {module,expand_test} = c:l(expand_test),
+ % These tests might fail if another module with the prefix "expand_" happens
+ % to also be loaded.
+ ?line {yes, "test:", []} = edlin_expand:expand(lists:reverse("expand_")),
+ ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")),
+ ?line {no,[],
+ [{"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = edlin_expand:expand(lists:reverse("expand_test:")),
+ ?line {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = edlin_expand:expand(
+ lists:reverse("expand_test:a_")),
+ ok.
+
+quoted_fun(doc) ->
+ ["Normal module name, some function names using quoted atoms"];
+quoted_fun(suite) ->
+ [];
+quoted_fun(Config) when is_list(Config) ->
+ ?line {module,expand_test} = c:l(expand_test),
+ ?line {module,expand_test1} = c:l(expand_test1),
+ %% should be no colon after test this time
+ ?line {yes, "test", []} = edlin_expand:expand(lists:reverse("expand_")),
+ ?line {no, [], []} = edlin_expand:expand(lists:reverse("expandXX_")),
+ ?line {no,[],[{"'#weird-fun-name'",0},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0},
+ {"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = edlin_expand:expand(
+ lists:reverse("expand_test1:")),
+ ?line {yes,"_",[]} = edlin_expand:expand(
+ lists:reverse("expand_test1:a")),
+ ?line {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = edlin_expand:expand(
+ lists:reverse("expand_test1:a_")),
+ ?line {yes,[],
+ [{"'#weird-fun-name'",0},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
+ lists:reverse("expand_test1:'")),
+ ?line {yes,"uoted_fun_",[]} = edlin_expand:expand(
+ lists:reverse("expand_test1:'Q")),
+ ?line {yes,[],
+ [{"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
+ lists:reverse("expand_test1:'Quoted_fun_")),
+ ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand(
+ lists:reverse("expand_test1:'#")),
+ ok.
+
+quoted_module(doc) ->
+ [""];
+quoted_module(suite) ->
+ [];
+quoted_module(Config) when is_list(Config) ->
+ ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ ?line {yes, "Caps':", []} = edlin_expand:expand(lists:reverse("'ExpandTest")),
+ ?line {no,[],
+ [{"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = edlin_expand:expand(lists:reverse("'ExpandTestCaps':")),
+ ?line {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps':a_")),
+ ok.
+
+quoted_both(suite) ->
+ [];
+quoted_both(Config) when is_list(Config) ->
+ ?line {module,'ExpandTestCaps'} = c:l('ExpandTestCaps'),
+ ?line {module,'ExpandTestCaps1'} = c:l('ExpandTestCaps1'),
+ %% should be no colon (or quote) after test this time
+ ?line {yes, "Caps", []} = edlin_expand:expand(lists:reverse("'ExpandTest")),
+ ?line {no,[],[{"'#weird-fun-name'",0},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0},
+ {"a_fun_name",1},
+ {"a_less_fun_name",1},
+ {"b_comes_after_a",1},
+ {"module_info",0},
+ {"module_info",1}]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':")),
+ ?line {yes,"_",[]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':a")),
+ ?line {yes,[],[{"a_fun_name",1},
+ {"a_less_fun_name",1}]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':a_")),
+ ?line {yes,[],
+ [{"'#weird-fun-name'",0},
+ {"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':'")),
+ ?line {yes,"uoted_fun_",[]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':'Q")),
+ ?line {yes,[],
+ [{"'Quoted_fun_name'",0},
+ {"'Quoted_fun_too'",0}]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':'Quoted_fun_")),
+ ?line {yes,"weird-fun-name'(",[]} = edlin_expand:expand(
+ lists:reverse("'ExpandTestCaps1':'#")),
+ ok.
diff --git a/lib/stdlib/test/expand_test.erl b/lib/stdlib/test/expand_test.erl
new file mode 100644
index 0000000000..4a0e0b4784
--- /dev/null
+++ b/lib/stdlib/test/expand_test.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(expand_test).
+
+-export([a_fun_name/1,
+ a_less_fun_name/1,
+ b_comes_after_a/1]).
+
+a_fun_name(X) ->
+ X.
+
+a_less_fun_name(X) ->
+ X.
+
+b_comes_after_a(X) ->
+ X.
diff --git a/lib/stdlib/test/expand_test1.erl b/lib/stdlib/test/expand_test1.erl
new file mode 100644
index 0000000000..8e9344fa31
--- /dev/null
+++ b/lib/stdlib/test/expand_test1.erl
@@ -0,0 +1,44 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(expand_test1).
+
+-export([a_fun_name/1,
+ a_less_fun_name/1,
+ b_comes_after_a/1,
+ 'Quoted_fun_name'/0,
+ 'Quoted_fun_too'/0,
+ '#weird-fun-name'/0]).
+
+a_fun_name(X) ->
+ X.
+
+a_less_fun_name(X) ->
+ X.
+
+b_comes_after_a(X) ->
+ X.
+
+'Quoted_fun_name'() ->
+ whoopee.
+
+'Quoted_fun_too'() ->
+ too.
+
+'#weird-fun-name'() ->
+ weird.