diff --git a/bash_completion b/bash_completion index bf42e00885b..52f846a785a 100644 --- a/bash_completion +++ b/bash_completion @@ -1142,6 +1142,15 @@ _comp_compgen_filedir() -f ${_plusdirs+"${_plusdirs[@]}"} fi + if ((${#toks[@]} != 0)); then + # Remove . and .. (as well as */. and */..) from suggestions, unless + # .. or */.. was typed explicitly by the user (for users who use + # tab-completion to append a slash after '..') + if [[ $cur != ?(*/).. ]]; then + _comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"' + fi + fi + if ((${#toks[@]} != 0)); then # 2>/dev/null for direct invocation, e.g. in the _comp_compgen_filedir # unit test @@ -3042,6 +3051,13 @@ _comp_compgen_filedir_xspec() ((${#toks[@]})) || return 1 + # Remove . and .. (as well as */. and */..) from suggestions, unless .. or + # */.. was typed explicitly by the user (for users who use tab-completion + # to append a slash after '..') + if [[ $cur != ?(*/).. ]]; then + _comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"' || return 1 + fi + compopt -o filenames _comp_compgen -RU toks -- -W '"${toks[@]}"' } diff --git a/test/fixtures/_filedir/.dotfile1 b/test/fixtures/_filedir/.dotfile1 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/fixtures/_filedir/.dotfile2 b/test/fixtures/_filedir/.dotfile2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/t/unit/test_unit_compgen_filedir.py b/test/t/unit/test_unit_compgen_filedir.py index 1e3844faa48..c20af0d44a6 100644 --- a/test/t/unit/test_unit_compgen_filedir.py +++ b/test/t/unit/test_unit_compgen_filedir.py @@ -6,7 +6,7 @@ import pytest -from conftest import assert_bash_exec, assert_complete +from conftest import assert_bash_exec, assert_complete, bash_env_saved @pytest.mark.bashcomp(cmd=None, ignore_env=r"^\+COMPREPLY=") @@ -59,7 +59,9 @@ def utf8_ctype(self, bash): return lc_ctype def test_1(self, bash): - assert_bash_exec(bash, "_comp_compgen_filedir >/dev/null") + with bash_env_saved(bash) as bash_env: + bash_env.write_variable("cur", "") + assert_bash_exec(bash, 'cur="" _comp_compgen_filedir >/dev/null') @pytest.mark.parametrize("funcname", "f f2".split()) def test_2(self, bash, functions, funcname): @@ -233,3 +235,27 @@ def test_26(self, bash, functions, funcname): def test_27(self, bash, functions, funcname, utf8_ctype): completion = assert_complete(bash, "%s aƩ/" % funcname, cwd="_filedir") assert completion == "g" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_28_dot_1(self, bash, functions, funcname): + """Exclude . and .. when the completion is attempted for '.[TAB]'""" + completion = assert_complete(bash, r"%s ." % funcname, cwd="_filedir") + assert completion == [".dotfile1", ".dotfile2"] + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_28_dot_2(self, bash, functions, funcname): + """Exclude . and .. when the completion is attempted for 'dir/.[TAB]'""" + completion = assert_complete(bash, r"%s _filedir/." % funcname) + assert completion == [".dotfile1", ".dotfile2"] + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_28_dot_3(self, bash, functions, funcname): + """Include . when the completion is attempted for '..[TAB]'""" + completion = assert_complete(bash, r"%s .." % funcname, cwd="_filedir") + assert completion == "/" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_28_dot_4(self, bash, functions, funcname): + """Include . when the completion is attempted for '..[TAB]'""" + completion = assert_complete(bash, r"%s _filedir/.." % funcname) + assert completion == "/"