diff options
author | Ulf Wiger <[email protected]> | 2009-12-17 23:54:51 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2010-01-07 10:13:49 +0100 |
commit | 1c3a6c89c4f8d25dc0a3f71f4967ad80729cdbfe (patch) | |
tree | 9b46e2dbc05b66b97d06e472bda7c8af2511adc5 /lib/stdlib | |
parent | ebaa01050deb2bbc4cf4e1279e3d8ffe220080d9 (diff) | |
download | otp-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/stdlib')
-rw-r--r-- | lib/stdlib/src/edlin.erl | 26 | ||||
-rw-r--r-- | lib/stdlib/src/edlin_expand.erl | 39 | ||||
-rw-r--r-- | lib/stdlib/test/ExpandTestCaps.erl | 32 | ||||
-rw-r--r-- | lib/stdlib/test/ExpandTestCaps1.erl | 44 | ||||
-rw-r--r-- | lib/stdlib/test/Makefile | 5 | ||||
-rw-r--r-- | lib/stdlib/test/edlin_expand_SUITE.erl | 156 | ||||
-rw-r--r-- | lib/stdlib/test/expand_test.erl | 32 | ||||
-rw-r--r-- | lib/stdlib/test/expand_test1.erl | 44 |
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. |