complete.inc

  1. 8.0.x includes/complete.inc
  2. 6.x includes/complete.inc
  3. 7.x includes/complete.inc
  4. 5.x includes/complete.inc
  5. master includes/complete.inc

Provide completion output for shells.

This is not called directly, but by shell completion scripts specific to each shell (bash, csh etc). These run whenever the user triggers completion, typically when pressing <tab>. The shell completion scripts should call "drush complete <text>", where <text> is the full command line, which we take as input and use to produce a list of possible completions for the current/next word, separated by newlines. Typically, when multiple completions are returned the shell will display them to the user in a concise format - but when a single completion is returned it will autocomplete.

We provide completion for site aliases, commands, shell aliases, options, engines and arguments. Displaying all of these when the last word has no characters yet is not useful, as there are too many items. Instead we filter the possible completions based on position, in a similar way to git. For example:

  • We only display site aliases and commands if one is not already present.
  • We only display options if the user has already entered a hyphen.
  • We only display global options before a command is entered, and we only display command specific options after the command (Drush itself does not care about option placement, but this approach keeps things more concise).

Below is typical output of complete in different situations. Tokens in square brackets are optional, and [word] will filter available options that start with the same characters, or display all listed options if empty. drush --[word] : Output global options drush [word] : Output site aliases, sites, commands and shell aliases drush [@alias] [word] : Output commands drush [@alias] command [word] : Output command specific arguments drush [@alias] command --[word] : Output command specific options

Because the purpose of autocompletion is to make the command line more efficient for users we need to respond quickly with the list of completions. To do this, we call drush_complete() early in the Drush bootstrap, and implement a simple caching system.

To generate the list of completions, we set up the Drush environment as if the command was called on it's own, parse the command using the standard Drush functions, bootstrap the site (if any) and collect available completions from various sources. Because this can be somewhat slow, we cache the results. The cache strategy aims to balance accuracy and responsiveness:

  • We cache per site, if a site is available.
  • We generate (and cache) everything except arguments at the same time, so subsequent completions on the site don't need any bootstrap.
  • We generate and cache arguments on-demand, since these can often be expensive to generate. Arguments are also cached per-site.

For argument completions, commandfiles can implement COMMANDFILE_COMMAND_complete() returning an array containing a key 'values' containing an array of all possible argument completions for that command. For example, return array('values' => array('aardvark', 'aardwolf')) offers the words 'aardvark' and 'aardwolf', or will complete to 'aardwolf' if the letters 'aardw' are already present. Since command arguments are cached, commandfiles can bootstrap a site or perform other somewhat time consuming activities to retrieve the list of possible arguments. Commands can also clear the cache (or just the "arguments" cache for their command) when the completion results have likely changed - see drush_complete_cache_clear().

Commandfiles can also return a special optional element in their array with the key 'files' that contains an array of patterns/flags for the glob() function. These are used to produce file and directory completions (the results of these are not cached, since this is a fast operation). See http://php.net/glob for details of valid patterns and flags. For example the following will complete the command arguments on all directories, as well as files ending in tar.gz: return array( 'files' => array( 'directories' => array( 'pattern' => '*', 'flags' => GLOB_ONLYDIR, ), 'tar' => array( 'pattern' => '*.tar.gz', ), ), );

To check completion results without needing to actually trigger shell completion, you can call this manually using a command like:

drush --early=includes/complete.inc [--complete-debug] drush [@alias] [command]...

