Skip to content

Commit

Permalink
Let bash add or suppress trailing space
Browse files Browse the repository at this point in the history
Fixes #50 and #151
  • Loading branch information
evanunderscore committed Oct 29, 2016
1 parent 75bc04b commit 2c6b66e
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 35 deletions.
8 changes: 0 additions & 8 deletions argcomplete/__init__.py
Expand Up @@ -521,14 +521,6 @@ def quote_completions(self, completions, cword_prequote, first_colon_pos):
for char in special_chars:
completions = [c.replace(char, "\\" + char) for c in completions]

# Note: similar functionality in bash is turned off by supplying the "-o nospace" option to complete.
# We can't use that functionality because bash is not smart enough to recognize continuation characters (/) for
# which no space should be added.
continuation_chars = "=/:"
if len(completions) == 1 and completions[0][-1] not in continuation_chars:
if cword_prequote == "" and not completions[0].endswith(" "):
completions[0] += " "

return completions

def rl_complete(self, text, state):
Expand Down
2 changes: 2 additions & 0 deletions argcomplete/bash_completion.d/python-argcomplete.sh
Expand Up @@ -46,6 +46,8 @@ _python_argcomplete_global() {
"$executable" 8>&1 9>&2 1>/dev/null 2>&1) )
if [[ $? != 0 ]]; then
unset COMPREPLY
elif [[ "$COMPREPLY" =~ [=/:]$ ]]; then
compopt -o nospace
fi
else
type -t _completion_loader | grep -q 'function' && _completion_loader "$@"
Expand Down
2 changes: 1 addition & 1 deletion scripts/activate-global-python-argcomplete
Expand Up @@ -39,7 +39,7 @@ elif not os.path.exists(args.dest) and args.dest != '-':
activator = os.path.join(os.path.dirname(argcomplete.__file__), 'bash_completion.d', 'python-argcomplete.sh')

if args.complete_arguments is None:
complete_options = '-o nospace -o default -o bashdefault' if args.use_defaults else '-o nospace -o bashdefault'
complete_options = '-o default -o bashdefault' if args.use_defaults else '-o bashdefault'
else:
complete_options = " ".join(args.complete_arguments)
complete_call = "complete{} -D -F _python_argcomplete_global".format(" " + complete_options if complete_options else "")
Expand Down
4 changes: 3 additions & 1 deletion scripts/register-python-argcomplete
Expand Up @@ -33,6 +33,8 @@ _python_argcomplete() {
"$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
if [[ $? != 0 ]]; then
unset COMPREPLY
elif [[ "$COMPREPLY" =~ [=/:]$ ]]; then
compopt -o nospace
fi
}
complete %(complete_opts)s -F _python_argcomplete "%(executable)s"
Expand Down Expand Up @@ -67,7 +69,7 @@ if len(sys.argv) == 1:
args = parser.parse_args()

if args.complete_arguments is None:
complete_options = '-o nospace -o default' if args.use_defaults else '-o nospace'
complete_options = '-o default' if args.use_defaults else '-o nospace'
else:
complete_options = " ".join(args.complete_arguments)

Expand Down
1 change: 1 addition & 0 deletions test/prog
@@ -1,4 +1,5 @@
#!/usr/bin/env python
# PYTHON_ARGCOMPLETE_OK
"""Test script used by test.TestBash."""
import argparse
import argcomplete
Expand Down
55 changes: 30 additions & 25 deletions test/test.py
Expand Up @@ -102,10 +102,10 @@ def make_parser():

expected_outputs = (
("prog ", ["--ship", "-h", "--help"]),
("prog --shi", ["--ship "]),
("prog --shi", ["--ship"]),
("prog --ship ", ["submarine", "speedboat"]),
("prog --ship s", ["submarine", "speedboat"]),
("prog --ship su", ["submarine "])
("prog --ship su", ["submarine"])
)

for cmd, output in expected_outputs:
Expand All @@ -120,7 +120,7 @@ def make_parser():
expected_outputs = (
("prog ", ["4", "8", "15", "16", "23", "42", "-h", "--help"]),
("prog 1", ["15", "16"]),
("prog 2", ["23 "]),
("prog 2", ["23"]),
("prog 4", ["4", "42"]),
("prog 4 ", ["-h", "--help"])
)
Expand All @@ -145,7 +145,7 @@ def make_parser():

