Skip to content

Commit

Permalink
feat(_comp_compgen_filedir{,_xspec}): do not generate "." and ".."
Browse files Browse the repository at this point in the history
Currently, "." and ".." are generated as completions when the current
path segment starts with a dot.  However, this impedes the completion
of any dotfiles when the current path segments is "." because the word
"." already matches the generated ".".  When the user attempts the
completion after inputting ".", the user is likely to intend to
complete a dotfile name as ".dotfile" (rather than to complete just a
slash as "./" because the user could just press "/" in such a case).
In this patch, we do not generate "." and ".." unless the user has
explicitly input "..".

The behavioral changes are summarized below.  Other possibilities of
the detailed behaviors are commented in the "[ Note: ... ]" sections.
If necessary, they can be reconsidered and adjusted in later commits.

* cmd .[TAB]

  When the current directory has no dotfiles (i.e., filenames starting
  with "."), it completed a slash.  Nothing will happen after this
  patch. [ Note: as another option, we might generate "." only when no
  dotfiles are found.  However, that might be annoying when the user
  tries to probe the existence of the dotfiles by pressing TAB, where
  the user does not expect the insertion of a slash. ]

  When the current directory has dotfiles, nothing happened.  After
  this patch, this will insert the common prefix of the dotfiles.
  Note that both "." and ".." are ignored in determining the common
  prefix.

* cmd ..[TAB]

  When the current directory has no files starting with "..", this
  completes a slash to form "../".  The behavior will not be changed
  by this patch. [ Note: as another option, we might disable ".." at
  all to be consistent with the case of ".".  However, the files
  starting with ".." are unlikely, and the user is less likely to
  probe the existence of the files starting with ".." by pressing TAB
  after "..".  For this reason, we generate ".." even if it would
  prevent completion of the common prefix of the files. ]

  When the current directory has files starting with "..", nothing
  happens with this.  The behavior will not be changed by this
  patch. [ Note: as another option, we might generate ".." only when
  there are no files starting with "..", but we here assume that the
  user may want to complete a slash as ".." even when there are files
  starting with "..". ]

References:
scop#364
scop#1230

Co-authored-by: Koichi Murase <myoga.murase@gmail.com>
  • Loading branch information
Maelan and akinomyoga committed Aug 11, 2024
1 parent b44b29c commit eaa9bca
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 2 deletions.
16 changes: 16 additions & 0 deletions bash_completion
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[@]}"'
}
Expand Down
Empty file.
Empty file.
30 changes: 28 additions & 2 deletions test/t/unit/test_unit_compgen_filedir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=")
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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 == "/"

0 comments on commit eaa9bca

Please sign in to comment.