Skip to content

Commit 2a30a2b

Browse files
fix: completion generation inside $()
Command substitution completion was incorrectly setting the `prev` variable due to the `_comp_initialize` function reassigning it from the original completion context after processing the substitution. The fix ensures that `words` and `cword` variables are also updated to match the command substitution context, preventing the later reassignment from overriding the correct `prev` value. Unit test added to validate command substitution correctly sets `COMP_LINE`, `COMP_CWORD`, `cur`, and `prev` variables for both single commands and commands with arguments.
1 parent 810b457 commit 2a30a2b

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

bash_completion

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,27 @@ _comp_initialize()
14831483
local redir='@(?(+([0-9])|{[a-zA-Z_]*([a-zA-Z_0-9])})@(>?([>|&])|<?([>&])|<<?([-<]))|&>?(>))'
14841484
_comp_get_words -n "$exclude<>&" cur prev words cword
14851485

1486+
# If the current word is a command substitution $(, reset the completion context to be the
1487+
# command line *inside* the substitution.
1488+
if [[ $cur == '$('* ]]; then
1489+
local inner_line=${cur#\$\(}
1490+
1491+
# Replace the completion variables with ones that reflect the inner context.
1492+
COMP_LINE=$inner_line
1493+
read -ra COMP_WORDS <<< "$inner_line"
1494+
COMP_CWORD=$(( ${#COMP_WORDS[@]} - 1 ))
1495+
cur=${COMP_WORDS[COMP_CWORD]}
1496+
# Also update words and cword used by the rest of _comp_initialize
1497+
words=("${COMP_WORDS[@]}")
1498+
cword=$COMP_CWORD
1499+
# Handle case where there's only one word in the substitution
1500+
if (( COMP_CWORD > 0 )); then
1501+
prev=${COMP_WORDS[COMP_CWORD-1]}
1502+
else
1503+
prev=""
1504+
fi
1505+
fi
1506+
14861507
# Complete variable names.
14871508
_comp_compgen_variables && return 1
14881509

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
3+
from conftest import TestUnitBase
4+
5+
6+
@pytest.mark.bashcomp(
7+
cmd=None,
8+
ignore_env=r"^[+-](COMP(_(WORDS|CWORD|LINE|POINT)|REPLY)|"
9+
r"cur|prev|cword|words)=",
10+
)
11+
class TestUnitCommandSubstitution(TestUnitBase):
12+
def test_command_substitution_completion(self, bash):
13+
"""Test that command substitution completion works correctly"""
14+
# Test basic command substitution: $(echo
15+
output = self._test_unit(
16+
"_comp_initialize %s; echo $COMP_LINE,$COMP_CWORD,$cur,$prev",
17+
bash,
18+
"(echo '$(echo')",
19+
1,
20+
"echo '$(echo'",
21+
12,
22+
)
23+
assert output == "echo,0,echo,"
24+
25+
# Test command substitution with arguments: $(ls -l
26+
output = self._test_unit(
27+
"_comp_initialize %s; echo $COMP_LINE,$COMP_CWORD,$cur,$prev",
28+
bash,
29+
"(echo '$(ls -l')",
30+
1,
31+
"echo '$(ls -l'",
32+
13,
33+
)
34+
assert output == "ls -l,1,-l,ls"
35+
36+
# Test that normal completion is not affected
37+
output = self._test_unit(
38+
"_comp_initialize %s; echo $COMP_LINE,$COMP_CWORD,$cur,$prev",
39+
bash,
40+
"(echo hello)",
41+
1,
42+
"echo hello",
43+
10,
44+
)
45+
assert output == "echo hello,1,hello,echo"

0 commit comments

Comments
 (0)