Index: commands/pm/pm.drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/pm/pm.drush.inc,v retrieving revision 1.41 diff -u -r1.41 pm.drush.inc --- commands/pm/pm.drush.inc 18 Jun 2009 01:39:16 -0000 1.41 +++ commands/pm/pm.drush.inc 25 Jun 2009 00:58:06 -0000 @@ -101,9 +101,6 @@ 'description' => 'Show module enabled/disabled status', 'callback' => 'pm_module_manage', 'callback arguments' => array(array(), FALSE), - 'options' => array( - '--pipe' => 'Returns a space delimited list of enabled modules.', - ), ); $items['refresh'] = array( 'description' => 'Refresh update status information', @@ -308,9 +305,6 @@ $rows[] = array($info['name'] . ' (' . $module_name . ')', $enabled, truncate_utf8($info['description'], 60, FALSE, TRUE)); } drush_print_table($rows, TRUE); - - // Space delimited list for use by other scripts. Set the --pipe option. - drush_print_pipe(implode(' ', $pipe)); } /** @@ -746,6 +740,34 @@ } /** + * Fetch project XML files + */ +function drush_pm_dl_xml($project = 'project-list', $drupal_version = 'all') { + $xml = FALSE; + + // Don't rely on UPDATE_DEFAULT_URL since we are not fully bootstrapped. + $url = 'http://updates.drupal.org/release-history' . "/$project/". $drupal_version; + + // A simple download, which is never available via CVS. + // Some hosts have allow_url_fopen disabled. + if (!$xml = @simplexml_load_file($url)) { + if (!drush_shell_exec("wget $url")) { + drush_shell_exec("curl -O $url"); + } + // Get the filename... + $filename = explode('/', $url); + $filename = array_pop($filename); + $xml = simplexml_load_file($filename); + drush_op('unlink', $filename); + } + if ($error = $xml->xpath('/error')) { + drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]); + $xml = FALSE; + } + return $xml; +} + +/** * Command callback. Download drupal core. */ function drush_pm_dl() { @@ -765,57 +787,37 @@ $project_types_xpath = '(value="' . implode('" or value="', $project_types) . '")'; foreach ($requestdata as $package) { $project = $package['name']; - // Don't rely on UPDATE_DEFAULT_URL since we are not fully bootstrapped. - $url = 'http://updates.drupal.org/release-history' . "/$project/". $package['drupal_version']; - - // A simple download, which is never available via CVS. - // Some hosts have allow_url_fopen disabled. - if (!$xml = @simplexml_load_file($url)) { - if (!drush_shell_exec("wget $url")) { - drush_shell_exec("curl -O $url"); - } - // Get the filename... - $filename = explode('/', $url); - $filename = array_pop($filename); - $xml = simplexml_load_file($filename); - drush_op('unlink', $filename); - } - - if ($xml) { - if ($error = $xml->xpath('/error')) { - drush_set_error('DRUSH_PM_COULD_NOT_LOAD_UPDATE_FILE', $error[0]); - } - else { - // Try to get the specified release. - if ($package['version']) { - if ($releases = $xml->xpath("/project/releases/release[status='published'][version='" . $package['version'] . "']")) { - $release = (array)$releases[0]; - } - if (empty($release)) { - drush_log(dt("Could not locate specified project version, downloading latest stable version"), 'notice'); - } - } - // If that did not work, get the first published release for the recommended major version. - if (empty($release)) { - $recommended_major = $xml->xpath("/project/recommended_major"); - $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$recommended_major[0] . "]"; - $releases = $xml->xpath($xpath_releases); + + if ($xml = drush_pm_dl_xml($project, $package['drupal_version'])) { + // Try to get the specified release. + if ($package['version']) { + if ($releases = $xml->xpath("/project/releases/release[status='published'][version='" . $package['version'] . "']")) { $release = (array)$releases[0]; } - // Determine what type of project we have, so we know where to put it. - $release['type'] = 'module'; - - if ($types = $xml->xpath('/project/terms/term[name="Projects" and ' . $project_types_xpath . ']')) { - $release['type'] = array_search($types[0]->value, $project_types); + if (empty($release)) { + drush_log(dt("Could not locate specified project version, downloading latest stable version"), 'notice'); } - - if ($destination = pm_dl_destination($release['type'])) { - if (package_handler_install_project($project, $release, $destination)) { - drush_log(dt("Project !project (!version) downloaded to !dest.", - array('!project' => $project, '!version' => $release['version'], '!dest' => $destination)), 'success'); - drush_command_invoke_all('drush_pm_post_install', $project, $release, $destination); - version_control_post_install($project, $release, $destination); - } + } + // If that did not work, get the first published release for the recommended major version. + if (empty($release)) { + $recommended_major = $xml->xpath("/project/recommended_major"); + $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$recommended_major[0] . "]"; + $releases = $xml->xpath($xpath_releases); + $release = (array)$releases[0]; + } + // Determine what type of project we have, so we know where to put it. + $release['type'] = 'module'; + + if ($types = $xml->xpath('/project/terms/term[name="Projects" and ' . $project_types_xpath . ']')) { + $release['type'] = array_search($types[0]->value, $project_types); + } + + if ($destination = pm_dl_destination($release['type'])) { + if (package_handler_install_project($project, $release, $destination)) { + drush_log(dt("Project !project (!version) downloaded to !dest.", + array('!project' => $project, '!version' => $release['version'], '!dest' => $destination)), 'success'); + drush_command_invoke_all('drush_pm_post_install', $project, $release, $destination); + version_control_post_install($project, $release, $destination); } } } @@ -828,3 +830,28 @@ unset($package, $error, $release, $types); } } + +function pm_drush_complete() { + $complete = array(); + if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_DATABASE) { + $result = db_query('SELECT name, status FROM {system} WHERE type = "module"'); + while ($module = db_fetch_object($result)) { + if ($module->status == TRUE) { + $complete['commands']['disable'][] = $module->name; + } + else { + $complete['commands']['enable'][] = $module->name; + } + } + } + if ($xml = drush_pm_dl_xml()) { + // TODO: Possibly introduce a separate caching step for this. + if ($projects = $xml->xpath("/projects/project[project_status='published'][api_versions/api_version='" . drush_get_context('DRUSH_DRUPAL_MAJOR_VERSION', 6) . ".x']/short_name")) { + foreach ($projects as $project) { + $complete['modules'][] = (string)$project; + } + $complete['commands']['dl'] = $complete['commands']['update'] = $complete['commands']['updatecode'] = $complete['commands']['info'] = 'modules'; + } + } + return $complete; +} \ No newline at end of file Index: includes/drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/drush.inc,v retrieving revision 1.41 diff -u -r1.41 drush.inc --- includes/drush.inc 27 May 2009 00:47:38 -0000 1.41 +++ includes/drush.inc 25 Jun 2009 00:58:06 -0000 @@ -298,7 +298,6 @@ $options['-c, --config'] = dt("Specify a config file to use. See example.drushrc.php"); $options['-u, --user'] = dt("Specify a user to login with. May be a name or a number."); $options['-b, --backend'] = dt("Hide all output and return structured data (internal use only)."); - $options['-p, --pipe'] = dt("Emit a compact representation of the command for scripting."); return $options; } @@ -440,16 +439,6 @@ } /** - * Stores a message which is printed during drush_shutdown() if in compact mode. - * @param $message - * The message to print. - */ -function drush_print_pipe($message = '') { - $buffer = &drush_get_context('DRUSH_PIPE_BUFFER' , ''); - $buffer .= $message; -} - -/** * Prints an array or string. * @param $array * The array to print. @@ -663,14 +652,6 @@ drush_log($contents); } -/* - * Display the pipe output for the current request. - */ -function drush_pipe_output() { - $pipe = drush_get_context('DRUSH_PIPE_BUFFER'); - drush_print_r($pipe); -} - /** * Display the log message * Index: includes/environment.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/includes/environment.inc,v retrieving revision 1.37 diff -u -r1.37 environment.inc --- includes/environment.inc 24 Jun 2009 00:00:23 -0000 1.37 +++ includes/environment.inc 25 Jun 2009 00:58:06 -0000 @@ -242,9 +242,12 @@ /** * Bootstrap to the highest level possible, without triggering any errors. */ -function drush_bootstrap_max() { +function drush_bootstrap_max($max = NULL) { $phases = _drush_bootstrap_phases(); $phase_index = DRUSH_BOOTSTRAP_DRUSH; + if (isset($max)) { + $phases = array_slice($phases, 0, $max+1); + } // Try to bootstrap to the maximum possible level, without generating errors foreach ($phases as $phase_index) { @@ -408,11 +411,8 @@ drush_set_context('stdin', $stdin_options); } } - - // Pipe implies quiet. - $quiet = drush_set_context('DRUSH_QUIET', drush_get_option(array('q', 'quiet', 'p', 'pipe'))); - - drush_set_context('DRUSH_PIPE', drush_get_option(array('p', 'pipe'))); + + $quiet = drush_set_context('DRUSH_QUIET', drush_get_option(array('q', 'quiet'))); // When running in backend mode, all output is buffered, and returned // as a property of a JSON encoded associative array. Index: commands/core/core.drush.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/commands/core/core.drush.inc,v retrieving revision 1.30 diff -u -r1.30 core.drush.inc --- commands/core/core.drush.inc 2 Jun 2009 23:48:31 -0000 1.30 +++ commands/core/core.drush.inc 25 Jun 2009 00:58:06 -0000 @@ -29,7 +29,6 @@ 'examples' => array( 'drush dl cck zen' => 'Download CCK module and Zen theme.', 'drush --uri=http://example.com status' => 'Show status command for the example.com multi-site.', - 'drush help --pipe' => 'A space delimited list of commands', ), ); $items['cron'] = array( @@ -100,6 +99,9 @@ 'code' => 'PHP code', ), ); + $items['complete'] = array( + 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap. + ); return $items; } @@ -155,7 +157,6 @@ foreach($commands as $key => $command) { if (!array_key_exists($key, $printed_rows)) { $rows[$key] = array($key, $commands[$key]['description']); - $pipe[] = "\"$key\""; } } drush_print_table($rows, FALSE, array(0 => 20)); @@ -165,9 +166,7 @@ break; } } - - // Space delimited list for use by other scripts. Set the --pipe option. - drush_print_pipe(implode(' ', $pipe)); + return; } else { @@ -439,3 +438,123 @@ function drush_core_eval($command) { eval($command . ';'); } + +function drush_core_complete() { + // Set up arguments as if we were running the command, and attempt to parse. + $args = drush_get_context('argv'); + // TODO: This needs work. + // For some reason we get duplicate drush/drush.php arguments in some cases, + // we also need to remove the "complete" command (running now). + $args = preg_grep("/(drush|drush.php|complete)/", $args, PREG_GREP_INVERT); + // We also get an empty argument on the end when we autocomplete on just 'drush '. + $last_word = end($args); + if (empty($last_word)) { + array_pop($args); + } + // Add the drush command back in, since we removed it above. + array_unshift($args, 'drush'); + // Reindex the array. + $args = array_values($args); + drush_set_context('argv', $args); + drush_set_command(NULL); + drush_parse_args(); + drush_parse_command(); + + $complete = array(); + $command = drush_get_command(); + $command_name = $command['command']; + $cache_filename = sys_get_temp_dir() . '/drush_complete.' . substr(md5(DRUPAL_ROOT . drush_get_context('DRUSH_DRUPAL_SITE_ROOT')), 0, 5); + if (file_exists($cache_filename)) { + $cache = file_get_contents($cache_filename); + $complete = unserialize($cache); + } + if (empty($complete)) { + // Bootstrap to the highest level possible (up to the database level). + drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_DATABASE); + $complete = drush_command_invoke_all('drush_complete'); + foreach (array_keys(drush_get_commands()) as $command_key) { + if (!isset($complete['commands'][$command_key])) { + $complete['commands'][$command_key] = NULL; + } + } + } +// print_r($complete); + $completions = array(); + + // Argument debugging code +// foreach ($args as $n => $arg) { +// $completions[] = $n.'-'.$arg; +// } + + // Add in any command specific options. + if ($command && $command_name !== 'help') { + $complete['options'] = array_merge($complete['options'], array_keys($command['options'])); + } + // Remove any options that have been already specified. + foreach (array_keys(drush_get_merged_options()) as $option) { + unset($complete['options'][array_search('--' . $option, $complete['options'])]); + } + + // Complete options (starting with a hypnen) first. + $last_word = end($args); + if ($last_word[0] == '-') { + $completions = preg_grep("/^$last_word/", $complete['options']); + } + else if ($command && $command_name !== 'help') { + // We do have a valid command already, so we try matching command arguments. + $complete_arguments = array(); + if (isset($complete['commands'][$command_name])) { + if (is_array($complete['commands'][$command_name])) { + // Arguments can be added as an array on the command. + $complete_arguments = $complete['commands'][$command_name]; + } + if (is_string($complete['commands'][$command_name]) && isset($complete[$complete['commands'][$command_name]]) && is_array($complete[$complete['commands'][$command_name]])) { + // Alternatively a key to retrieve argument sets shared between multiple commands. + $complete_arguments = $complete[$complete['commands'][$command_name]]; + } + } + while (!empty($args) && empty($completions)) { + $candidate = implode(' ', $args); + $completions = preg_grep("/^$candidate/", $complete_arguments); + array_shift($args); + } + if (empty($completions)) { + $completions = array_merge($complete_arguments, $complete['options']); + } + } + else { + // We don't have a valid command, so we complete available commands trying + // the longest match first. + while (!empty($args) && empty($completions)) { + $candidate = implode(' ', $args); + $completions = preg_grep("/^$candidate/", array_keys($complete['commands'])); + array_shift($args); + } + } + // The default, if we haven't yet found any context sensitive completions is + // to return commands and options. + if (empty($completions)) { + $completions = array_merge(array_keys($complete['commands']), $complete['options']); + } + drush_print(implode("\n", $completions)); + if (!file_exists($cache_filename)) { + file_put_contents($cache_filename, serialize($complete)); + } +} + +function core_drush_complete() { + // TODO: Store this in a better structured format upstream + $complete = array(); + preg_match_all('/--[a-z-]*/', implode(' ', array_keys(drush_get_option_help())), $matches); + $complete['options'] = $matches[0]; + + if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_DATABASE) { + $result = db_query('SELECT DISTINCT(type) FROM {watchdog} ORDER BY type'); + while ($object = db_fetch_object($result)) { + $complete['watchdog'][] = $object->type; + } + $complete['commands']['watchdog show'] = 'watchdog'; + $complete['commands']['watchdog delete'] = 'watchdog'; + } + return $complete; +} \ No newline at end of file Index: drush.php =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/drush/drush.php,v retrieving revision 1.68 diff -u -r1.68 drush.php --- drush.php 4 Jun 2009 19:48:55 -0000 1.68 +++ drush.php 25 Jun 2009 00:58:06 -0000 @@ -156,12 +156,7 @@ elseif (drush_get_context('DRUSH_QUIET')) { ob_end_clean(); } - - // If we are in pipe mode, emit the compact representation of the command, if available. - if (drush_get_context('DRUSH_PIPE')) { - drush_pipe_output(); - } - + exit((drush_get_error()) ? DRUSH_FRAMEWORK_ERROR : DRUSH_SUCCESS); } Index: drush.completion =================================================================== RCS file: drush.completion diff -N drush.completion --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ drush.completion 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,9 @@ +#!/bin/bash +_drush_completion() { + oldIFS="$IFS"; + IFS=$'\n'; + COMPREPLY=( $(drush complete "${COMP_WORDS[@]}" < /dev/null) ) + IFS="$oldIFS"; +} + +complete -F _drush_completion d dr drush drush.php