Skip to content

Commit 0e7a0cf

Browse files
Maelanakinomyogayedayak
authored
feat(_comp_compgen_filedir,_comp_compgen_filedir_xspec): don’t suggest . and .. (#1230)
* feat(_comp_compgen_filedir{,_xspec}): do not generate "." and ".." 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: #364 #1230 Co-authored-by: Koichi Murase <[email protected]> Co-authored-by: Yedaya Katsman <[email protected]>
1 parent 36cbfc6 commit 0e7a0cf

File tree

8 files changed

+69
-3
lines changed

8 files changed

+69
-3
lines changed

bash_completion

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,15 @@ _comp_compgen_filedir()
11421142
-f ${_plusdirs+"${_plusdirs[@]}"}
11431143
fi
11441144

1145+
if ((${#toks[@]} != 0)); then
1146+
# Remove . and .. (as well as */. and */..) from suggestions, unless
1147+
# .. or */.. was typed explicitly by the user (for users who use
1148+
# tab-completion to append a slash after '..')
1149+
if [[ $cur != ?(*/).. ]]; then
1150+
_comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"'
1151+
fi
1152+
fi
1153+
11451154
if ((${#toks[@]} != 0)); then
11461155
# 2>/dev/null for direct invocation, e.g. in the _comp_compgen_filedir
11471156
# unit test
@@ -3042,6 +3051,13 @@ _comp_compgen_filedir_xspec()
30423051
30433052
((${#toks[@]})) || return 1
30443053
3054+
# Remove . and .. (as well as */. and */..) from suggestions, unless .. or
3055+
# */.. was typed explicitly by the user (for users who use tab-completion
3056+
# to append a slash after '..')
3057+
if [[ $cur != ?(*/).. ]]; then
3058+
_comp_compgen -Rv toks -- -X '?(*/)@(.|..)' -W '"${toks[@]}"' || return 1
3059+
fi
3060+
30453061
compopt -o filenames
30463062
_comp_compgen -RU toks -- -W '"${toks[@]}"'
30473063
}

test/fixtures/_filedir/.dotfile1

Whitespace-only changes.

test/fixtures/_filedir/.dotfile2

Whitespace-only changes.

test/fixtures/_filedir/dotdot/..2016_02_01_15_04_05.123456

Whitespace-only changes.

test/fixtures/_filedir/dotdot/..data

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
..2016_02_01_15_04_05.123456

test/fixtures/_filedir/dotdot/..folder/.nothing_here

Whitespace-only changes.

test/t/unit/test_unit_compgen_filedir.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import pytest
88

9-
from conftest import assert_bash_exec, assert_complete
9+
from conftest import assert_bash_exec, assert_complete, bash_env_saved
1010

1111

1212
@pytest.mark.bashcomp(cmd=None, ignore_env=r"^\+COMPREPLY=")
@@ -59,7 +59,9 @@ def utf8_ctype(self, bash):
5959
return lc_ctype
6060

6161
def test_1(self, bash):
62-
assert_bash_exec(bash, "_comp_compgen_filedir >/dev/null")
62+
with bash_env_saved(bash) as bash_env:
63+
bash_env.write_variable("cur", "")
64+
assert_bash_exec(bash, 'cur="" _comp_compgen_filedir >/dev/null')
6365

6466
@pytest.mark.parametrize("funcname", "f f2".split())
6567
def test_2(self, bash, functions, funcname):
@@ -233,3 +235,47 @@ def test_26(self, bash, functions, funcname):
233235
def test_27(self, bash, functions, funcname, utf8_ctype):
234236
completion = assert_complete(bash, "%s aé/" % funcname, cwd="_filedir")
235237
assert completion == "g"
238+
239+
@pytest.mark.parametrize("funcname", "f f2".split())
240+
def test_28_dot_1(self, bash, functions, funcname):
241+
"""Exclude . and .. when the completion is attempted for '.[TAB]'"""
242+
completion = assert_complete(bash, r"%s ." % funcname, cwd="_filedir")
243+
assert completion == [".dotfile1", ".dotfile2"]
244+
245+
@pytest.mark.parametrize("funcname", "f f2".split())
246+
def test_28_dot_2(self, bash, functions, funcname):
247+
"""Exclude . and .. when the completion is attempted for 'dir/.[TAB]'"""
248+
completion = assert_complete(bash, r"%s _filedir/." % funcname)
249+
assert completion == [".dotfile1", ".dotfile2"]
250+
251+
@pytest.mark.parametrize("funcname", "f f2".split())
252+
def test_28_dot_3(self, bash, functions, funcname):
253+
"""Include . when the completion is attempted for '..[TAB]'"""
254+
completion = assert_complete(bash, r"%s .." % funcname, cwd="_filedir")
255+
assert completion == "/"
256+
257+
@pytest.mark.parametrize("funcname", "f f2".split())
258+
def test_28_dot_4(self, bash, functions, funcname):
259+
"""Include . when the completion is attempted for '..[TAB]'"""
260+
completion = assert_complete(bash, r"%s _filedir/.." % funcname)
261+
assert completion == "/"
262+
263+
@pytest.mark.parametrize("funcname", "f f2".split())
264+
def test_29_dotdot(self, bash, functions, funcname):
265+
"""Complete files starting with "..".
266+
267+
These types of files are used by the go kubernetes atomic writer [0],
268+
and presumably other types of systems, and we want to make sure they
269+
will be completed correctly.
270+
271+
[0] https://pkg.go.dev/k8s.io/kubernetes/pkg/volume/util#AtomicWriter.Write
272+
"""
273+
completion = assert_complete(
274+
bash, r"%s .." % funcname, cwd="_filedir/dotdot/"
275+
)
276+
assert completion == [
277+
"../",
278+
"..2016_02_01_15_04_05.123456",
279+
"..data",
280+
"..folder/",
281+
]

test/t/unit/test_unit_expand_glob.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ def functions(self, bash):
2222

2323
def test_match_all(self, bash, functions):
2424
output = assert_bash_exec(bash, "__tester '*'", want_output=True)
25-
assert output.strip() == "<a b><a$b><a&b><a'b><ab><aé><brackets><ext>"
25+
assert (
26+
output.strip()
27+
== "<a b><a$b><a&b><a'b><ab><aé><brackets><dotdot><ext>"
28+
)
2629

2730
def test_match_pattern(self, bash, functions):
2831
output = assert_bash_exec(bash, "__tester 'a*'", want_output=True)

0 commit comments

Comments
 (0)