1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2025-02-17 01:08:40 +01:00

[Wilds] Shell complete files with spaces in them correctly

Summary: Fixes T9116. Ref T13098. Fix shell completion handling of filenames with spaces in them. This is a big mess -- see T9116 for discussion and I'll walk through things below.

Test Plan:
These all now seem to do the right thing:

```
arc we<tab> -> arc weld (+space)
arc weld fo<tab> -> arc weld foo\ bar (+space)
arc weld 'fo<tab> -> arc weld 'foo bar' (+space)
arc weld src<tab> -> arc weld src/ (no space)
arc weld src/w<tab> -> arc weld src/work (no space)
arc weld src/work<tab><tab> -> suggests "workflow/", "workingcopy/"
arc shell-complete --gen<tab> -> arc shell-complete --generate
```

These also work, which I think is nice-to-have:

```
arc WEL<tab> -> arc weld
arc shell-complete --GEN<tab> -> arc shell-complete --generate
```

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13098, T9116

Differential Revision: https://secure.phabricator.com/D19705
This commit is contained in:
epriestley 2018-09-24 17:13:36 -07:00
parent 50dfc9cc41
commit afcaeea9c3
2 changed files with 51 additions and 37 deletions

View file

@ -506,18 +506,8 @@ EOTEXT
$complete[] = $alias->getTrigger(); $complete[] = $alias->getTrigger();
} }
// Remove invalid possibilities. For example, if the user has typed
// "skun<tab>", it obviously can't complete to "zebra". We don't really
// need to do this filtering ourselves: the shell completion will
// automatically match things for us even if we emit impossible results.
// However, it's a little easier to debug the raw output if we clean it
// up here before printing it out.
$partial = $argv[$pos]; $partial = $argv[$pos];
foreach ($complete as $key => $candidate) { $complete = $this->getMatches($complete, $partial);
if (strncmp($partial, $candidate, strlen($partial))) {
unset($complete[$key]);
}
}
if ($complete) { if ($complete) {
return $this->suggestStrings($complete); return $this->suggestStrings($complete);
@ -540,6 +530,7 @@ EOTEXT
$arguments = $workflow->getWorkflowArguments(); $arguments = $workflow->getWorkflowArguments();
$arguments = mpull($arguments, null, 'getKey'); $arguments = mpull($arguments, null, 'getKey');
$current = idx($argv, $pos, '');
$argument = null; $argument = null;
$prev = idx($argv, $pos - 1, null); $prev = idx($argv, $pos - 1, null);
@ -554,7 +545,7 @@ EOTEXT
if ($argument && strlen($argument->getParameter())) { if ($argument && strlen($argument->getParameter())) {
if ($argument->getIsPathArgument()) { if ($argument->getIsPathArgument()) {
return $this->suggestPaths(); return $this->suggestPaths($current);
} else { } else {
return $this->suggestNothing(); return $this->suggestNothing();
} }
@ -575,15 +566,7 @@ EOTEXT
$flags[] = '--'.$argument->getKey(); $flags[] = '--'.$argument->getKey();
} }
$current = idx($argv, $pos, ''); $matches = $this->getMatches($flags, $current);
$matches = array();
if (strlen($current)) {
foreach ($flags as $possible) {
if (!strncmp($possible, $current, strlen($current))) {
$matches[] = $possible;
}
}
}
// If whatever the user is completing does not match the prefix of any // If whatever the user is completing does not match the prefix of any
// flag, try to autcomplete a wildcard argument if it has some kind of // flag, try to autcomplete a wildcard argument if it has some kind of
@ -597,7 +580,7 @@ EOTEXT
// and Workflows. // and Workflows.
if ($wildcard->getIsPathArgument()) { if ($wildcard->getIsPathArgument()) {
return $this->suggestPaths(); return $this->suggestPaths($current);
} }
} }
@ -605,19 +588,51 @@ EOTEXT
} }
} }
private function suggestPaths() { private function suggestPaths($prefix) {
echo "FILE\n"; // NOTE: We are returning a directive to the bash script to run "compgen"
// for us rather than running it ourselves. If we run:
//
// compgen -A file -- %s
//
// ...from this context, it fails (exits with error code 1 and no output)
// if the prefix is "foo\ ", on my machine. See T9116 for some dicussion.
echo "<compgen:file>";
return 0; return 0;
} }
private function suggestNothing() { private function suggestNothing() {
echo "ARGUMENT\n"; return $this->suggestStrings(array());
return 0;
} }
private function suggestStrings(array $strings) { private function suggestStrings(array $strings) {
echo implode(' ', $strings)."\n"; sort($strings);
echo implode("\n", $strings);
return 0; return 0;
} }
private function getMatches(array $candidates, $prefix) {
$matches = array();
if (strlen($prefix)) {
foreach ($candidates as $possible) {
if (!strncmp($possible, $prefix, strlen($prefix))) {
$matches[] = $possible;
}
}
// If we matched nothing, try a case-insensitive match.
if (!$matches) {
foreach ($candidates as $possible) {
if (!strncasecmp($possible, $prefix, strlen($prefix))) {
$matches[] = $possible;
}
}
}
} else {
$matches = $candidates;
}
return $matches;
}
} }

View file

@ -2,22 +2,21 @@ _arcanist_complete_{{{BIN}}} ()
{ {
COMPREPLY=() COMPREPLY=()
CUR="${COMP_WORDS[COMP_CWORD]}" RESULT=$(echo | {{{BIN}}} shell-complete \
OPTS=$(echo | {{{BIN}}} shell-complete --current ${COMP_CWORD} -- ${COMP_WORDS[@]} 2>/dev/null) --current ${COMP_CWORD} \
-- \
"${COMP_WORDS[@]}" \
2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
return $? return $?
fi fi
if [ "$OPTS" = "FILE" ]; then if [ "$RESULT" == "<compgen:file>" ]; then
COMPREPLY=( $(compgen -f -- ${CUR}) ) RESULT=$( compgen -A file -- ${COMP_WORDS[COMP_CWORD]} )
return 0
fi fi
if [ "$OPTS" = "ARGUMENT" ]; then local IFS=$'\n'
return 0 COMPREPLY=( $RESULT )
fi
COMPREPLY=( $(compgen -W "${OPTS}" -- ${CUR}) )
} }
complete -F _arcanist_complete_{{{BIN}}} -o filenames {{{BIN}}} complete -F _arcanist_complete_{{{BIN}}} -o filenames {{{BIN}}}