summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOwen Barton2013-03-24 08:44:41 (GMT)
committer Owen Barton2013-03-24 08:55:08 (GMT)
commit10c4771cbf5529ced4aacbcac77c6999badef306 (patch)
tree61e9ced65b637cd9e956828559ffad4cf1770292
parent93838330106fd7fea9fe9051a327b8cc37aa7e46 (diff)
Various fixes to the file/dir completion UX
-rw-r--r--includes/complete.inc89
1 files changed, 60 insertions, 29 deletions
diff --git a/includes/complete.inc b/includes/complete.inc
index 85b5cf4..4799793 100644
--- a/includes/complete.inc
+++ b/includes/complete.inc
@@ -290,44 +290,44 @@ function drush_complete_match($last_word, $values) {
*/
function drush_complete_match_file($last_word, $files) {
$return = array();
- $firstchar = '';
- $full_paths = TRUE;
- if (isset($last_word) && $last_word[0] == '~') {
+ if ($last_word[0] == '~') {
// Complete does not do tilde expansion, so we do it here.
- $parts = explode('/', $last_word);
// We shell out (unquoted) to expand the tilde.
- drush_shell_exec('echo ' . $parts[0]);
- $output = drush_shell_exec_output();
- $parts[0] = $output[0];
- $last_word = implode('/', $parts);
+ drush_shell_exec('echo ' . $last_word);
+ return drush_shell_exec_output();
}
+
+ $dir = '';
+ if (substr($last_word, -1) == '/' && is_dir($last_word)) {
+ // If we exactly match a trailing directory, then we use that as the base
+ // for the listing. We only do this if a trailing slash is present, since at
+ // this stage it is still possible there are other directories that start
+ // with this string.
+ $dir = $last_word;
+ }
+ else {
+ // Otherwise we discard the last part of the path (this is matched against
+ // the list later), and use that as our base.
+ $dir = dirname($last_word);
+ if (empty($dir) || $dir == '.' && $last_word != '.' && substr($last_word, 0, 2) != './') {
+ // We are looking at the current working directory, so unless the user is
+ // actually specifying a leading dot we leave the path empty.
+ $dir = '';
+ }
+ else {
+ // In all other cases we need to add a trailing slash.
+ $dir .= '/';
+ }
+ }
+
foreach ($files as $spec) {
// We always include GLOB_MARK, as an easy way to detect directories.
$flags = GLOB_MARK;
if (isset($spec['flags'])) {
$flags = $spec['flags'] | GLOB_MARK;
}
- $listing = glob($last_word . $spec['pattern'], $flags);
- foreach ($listing as $item) {
- // Detect if the initial characters of the file/dirs to be listing differ.
- // If they do, we return a list of just their names. If they all have the
- // same first character we return full paths, to prevent the shell
- // replacing the current path with just the matching character(s).
- $char = $item[strrpos($last_word, '/') + 1];
- if (empty($firstchar)) {
- $firstchar = $char;
- }
- else if ($firstchar !== $char) {
- $full_paths = FALSE;
- }
- $return[] = $item;
- }
- }
- // If we don't need to return full paths, shorten them appropriately.
- if ($full_paths == FALSE) {
- foreach ($return as $id => $item) {
- $return[$id] = substr($return[$id], strrpos($last_word, '/') + 1);
- }
+ $listing = glob($dir . $spec['pattern'], $flags);
+ $return = array_merge($return, drush_complete_match($last_word, $listing));
}
// If we are returning a single item (which will become part of the final
// command), we need to use the full path, and we need to escape it
@@ -335,6 +335,7 @@ function drush_complete_match_file($last_word, $files) {
if (count($return) == 1) {
// Escape common shell metacharacters (we don't use escapeshellarg as it
// single quotes everything, even when unnecessary).
+ $item = array_pop($return);
$item = preg_replace('/[ |&;()<>]/', "\\\\$0", $item);
if (substr($item, -1) !== '/') {
// Insert a space after files, since the argument is complete.
@@ -342,6 +343,36 @@ function drush_complete_match_file($last_word, $files) {
}
$return = array($item);
}
+ else {
+ $firstchar = TRUE;
+ if ($last_word[0] == '/') {
+ // If we are working with absolute paths, we need to check if the first
+ // character of all the completions matches. If it does, then we pass a
+ // full path for each match, so the shell completes as far as it can,
+ // matching the behaviour with relative paths.
+ $pos = strlen($last_word);
+ foreach ($return as $id => $item) {
+ if ($item[$pos] !== $return[0][$pos]) {
+ $firstchar = FALSE;
+ continue;
+ }
+ }
+ }
+ foreach ($return as $id => $item) {
+ // For directories we leave the path alone.
+ $slash_pos = strpos($last_word, '/');
+ if ($slash_pos === 0 && $firstchar) {
+ // With absolute paths where completions share initial characters, we
+ // pass in a resolved path.
+ $return[$id] = realpath($item);
+ }
+ else if ($slash_pos !== FALSE && $dir != './') {
+ // For files, we pass only the file name, ignoring the false match when
+ // the user is using a single dot relative path.
+ $return[$id] = basename($item);
+ }
+ }
+ }
return $return;
}