expected_outputs = (
("prog ", ["--foo", "--bar", "-h", "--help"]),
("prog --b", ["--bar "])
("prog --b", ["--bar"])
)

for cmd, output in expected_outputs:
Expand All @@ -160,9 +160,9 @@ def make_parser():

expected_outputs = (
("prog ", ["bus", "car", "-h", "--help"]),
("prog bu", ["bus "]),
("prog bu", ["bus"]),
("prog bus ", ["apple", "orange", "-h", "--help"]),
("prog bus appl", ["apple "]),
("prog bus appl", ["apple"]),
("prog bus apple ", ["-h", "--help"]),
)

Expand All @@ -181,12 +181,12 @@ def make_parser():

expected_outputs = (
("prog ", ["a", "b", "c", "-h", "--help"]),
("prog b", ["b "]),
("prog b", ["b"]),
("prog b ", ["a", "b", "c", "-h", "--help"]),
("prog c b ", ["build", "-h", "--help"]),
("prog c b bu", ["build "]),
("prog c b bu", ["build"]),
("prog c b build ", ["bus", "car", "--profile", "-h", "--help"]),
("prog c b build ca", ["car "]),
("prog c b build ca", ["car"]),
("prog c b build car ", ["--profile", "-h", "--help"]),
("prog build car ", ["-h", "--help"]),
("prog a build car ", ["-h", "--help"]),
Expand Down Expand Up @@ -239,7 +239,7 @@ def make_parser():
expected_outputs = (
("prog subcommand val1 ", ["val1", "--arg4", "--arg5", "-h", "--help"]),
("prog subcommand val1 val2 --arg5 val5 ", ["val5", "--arg4", "--arg5", "-h", "--help"]),
("prog subcommand val1 val2 --arg5 val6 --arg4 v", ["val6 "]),
("prog subcommand val1 val2 --arg5 val6 --arg4 v", ["val6"]),
)

for cmd, output in expected_outputs:
Expand Down Expand Up @@ -272,7 +272,7 @@ def make_parser():

expected_outputs = (
("prog subcommand --r ", ["abcxyz", "abcdefж/", "abcaha/"]),
("prog subcommand --w abcdefж/klm/t", ["abcdefж/klm/test "]),
("prog subcommand --w abcdefж/klm/t", ["abcdefж/klm/test"]),
)

for cmd, output in expected_outputs:
Expand Down Expand Up @@ -316,7 +316,7 @@ def make_parser():

expected_outputs = (
("prog ", ["--help", "eggs", "-h", "spam", "--age"]),
("prog --age 1 eggs", ["eggs "]),
("prog --age 1 eggs", ["eggs"]),
("prog --age 2 eggs ", ["on a train", "with a goat", "on a boat", "in the rain", "--help", "-h"]),
("prog eggs ", ["on a train", "with a goat", "on a boat", "in the rain", "--help", "-h"]),
("prog eggs \"on a", ['on a train', 'on a boat']),
Expand Down Expand Up @@ -391,9 +391,9 @@ def make_parser():
expected_outputs = (
("prog ", ["-h", "--help"], validators[0]),
("prog ", ["bus", "car", "-h", "--help"], validators[1]),
("prog bu", ["bus "], validators[1]),
("prog bu", ["bus"], validators[1]),
("prog bus ", ["apple", "orange", "-h", "--help"], validators[1]),
("prog bus appl", ["apple "], validators[2]),
("prog bus appl", ["apple"], validators[2]),
("prog bus cappl", [""], validators[2]),
("prog bus pple ", ["-h", "--help"], validators[2]),
)
Expand Down Expand Up @@ -464,10 +464,10 @@ def make_parser():
expected_outputs = (
("prog ", ["c", "d", "-h", "--help"]),
("prog c ", ["bus", "car", "-h", "--help"]),
("prog c bu", ["bus "]),
("prog c bu", ["bus"]),
("prog c bus ", ["bus", "car", "apple", "orange", "-h", "--help"]),
("prog c bus car ", ["bus", "car", "apple", "orange", "-h", "--help"]),
("prog c bus appl", ["apple "]),
("prog c bus appl", ["apple"]),
# No way to know which completers to run past this point.
("prog c bus apple ", ["bus", "car", "apple", "orange", "end", "-h", "--help"]),
("prog c bus car apple ", ["bus", "car", "apple", "orange", "end", "-h", "--help"]),
Expand Down Expand Up @@ -542,7 +542,7 @@ def make_parser():

("prog --foobar ", ["pos", "--opt"]),
("prog --foobar pos ", ["pos", "--opt"]),
("prog --foobar --", ["--opt "]),
("prog --foobar --", ["--opt"]),
("prog --foobar --opt ", ["pos", "--opt"]),
)

Expand Down Expand Up @@ -572,7 +572,7 @@ def make_parser():
def test_skipped_completer(self):
parser = ArgumentParser(add_help=False)
parser.add_argument("--foo", choices=["--bar"])
self.assertEqual(self.run_completer(parser, "prog --foo --"), ["--foo "])
self.assertEqual(self.run_completer(parser, "prog --foo --"), ["--foo"])

def test_optional_long_short_filtering(self):
def make_parser():
Expand All @@ -589,9 +589,9 @@ def make_parser():
short_opts = "-b -t -x -z -h --foo --baz --xyz".split()
expected_outputs = (
("prog ", {"long": long_opts, "short": short_opts, True: long_opts + short_opts, False: [""]}),
("prog --foo", {"long": ["--foo "], "short": ["--foo "], True: ["--foo "], False: ["--foo "]}),
("prog --foo", {"long": ["--foo"], "short": ["--foo"], True: ["--foo"], False: ["--foo"]}),
("prog --b", {"long": ["--bar", "--baz"], "short": ["--bar", "--baz"], True: ["--bar", "--baz"], False: ["--bar", "--baz"]}),
("prog -z -x", {"long": ["-x "], "short": ["-x "], True: ["-x "], False: ["-x "]}),
("prog -z -x", {"long": ["-x"], "short": ["-x"], True: ["-x"], False: ["-x"]}),
)
for cmd, outputs in expected_outputs:
for always_complete_options, output in outputs.items():
Expand Down Expand Up @@ -630,7 +630,7 @@ def make_parser():
("prog ", ["--get", "--set", "name1", "name2"]),
("prog --", ["--get", "--set"]),
("prog --get ", ["--get", "name1", "name2"]),
("prog --get name1 ", ["--get "])
("prog --get name1 ", ["--get"])
)

for cmd, output in expected_outputs:
Expand Down Expand Up @@ -724,10 +724,10 @@ def abc():

expected_outputs = (
("prog ", ["-h", "--help", "--foo", "--bar", "list", "show", "set"]),
("prog li", ["list "]),
("prog li", ["list"]),
("prog s", ["show", "set"]),
("prog show ", ["--test", "depth", "-h", "--help"]),
("prog show d", ["depth "]),
("prog show d", ["depth"]),
("prog show depth ", ["-h", "--help"]),
)

Expand Down Expand Up @@ -774,12 +774,14 @@ def test_escaped_special_in_double_quotes(self):


class TestBash(unittest.TestCase):
install_cmd = 'eval "$(register-python-argcomplete prog)"'

def setUp(self):
bash = pexpect.replwrap.bash()
path = ':'.join(['$PATH', os.path.join(BASE_DIR, 'scripts'), TEST_DIR])
bash.run_command('export PATH=' + path)
bash.run_command('export PYTHONPATH=' + BASE_DIR)
bash.run_command('eval "$(register-python-argcomplete prog)"')
bash.run_command(self.install_cmd)
self.bash = bash

def tearDown(self):
Expand Down Expand Up @@ -812,7 +814,6 @@ def test_continuation(self):
self.assertEqual(self.bash.run_command('prog cont bar\t--'), 'bar/--\r\n')
self.assertEqual(self.bash.run_command('prog cont baz\t--'), 'baz:--\r\n')

@unittest.expectedFailure
def test_quoted_exact(self):
self.assertEqual(self.bash.run_command('prog basic "f\t--'), 'foo\r\n')

Expand Down Expand Up @@ -859,5 +860,9 @@ def test_single_quotes_in_single_quotes(self):
self.assertEqual(self.bash.run_command("prog quote '1\t"), "1'1\r\n")


class TestBashGlobal(TestBash):
install_cmd = 'eval "$(activate-global-python-argcomplete --dest=-)"'


if __name__ == "__main__":
unittest.main()

0 comments on commit 2c6b66e

Please sign in to comment.