If you want to simulate the results of pressing tab after a space (i.e. and empty last word, include '' on the end of your command:

drush --early=includes/complete.inc [--complete-debug] drush ''

Functions

Namesort descending Description
drush_complete_cache_cid Generate a cache id.
drush_complete_cache_set Stores caches for completions.
drush_complete_get Retrieves from cache, or generates a listing of completion candidates of a specific type (and optionally, command).
drush_complete_match Retrieves the appropriate list of candidate completions, then filters this list using the last word that we are trying to complete.
drush_complete_match_file Retrieves the appropriate list of candidate file/directory completions, filtered by the last word that we are trying to complete.
drush_complete_process_argv This function resets the raw arguments so that Drush can parse the command as if it was run directly. The shell complete command passes the full command line as an argument, and the --early and --complete-debug options have to come before that, and…
drush_complete_rebuild Rebuild and cache completions for everything except command arguments.
drush_complete_rebuild_arguments Rebuild and cache completions for command arguments.
drush_complete_trailing_space Helper callback function that adds a trailing space to completes in an array.
drush_early_complete Produce autocomplete output.
drush_hyphenate_options Simple helper function to ensure options are properly hyphenated before we return them to the user (we match against the non-hyphenated versions internally).

File

includes/complete.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. *
  5. * Provide completion output for shells.
  6. *
  7. * This is not called directly, but by shell completion scripts specific to
  8. * each shell (bash, csh etc). These run whenever the user triggers completion,
  9. * typically when pressing <tab>. The shell completion scripts should call
  10. * "drush complete <text>", where <text> is the full command line, which we take
  11. * as input and use to produce a list of possible completions for the
  12. * current/next word, separated by newlines. Typically, when multiple
  13. * completions are returned the shell will display them to the user in a concise
  14. * format - but when a single completion is returned it will autocomplete.
  15. *
  16. * We provide completion for site aliases, commands, shell aliases, options,
  17. * engines and arguments. Displaying all of these when the last word has no
  18. * characters yet is not useful, as there are too many items. Instead we filter
  19. * the possible completions based on position, in a similar way to git.
  20. * For example:
  21. * - We only display site aliases and commands if one is not already present.
  22. * - We only display options if the user has already entered a hyphen.
  23. * - We only display global options before a command is entered, and we only
  24. * display command specific options after the command (Drush itself does not
  25. * care about option placement, but this approach keeps things more concise).
  26. *
  27. * Below is typical output of complete in different situations. Tokens in square
  28. * brackets are optional, and [word] will filter available options that start
  29. * with the same characters, or display all listed options if empty.
  30. * drush --[word] : Output global options
  31. * drush [word] : Output site aliases, sites, commands and shell aliases
  32. * drush [@alias] [word] : Output commands
  33. * drush [@alias] command [word] : Output command specific arguments
  34. * drush [@alias] command --[word] : Output command specific options
  35. *
  36. * Because the purpose of autocompletion is to make the command line more
  37. * efficient for users we need to respond quickly with the list of completions.
  38. * To do this, we call drush_complete() early in the Drush bootstrap, and
  39. * implement a simple caching system.
  40. *
  41. * To generate the list of completions, we set up the Drush environment as if
  42. * the command was called on it's own, parse the command using the standard
  43. * Drush functions, bootstrap the site (if any) and collect available
  44. * completions from various sources. Because this can be somewhat slow, we cache
  45. * the results. The cache strategy aims to balance accuracy and responsiveness:
  46. * - We cache per site, if a site is available.
  47. * - We generate (and cache) everything except arguments at the same time, so
  48. * subsequent completions on the site don't need any bootstrap.
  49. * - We generate and cache arguments on-demand, since these can often be
  50. * expensive to generate. Arguments are also cached per-site.
  51. *
  52. * For argument completions, commandfiles can implement
  53. * COMMANDFILE_COMMAND_complete() returning an array containing a key 'values'
  54. * containing an array of all possible argument completions for that command.
  55. * For example, return array('values' => array('aardvark', 'aardwolf')) offers
  56. * the words 'aardvark' and 'aardwolf', or will complete to 'aardwolf' if the
  57. * letters 'aardw' are already present. Since command arguments are cached,
  58. * commandfiles can bootstrap a site or perform other somewhat time consuming
  59. * activities to retrieve the list of possible arguments. Commands can also
  60. * clear the cache (or just the "arguments" cache for their command) when the
  61. * completion results have likely changed - see drush_complete_cache_clear().
  62. *
  63. * Commandfiles can also return a special optional element in their array with
  64. * the key 'files' that contains an array of patterns/flags for the glob()
  65. * function. These are used to produce file and directory completions (the
  66. * results of these are not cached, since this is a fast operation).
  67. * See http://php.net/glob for details of valid patterns and flags.
  68. * For example the following will complete the command arguments on all
  69. * directories, as well as files ending in tar.gz:
  70. * return array(
  71. * 'files' => array(
  72. * 'directories' => array(
  73. * 'pattern' => '*',
  74. * 'flags' => GLOB_ONLYDIR,
  75. * ),
  76. * 'tar' => array(
  77. * 'pattern' => '*.tar.gz',
  78. * ),
  79. * ),
  80. * );
  81. *
  82. * To check completion results without needing to actually trigger shell
  83. * completion, you can call this manually using a command like:
  84. *
  85. * drush --early=includes/complete.inc [--complete-debug] drush [@alias] [command]...
  86. *
  87. * If you want to simulate the results of pressing tab after a space (i.e.
  88. * and empty last word, include '' on the end of your command:
  89. *
  90. * drush --early=includes/complete.inc [--complete-debug] drush ''
  91. */
  92. /**
  93. * Produce autocomplete output.
  94. *
  95. * Determine position (is there a site-alias or command set, and are we trying
  96. * to complete an option). Then produce a list of completions for the last word
  97. * and output them separated by newlines.
  98. */
  99. function drush_early_complete() {
  100. // We use a distinct --complete-debug option to avoid unwanted debug messages
  101. // being printed when users use this option for other purposes in the command
  102. // they are trying to complete.
  103. drush_set_option('debug', FALSE);
  104. if (drush_get_option('complete-debug', FALSE)) {
  105. drush_set_context('DRUSH_DEBUG', TRUE);
  106. }
  107. // Set up as if we were running the command, and attempt to parse.
  108. $argv = drush_complete_process_argv();
  109. if ($alias = drush_get_context('DRUSH_TARGET_SITE_ALIAS')) {
  110. $set_sitealias_name = $alias;
  111. $set_sitealias = drush_sitealias_get_record($alias);
  112. }
  113. // Arguments have now had site-aliases and options removed, so we take the
  114. // first item as our command. We need to know if the command is valid, so that
  115. // we know if we are supposed to complete an in-progress command name, or
  116. // arguments for a command. We do this by checking against our per-site cache
  117. // of command names (which will only bootstrap if the cache needs to be
  118. // regenerated), rather than drush_parse_command() which always requires a
  119. // site bootstrap.
  120. $arguments = drush_get_arguments();
  121. $set_command_name = NULL;
  122. if (isset($arguments[0]) && in_array($arguments[0] . ' ', drush_complete_get('command-names'))) {
  123. $set_command_name = $arguments[0];
  124. }
  125. // We unset the command if it is "help" but that is not explicitly found in
  126. // args, since Drush sets the command to "help" if no command is specified,
  127. // which prevents completion of global options.
  128. if ($set_command_name == 'help' && !array_search('help', $argv)) {
  129. $set_command_name = NULL;
  130. }
  131. // Determine the word we are trying to complete, and if it is an option.
  132. $last_word = end($argv);
  133. $word_is_option = FALSE;
  134. if (!empty($last_word) && $last_word[0] == '-') {
  135. $word_is_option = TRUE;
  136. $last_word = ltrim($last_word, '-');
  137. }
  138. $completions = array();
  139. if (!$set_command_name) {
  140. // We have no command yet.
  141. if ($word_is_option) {
  142. // Include global option completions.
  143. $completions += drush_hyphenate_options(drush_complete_match($last_word, drush_complete_get('options')));
  144. }
  145. else {
  146. if (empty($set_sitealias_name)) {
  147. // Include site alias completions.
  148. $completions += drush_complete_match($last_word, drush_complete_get('site-aliases'));
  149. }
  150. // Include command completions.
  151. $completions += drush_complete_match($last_word, drush_complete_get('command-names'));
  152. }
  153. }
  154. else {
  155. if ($last_word == $set_command_name) {
  156. // The user just typed a valid command name, but we still do command
  157. // completion, as there may be other commands that start with the detected
  158. // command (e.g. "make" is a valid command, but so is "make-test").
  159. // If there is only the single matching command, this will include in the
  160. // completion list so they get a space inserted, confirming it is valid.
  161. $completions += drush_complete_match($last_word, drush_complete_get('command-names'));
  162. }
  163. else if ($word_is_option) {
  164. // Include command option completions.
  165. $completions += drush_hyphenate_options(drush_complete_match($last_word, drush_complete_get('options', $set_command_name)));
  166. }
  167. else {
  168. // Include command argument completions.
  169. $argument_completion = drush_complete_get('arguments', $set_command_name);
  170. if (isset($argument_completion['values'])) {
  171. $completions += drush_complete_match($last_word, $argument_completion['values']);
  172. }
  173. if (isset($argument_completion['files'])) {
  174. $completions += drush_complete_match_file($last_word, $argument_completion['files']);
  175. }
  176. }
  177. }
  178. if (!empty($completions)) {
  179. sort($completions);
  180. return implode("\n", $completions);
  181. }
  182. return TRUE;
  183. }
  184. /**
  185. * This function resets the raw arguments so that Drush can parse the command as
  186. * if it was run directly. The shell complete command passes the
  187. * full command line as an argument, and the --early and --complete-debug
  188. * options have to come before that, and the "drush" bash script will add a
  189. * --php option on the end, so we end up with something like this:
  190. *
  191. * /path/to/drush.php --early=includes/complete.inc [--complete-debug] drush [@alias] [command]... --php=/usr/bin/php
  192. *
  193. * Note that "drush" occurs twice, and also that the second occurrence could be
  194. * an alias, so we can't easily use it as to detect the start of the actual
  195. * command. Hence our approach is to remove the initial "drush" and then any
  196. * options directly following that - what remains is then the command we need
  197. * to complete - i.e.:
  198. *
  199. * drush [@alias] [command]...
  200. *
  201. * Note that if completion is initiated following a space an empty argument is
  202. * added to argv. So in that case argv looks something like this:
  203. * array (
  204. * '0' => '/path/to/drush.php',
  205. * '1' => '--early=includes/complete.inc',
  206. * '2' => 'drush',
  207. * '3' => 'topic',
  208. * '4' => '',
  209. * '5' => '--php=/usr/bin/php',
  210. * );
  211. *
  212. * @return $args
  213. * Array of arguments (argv), excluding the initial command and options
  214. * associated with the complete call.
  215. * array (
  216. * '0' => 'drush',
  217. * '1' => 'topic',
  218. * '2' => '',
  219. * );
  220. */
  221. function drush_complete_process_argv() {
  222. $argv = drush_get_context('argv');
  223. // Remove the first argument, which will be the "drush" command.
  224. array_shift($argv);
  225. while (substr($arg = array_shift($argv), 0, 2) == '--') {
  226. // We remove all options, until we get to a non option, which
  227. // marks the start of the actual command we are trying to complete.
  228. }
  229. // Replace the initial argument.
  230. array_unshift($argv, $arg);
  231. // Remove the --php option at the end if exists (added by the "drush" shell
  232. // script that is called when completion is requested).
  233. if (substr(end($argv), 0, 6) == '--php=') {
  234. array_pop($argv);
  235. }
  236. drush_set_context('argv', $argv);
  237. drush_set_command(NULL);
  238. // Reparse arguments, site alias, and command.
  239. drush_parse_args();
  240. // Ensure the base environment is configures, so tests look in the correct
  241. // places.
  242. _drush_bootstrap_base_environment();
  243. // Check for and record any site alias.
  244. drush_sitealias_check_arg();
  245. drush_sitealias_check_site_env();
  246. // Return the new argv for easy reference.
  247. return $argv;
  248. }
  249. /**
  250. * Retrieves the appropriate list of candidate completions, then filters this
  251. * list using the last word that we are trying to complete.
  252. *
  253. * @param string $last_word
  254. * The last word in the argument list (i.e. the subject of completion).
  255. * @param array $values
  256. * Array of possible completion values to filter.
  257. *
  258. * @return array
  259. * Array of candidate completions that start with the same characters as the
  260. * last word. If the last word is empty, return all candidates.
  261. */
  262. function drush_complete_match($last_word, $values) {
  263. // Using preg_grep appears to be faster that strpos with array_filter/loop.
  264. return preg_grep('/^' . preg_quote($last_word, '/') . '/', $values);
  265. }
  266. /**
  267. * Retrieves the appropriate list of candidate file/directory completions,
  268. * filtered by the last word that we are trying to complete.
  269. *
  270. * @param string $last_word
  271. * The last word in the argument list (i.e. the subject of completion).
  272. * @param array $files
  273. * Array of file specs, each with a pattern and flags subarray.
  274. *
  275. * @return array
  276. * Array of candidate file/directory completions that start with the same
  277. * characters as the last word. If the last word is empty, return all
  278. * candidates.
  279. */
  280. function drush_complete_match_file($last_word, $files) {
  281. $return = array();
  282. if ($last_word[0] == '~') {
  283. // Complete does not do tilde expansion, so we do it here.
  284. // We shell out (unquoted) to expand the tilde.
  285. drush_shell_exec('echo ' . $last_word);
  286. return drush_shell_exec_output();
  287. }
  288. $dir = '';
  289. if (substr($last_word, -1) == '/' && is_dir($last_word)) {
  290. // If we exactly match a trailing directory, then we use that as the base
  291. // for the listing. We only do this if a trailing slash is present, since at
  292. // this stage it is still possible there are other directories that start
  293. // with this string.
  294. $dir = $last_word;
  295. }
  296. else {
  297. // Otherwise we discard the last part of the path (this is matched against
  298. // the list later), and use that as our base.
  299. $dir = dirname($last_word);
  300. if (empty($dir) || $dir == '.' && $last_word != '.' && substr($last_word, 0, 2) != './') {
  301. // We are looking at the current working directory, so unless the user is
  302. // actually specifying a leading dot we leave the path empty.
  303. $dir = '';
  304. }
  305. else {
  306. // In all other cases we need to add a trailing slash.
  307. $dir .= '/';
  308. }
  309. }
  310. foreach ($files as $spec) {
  311. // We always include GLOB_MARK, as an easy way to detect directories.
  312. $flags = GLOB_MARK;
  313. if (isset($spec['flags'])) {
  314. $flags = $spec['flags'] | GLOB_MARK;
  315. }
  316. $listing = glob($dir . $spec['pattern'], $flags);
  317. $return = array_merge($return, drush_complete_match($last_word, $listing));
  318. }
  319. // If we are returning a single item (which will become part of the final
  320. // command), we need to use the full path, and we need to escape it
  321. // appropriately.
  322. if (count($return) == 1) {
  323. // Escape common shell metacharacters (we don't use escapeshellarg as it
  324. // single quotes everything, even when unnecessary).
  325. $item = array_pop($return);
  326. $item = preg_replace('/[ |&;()<>]/', "\\\\$0", $item);
  327. if (substr($item, -1) !== '/') {
  328. // Insert a space after files, since the argument is complete.
  329. $item = $item . ' ';
  330. }
  331. $return = array($item);
  332. }
  333. else {
  334. $firstchar = TRUE;
  335. if ($last_word[0] == '/') {
  336. // If we are working with absolute paths, we need to check if the first
  337. // character of all the completions matches. If it does, then we pass a
  338. // full path for each match, so the shell completes as far as it can,
  339. // matching the behaviour with relative paths.
  340. $pos = strlen($last_word);
  341. foreach ($return as $id => $item) {
  342. if ($item[$pos] !== $return[0][$pos]) {
  343. $firstchar = FALSE;
  344. continue;
  345. }
  346. }
  347. }
  348. foreach ($return as $id => $item) {
  349. // For directories we leave the path alone.
  350. $slash_pos = strpos($last_word, '/');
  351. if ($slash_pos === 0 && $firstchar) {
  352. // With absolute paths where completions share initial characters, we
  353. // pass in a resolved path.
  354. $return[$id] = realpath($item);
  355. }
  356. else if ($slash_pos !== FALSE && $dir != './') {
  357. // For files, we pass only the file name, ignoring the false match when
  358. // the user is using a single dot relative path.
  359. $return[$id] = basename($item);
  360. }
  361. }
  362. }
  363. return $return;
  364. }
  365. /**
  366. * Simple helper function to ensure options are properly hyphenated before we
  367. * return them to the user (we match against the non-hyphenated versions
  368. * internally).
  369. *
  370. * @param array $options
  371. * Array of unhyphenated option names.
  372. *
  373. * @return array
  374. * Array of hyphenated option names.
  375. */
  376. function drush_hyphenate_options($options) {
  377. foreach ($options as $key => $option) {
  378. $options[$key] = '--' . ltrim($option, '--');
  379. }
  380. return $options;
  381. }
  382. /**
  383. * Retrieves from cache, or generates a listing of completion candidates of a
  384. * specific type (and optionally, command).
  385. *
  386. * @param string $type
  387. * String indicating type of completions to return.
  388. * See drush_complete_rebuild() for possible keys.
  389. * @param string $command
  390. * An optional command name if command specific completion is needed.
  391. *
  392. * @return array
  393. * List of candidate completions.
  394. */
  395. function drush_complete_get($type, $command = NULL) {
  396. if (empty($command)) {
  397. // Retrieve global items from a non-command specific cache, or rebuild cache
  398. // if needed.
  399. $cache = drush_cache_get(drush_complete_cache_cid($type), 'complete');
  400. if (isset($cache->data)) {
  401. return $cache->data;
  402. }
  403. $complete = drush_complete_rebuild();
  404. return $complete[$type];
  405. }
  406. // Retrieve items from a command specific cache.
  407. $cache = drush_cache_get(drush_complete_cache_cid($type, $command), 'complete');
  408. if (isset($cache->data)) {
  409. return $cache->data;
  410. }
  411. // Build argument cache - built only on demand.
  412. if ($type == 'arguments') {
  413. return drush_complete_rebuild_arguments($command);
  414. }
  415. // Rebuild cache of general command specific items.
  416. $complete = drush_complete_rebuild();
  417. if (!empty($complete['commands'][$command][$type])) {
  418. return $complete['commands'][$command][$type];
  419. }
  420. return array();
  421. }
  422. /**
  423. * Rebuild and cache completions for everything except command arguments.
  424. *
  425. * @return array
  426. * Structured array of completion types, commands and candidate completions.
  427. */
  428. function drush_complete_rebuild() {
  429. $complete = array();
  430. // Bootstrap to the site level (if possible) - commands may need to check
  431. // the bootstrap level, and perhaps bootstrap higher in extraordinary cases.
  432. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
  433. $commands = drush_get_commands();
  434. foreach ($commands as $command_name => $command) {
  435. // Add command options and suboptions.
  436. $options = array_keys($command['options']);
  437. foreach ($command['sub-options'] as $option => $sub_options) {
  438. $options = array_merge($options, array_keys($sub_options));
  439. }
  440. $complete['commands'][$command_name]['options'] = $options;
  441. }
  442. // We treat shell aliases as commands for the purposes of completion.
  443. $complete['command-names'] = array_merge(array_keys($commands), array_keys(drush_get_context('shell-aliases', array())));
  444. $site_aliases = _drush_sitealias_all_list();
  445. // TODO: Figure out where this dummy @0 alias is introduced.
  446. unset($site_aliases['@0']);
  447. $complete['site-aliases'] = array_keys($site_aliases);
  448. $complete['options'] = array_keys(drush_get_global_options());
  449. // We add a space following all completes. Eventually there may be some
  450. // items (e.g. options that we know need values) where we don't add a space.
  451. array_walk_recursive($complete, 'drush_complete_trailing_space');
  452. drush_complete_cache_set($complete);
  453. return $complete;
  454. }
  455. /**
  456. * Helper callback function that adds a trailing space to completes in an array.
  457. */
  458. function drush_complete_trailing_space(&$item, $key) {
  459. if (!is_array($item)) {
  460. $item = (string)$item . ' ';
  461. }
  462. }
  463. /**
  464. * Rebuild and cache completions for command arguments.
  465. *
  466. * @param string $command
  467. * A specific command to retrieve and cache arguments for.
  468. *
  469. * @return array
  470. * Structured array of candidate completion arguments, keyed by the command.
  471. */
  472. function drush_complete_rebuild_arguments($command) {
  473. // Bootstrap to the site level (if possible) - commands may need to check
  474. // the bootstrap level, and perhaps bootstrap higher in extraordinary cases.
  475. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
  476. $commands = drush_get_commands();
  477. $hook = str_replace("-", "_", $commands[$command]['command-hook']);
  478. $result = drush_command_invoke_all($hook . '_complete');
  479. if (isset($result['values'])) {
  480. // We add a space following all completes. Eventually there may be some
  481. // items (e.g. comma separated arguments) where we don't add a space.
  482. array_walk($result['values'], 'drush_complete_trailing_space');
  483. }
  484. $complete = array(
  485. 'commands' => array(
  486. $command => array(
  487. 'arguments' => $result,
  488. )
  489. )
  490. );
  491. drush_complete_cache_set($complete);
  492. return $complete['commands'][$command]['arguments'];
  493. }
  494. /**
  495. * Stores caches for completions.
  496. *
  497. * @param $complete
  498. * A structured array of completions, keyed by type, including a 'commands'
  499. * type that contains all commands with command specific completions keyed by
  500. * type. The array does not need to include all types - used by
  501. * drush_complete_rebuild_arguments().
  502. */
  503. function drush_complete_cache_set($complete) {
  504. foreach ($complete as $type => $values) {
  505. if ($type == 'commands') {
  506. foreach ($values as $command_name => $command) {
  507. foreach ($command as $command_type => $command_values) {
  508. drush_cache_set(drush_complete_cache_cid($command_type, $command_name), $command_values, 'complete', DRUSH_CACHE_TEMPORARY);
  509. }
  510. }
  511. }
  512. else {
  513. drush_cache_set(drush_complete_cache_cid($type), $values, 'complete', DRUSH_CACHE_TEMPORARY);
  514. }
  515. }
  516. }
  517. /**
  518. * Generate a cache id.
  519. *
  520. * @param $type
  521. * The completion type.
  522. * @param $command
  523. * The command name (optional), if completions are command specific.
  524. *
  525. * @return string
  526. * Cache id.
  527. */
  528. function drush_complete_cache_cid($type, $command = NULL) {
  529. // For per-site caches, we include the site root and uri/path in the cache id
  530. // hash. These are quick to determine, and prevents a bootstrap to site just
  531. // to get a validated root and URI. Because these are not validated, there is
  532. // the possibility of cache misses/ but they should be rare, since sites are
  533. // normally referred to the same way (e.g. a site alias, or using the current
  534. // directory), at least within a single command completion session.
  535. // We also static cache them, since we may get differing results after
  536. // bootstrap, which prevents the caches from being found on the next call.
  537. static $root, $site;
  538. if (empty($root)) {
  539. $root = drush_get_option(array('r', 'root'), drush_locate_root());
  540. $site = drush_get_option(array('l', 'uri'), drush_site_path());
  541. }
  542. return drush_get_cid('complete', array(), array($type, $command, $root, $site));
  543. }