2006-11-29 14:21:02 +00:00
/** \file complete.c Functions related to tab-completion.
2005-09-20 13:26:39 +00:00
2012-11-18 10:23:22 +00:00
These functions are used for storing and retrieving tab - completion data , as well as for performing tab - completion .
2005-09-20 13:26:39 +00:00
*/
2006-08-11 01:18:35 +00:00
# include "config.h"
2005-09-20 13:26:39 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <limits.h>
# include <string.h>
# include <wchar.h>
# include <wctype.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <dirent.h>
# include <errno.h>
# include <termios.h>
# include <ctype.h>
# include <pwd.h>
# include <signal.h>
2006-02-08 15:29:09 +00:00
# include <wchar.h>
2012-02-24 20:13:35 +00:00
# include <pthread.h>
2012-04-24 02:29:44 +00:00
# include <algorithm>
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "tokenizer.h"
# include "wildcard.h"
# include "proc.h"
# include "parser.h"
# include "function.h"
# include "complete.h"
# include "builtin.h"
# include "env.h"
# include "exec.h"
# include "expand.h"
# include "common.h"
# include "reader.h"
# include "history.h"
# include "intern.h"
2006-01-30 16:51:50 +00:00
# include "parse_util.h"
2007-04-22 09:50:26 +00:00
# include "parser_keywords.h"
2005-09-20 13:26:39 +00:00
# include "wutil.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2005-09-20 13:26:39 +00:00
/*
2006-02-03 21:30:06 +00:00
Completion description strings , mostly for different types of files , such as sockets , block devices , etc .
2005-09-20 13:26:39 +00:00
2006-02-03 21:30:06 +00:00
There are a few more completion description strings defined in
expand . c . Maybe all completion description strings should be defined
in the same file ?
2005-09-20 13:26:39 +00:00
*/
/**
Description for ~ USER completion
*/
2008-01-13 19:32:21 +00:00
# define COMPLETE_USER_DESC _( L"Home for %ls" )
2005-09-20 13:26:39 +00:00
/**
Description for short variables . The value is concatenated to this description
*/
2007-02-17 11:05:55 +00:00
# define COMPLETE_VAR_DESC_VAL _( L"Variable: %ls" )
2005-09-20 13:26:39 +00:00
/**
The maximum number of commands on which to perform description
lookup . The lookup process is quite time consuming , so this should
be set to a pretty low number .
*/
# define MAX_CMD_DESC_LOOKUP 10
/**
Condition cache value returned from hashtable when this condition
2006-02-03 21:30:06 +00:00
has not yet been tested . This value is NULL , so that when the hash
table returns NULL , this wil be seen as an untested condition .
2005-09-20 13:26:39 +00:00
*/
# define CC_NOT_TESTED 0
/**
2006-02-03 21:30:06 +00:00
Condition cache value returned from hashtable when the condition is
met . This can be any value , that is a valid pointer , and that is
different from CC_NOT_TESTED and CC_FALSE .
2005-09-20 13:26:39 +00:00
*/
# define CC_TRUE L"true"
/**
2006-02-03 21:30:06 +00:00
Condition cache value returned from hashtable when the condition is
not met . This can be any value , that is a valid pointer , and that
is different from CC_NOT_TESTED and CC_TRUE .
2005-09-20 13:26:39 +00:00
*/
# define CC_FALSE L"false"
2006-06-07 23:56:01 +00:00
/**
The special cased translation macro for completions . The empty
string needs to be special cased , since it can occur , and should
not be translated . ( Gettext returns the version information as the
response )
*/
2006-11-29 14:18:22 +00:00
# ifdef USE_GETTEXT
2007-10-13 21:26:06 +00:00
# define C_(wstr) ((wcscmp(wstr, L"")==0)?L"":wgettext(wstr))
2006-11-29 14:18:22 +00:00
# else
# define C_(string) (string)
# endif
2013-03-06 04:54:16 +00:00
/* Testing apparatus */
const wcstring_list_t * s_override_variable_names = NULL ;
2006-06-07 23:56:01 +00:00
2013-03-06 04:54:16 +00:00
void complete_set_variable_names ( const wcstring_list_t * names )
{
s_override_variable_names = names ;
}
static inline wcstring_list_t complete_get_variable_names ( void )
{
if ( s_override_variable_names ! = NULL )
{
return * s_override_variable_names ;
}
else
{
return env_get_names ( 0 ) ;
}
}
2009-02-02 22:46:45 +00:00
2005-09-20 13:26:39 +00:00
/**
2006-01-30 19:53:10 +00:00
Struct describing a completion option entry .
2005-09-20 13:26:39 +00:00
If short_opt and long_opt are both zero , the comp field must not be
empty and contains a list of arguments to the command .
If either short_opt or long_opt are non - zero , they specify a switch
for the command . If \ c comp is also not empty , it contains a list
2006-02-03 21:30:06 +00:00
of non - switch arguments that may only follow directly after the
2012-11-18 10:23:22 +00:00
specified switch .
2005-09-20 13:26:39 +00:00
*/
typedef struct complete_entry_opt
{
2012-11-19 00:30:30 +00:00
/** Short style option */
wchar_t short_opt ;
/** Long style option */
wcstring long_opt ;
/** Arguments to the option */
wcstring comp ;
/** Description of the completion */
wcstring desc ;
/** Condition under which to use the option */
wcstring condition ;
/** Must be one of the values SHARED, NO_FILES, NO_COMMON,
EXCLUSIVE , and determines how completions should be performed
on the argument after the switch . */
int result_mode ;
/** True if old style long options are used */
int old_mode ;
/** Completion flags */
complete_flags_t flags ;
2012-11-18 10:23:22 +00:00
2012-02-09 00:15:53 +00:00
const wchar_t * localized_desc ( ) const
{
const wchar_t * tmp = desc . c_str ( ) ;
return C_ ( tmp ) ;
}
} complete_entry_opt_t ;
2005-09-20 13:26:39 +00:00
2012-04-12 01:25:37 +00:00
/* Last value used in the order field of completion_entry_t */
static unsigned int kCompleteOrder = 0 ;
2005-09-20 13:26:39 +00:00
/**
Struct describing a command completion
*/
2012-02-09 00:15:53 +00:00
typedef std : : list < complete_entry_opt_t > option_list_t ;
2012-02-26 21:27:31 +00:00
class completion_entry_t
2012-11-18 10:23:22 +00:00
{
2012-05-12 01:59:38 +00:00
public :
2012-11-19 00:30:30 +00:00
/** List of all options */
option_list_t options ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/** String containing all short option characters */
wcstring short_opt_str ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
public :
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/** Command string */
const wcstring cmd ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/** True if command is a path */
const bool cmd_is_path ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/** True if no other options than the ones supplied are possible */
bool authoritative ;
2012-11-18 10:23:22 +00:00
2012-04-12 01:25:37 +00:00
/** Order for when this completion was created. This aids in outputting completions sorted by time. */
const unsigned int order ;
2012-11-18 10:23:22 +00:00
2012-02-26 22:32:06 +00:00
/** Getters for option list. */
const option_list_t & get_options ( ) const ;
2012-11-18 10:23:22 +00:00
2012-05-12 01:59:38 +00:00
/** Adds or removes an option. */
void add_option ( const complete_entry_opt_t & opt ) ;
bool remove_option ( wchar_t short_opt , const wchar_t * long_opt ) ;
2012-11-18 10:23:22 +00:00
2012-02-26 22:32:06 +00:00
/** Getter for short_opt_str. */
2012-11-18 10:23:22 +00:00
wcstring & get_short_opt_str ( ) ;
2012-02-26 22:32:06 +00:00
const wcstring & get_short_opt_str ( ) const ;
2012-11-18 10:23:22 +00:00
2012-02-26 21:27:31 +00:00
completion_entry_t ( const wcstring & c , bool type , const wcstring & options , bool author ) :
2012-02-26 22:32:06 +00:00
short_opt_str ( options ) ,
2012-02-26 21:27:31 +00:00
cmd ( c ) ,
cmd_is_path ( type ) ,
2012-04-12 01:25:37 +00:00
authoritative ( author ) ,
order ( + + kCompleteOrder )
2012-02-26 21:27:31 +00:00
{
}
2012-02-08 22:47:50 +00:00
} ;
2005-09-20 13:26:39 +00:00
2012-04-10 03:17:06 +00:00
/** Set of all completion entries */
2012-11-19 00:30:30 +00:00
struct completion_entry_set_comparer
{
2012-04-10 03:17:06 +00:00
/** Comparison for std::set */
2012-11-19 00:30:30 +00:00
bool operator ( ) ( completion_entry_t * p1 , completion_entry_t * p2 ) const
{
2012-04-10 03:17:06 +00:00
/* Paths always come last for no particular reason */
2012-11-19 00:30:30 +00:00
if ( p1 - > cmd_is_path ! = p2 - > cmd_is_path )
{
2012-04-10 03:17:06 +00:00
return p1 - > cmd_is_path < p2 - > cmd_is_path ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-04-10 03:17:06 +00:00
return p1 - > cmd < p2 - > cmd ;
}
}
} ;
typedef std : : set < completion_entry_t * , completion_entry_set_comparer > completion_entry_set_t ;
static completion_entry_set_t completion_set ;
2012-02-26 22:32:06 +00:00
2012-04-12 01:25:37 +00:00
// Comparison function to sort completions by their order field
2012-11-19 00:30:30 +00:00
static bool compare_completions_by_order ( const completion_entry_t * p1 , const completion_entry_t * p2 )
{
2012-04-12 01:25:37 +00:00
return p1 - > order < p2 - > order ;
}
2012-02-26 22:32:06 +00:00
/** The lock that guards the list of completion entries */
2012-02-24 20:13:35 +00:00
static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER ;
2005-09-20 13:26:39 +00:00
2012-11-18 12:52:21 +00:00
/**
* The lock that guards the options list of individual completion entries .
* If both completion_lock and completion_entry_lock are to be taken ,
* completion_lock must be taken first .
*/
2012-02-26 22:32:06 +00:00
static pthread_mutex_t completion_entry_lock = PTHREAD_MUTEX_INITIALIZER ;
2012-05-12 01:59:38 +00:00
2012-11-19 00:30:30 +00:00
void completion_entry_t : : add_option ( const complete_entry_opt_t & opt )
{
2012-02-26 22:32:06 +00:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
2012-05-12 01:59:38 +00:00
options . push_front ( opt ) ;
2012-02-26 22:32:06 +00:00
}
2012-11-19 00:30:30 +00:00
const option_list_t & completion_entry_t : : get_options ( ) const
{
2012-02-26 22:32:06 +00:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
return options ;
}
2012-11-19 00:30:30 +00:00
wcstring & completion_entry_t : : get_short_opt_str ( )
{
2012-02-26 22:32:06 +00:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
return short_opt_str ;
}
2012-11-19 00:30:30 +00:00
const wcstring & completion_entry_t : : get_short_opt_str ( ) const
{
2012-02-26 22:32:06 +00:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
2012-11-18 10:23:22 +00:00
return short_opt_str ;
2012-02-26 22:32:06 +00:00
}
2005-09-20 13:26:39 +00:00
2013-04-16 22:01:24 +00:00
completion_t : : ~ completion_t ( )
{
}
2012-07-17 19:47:01 +00:00
/* completion_t functions */
2013-05-25 22:41:18 +00:00
completion_t : : completion_t ( const wcstring & comp , const wcstring & desc , string_fuzzy_match_t mat , int flags_val ) :
completion ( comp ) ,
description ( desc ) ,
match ( mat ) ,
flags ( flags_val )
2012-07-17 19:47:01 +00:00
{
2012-11-19 00:30:30 +00:00
if ( flags & COMPLETE_AUTO_SPACE )
2012-07-18 14:18:19 +00:00
{
flags = flags & ~ COMPLETE_AUTO_SPACE ;
size_t len = completion . size ( ) ;
2012-11-19 00:30:30 +00:00
if ( len > 0 & & ( wcschr ( L " /=@: " , comp . at ( len - 1 ) ) ! = 0 ) )
2012-07-18 14:18:19 +00:00
flags | = COMPLETE_NO_SPACE ;
}
2012-07-17 19:47:01 +00:00
}
2013-05-25 22:41:18 +00:00
completion_t : : completion_t ( const completion_t & him ) : completion ( him . completion ) , description ( him . description ) , match ( him . match ) , flags ( him . flags )
2012-07-17 19:47:01 +00:00
{
}
completion_t & completion_t : : operator = ( const completion_t & him )
{
if ( this ! = & him )
{
this - > completion = him . completion ;
this - > description = him . description ;
2013-05-25 22:41:18 +00:00
this - > match = him . match ;
2012-07-17 19:47:01 +00:00
this - > flags = him . flags ;
}
return * this ;
}
2012-08-22 00:18:52 +00:00
2012-11-19 00:30:30 +00:00
wcstring_list_t completions_to_wcstring_list ( const std : : vector < completion_t > & list )
2012-07-17 19:47:01 +00:00
{
wcstring_list_t strings ;
strings . reserve ( list . size ( ) ) ;
2012-11-19 00:30:30 +00:00
for ( std : : vector < completion_t > : : const_iterator iter = list . begin ( ) ; iter ! = list . end ( ) ; + + iter )
{
2012-07-17 19:47:01 +00:00
strings . push_back ( iter - > completion ) ;
}
return strings ;
}
2013-08-31 22:01:02 +00:00
bool completion_t : : is_alphabetically_less_than ( const completion_t & a , const completion_t & b )
{
return a . completion < b . completion ;
}
bool completion_t : : is_alphabetically_equal_to ( const completion_t & a , const completion_t & b )
2012-07-17 19:47:01 +00:00
{
2013-08-31 22:01:02 +00:00
return a . completion = = b . completion ;
2012-07-17 19:47:01 +00:00
}
2013-08-31 22:01:02 +00:00
2012-02-25 02:43:10 +00:00
/** Class representing an attempt to compute completions */
2012-11-19 00:30:30 +00:00
class completer_t
{
2013-03-06 04:54:16 +00:00
const completion_request_flags_t flags ;
2012-02-26 21:27:31 +00:00
const wcstring initial_cmd ;
2012-02-25 02:43:10 +00:00
std : : vector < completion_t > completions ;
2012-02-27 04:11:34 +00:00
wcstring_list_t commands_to_load ;
2012-11-18 10:23:22 +00:00
2012-02-26 21:27:31 +00:00
/** Table of completions conditions that have already been tested and the corresponding test results */
typedef std : : map < wcstring , bool > condition_cache_t ;
condition_cache_t condition_cache ;
2013-03-22 00:44:51 +00:00
2013-03-06 04:54:16 +00:00
enum complete_type_t
{
COMPLETE_DEFAULT ,
COMPLETE_AUTOSUGGEST
} ;
2013-03-22 00:44:51 +00:00
2013-03-06 04:54:16 +00:00
complete_type_t type ( ) const
{
return ( flags & COMPLETION_REQUEST_AUTOSUGGESTION ) ? COMPLETE_AUTOSUGGEST : COMPLETE_DEFAULT ;
}
2013-03-22 00:44:51 +00:00
2013-03-06 04:54:16 +00:00
bool wants_descriptions ( ) const
{
2013-03-22 00:44:51 +00:00
return ! ! ( flags & COMPLETION_REQUEST_DESCRIPTIONS ) ;
2013-03-06 04:54:16 +00:00
}
2013-03-22 00:44:51 +00:00
2013-03-06 04:54:16 +00:00
bool fuzzy ( ) const
{
2013-03-22 00:44:51 +00:00
return ! ! ( flags & COMPLETION_REQUEST_FUZZY_MATCH ) ;
2013-03-06 04:54:16 +00:00
}
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
fuzzy_match_type_t max_fuzzy_match_type ( ) const
{
/* If we are doing fuzzy matching, request all types; if not request only prefix matching */
return ( flags & COMPLETION_REQUEST_FUZZY_MATCH ) ? fuzzy_match_none : fuzzy_match_prefix_case_insensitive ;
}
2013-03-06 04:54:16 +00:00
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
public :
2013-03-06 04:54:16 +00:00
completer_t ( const wcstring & c , completion_request_flags_t f ) :
flags ( f ) ,
2012-02-27 04:11:34 +00:00
initial_cmd ( c )
2012-02-25 02:43:10 +00:00
{
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
bool empty ( ) const
{
return completions . empty ( ) ;
}
const std : : vector < completion_t > & get_completions ( void )
{
return completions ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
bool try_complete_variable ( const wcstring & str ) ;
bool try_complete_user ( const wcstring & str ) ;
2012-02-25 02:43:10 +00:00
2012-11-19 00:30:30 +00:00
bool complete_param ( const wcstring & cmd_orig ,
const wcstring & popt ,
const wcstring & str ,
bool use_switches ) ;
2012-11-18 10:23:22 +00:00
2012-02-26 02:54:49 +00:00
void complete_param_expand ( const wcstring & str , bool do_file ) ;
2012-11-18 10:23:22 +00:00
2012-05-05 21:21:21 +00:00
void debug_print_completions ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
void complete_cmd ( const wcstring & str ,
bool use_function ,
bool use_builtin ,
bool use_command ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
void complete_from_args ( const wcstring & str ,
const wcstring & args ,
const wcstring & desc ,
complete_flags_t flags ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
void complete_cmd_desc ( const wcstring & str ) ;
2012-11-18 10:23:22 +00:00
2012-08-01 23:32:52 +00:00
bool complete_variable ( const wcstring & str , size_t start_offset ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
bool condition_test ( const wcstring & condition ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
void complete_strings ( const wcstring & wc_escaped ,
const wchar_t * desc ,
wcstring ( * desc_func ) ( const wcstring & ) ,
std : : vector < completion_t > & possible_comp ,
complete_flags_t flags ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
expand_flags_t expand_flags ( ) const
{
2012-08-07 07:01:48 +00:00
/* Never do command substitution in autosuggestions. Sadly, we also can't yet do job expansion because it's not thread safe. */
2012-03-10 04:16:26 +00:00
expand_flags_t result = 0 ;
2013-03-06 04:54:16 +00:00
if ( this - > type ( ) = = COMPLETE_AUTOSUGGEST )
2012-08-07 07:01:48 +00:00
result | = EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS ;
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
/* Allow fuzzy matching */
if ( this - > fuzzy ( ) )
result | = EXPAND_FUZZY_MATCH ;
2013-06-02 08:14:26 +00:00
2012-03-10 04:16:26 +00:00
return result ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
void get_commands_to_load ( wcstring_list_t * lst )
{
2012-02-27 04:11:34 +00:00
if ( lst )
lst - > insert ( lst - > end ( ) , commands_to_load . begin ( ) , commands_to_load . end ( ) ) ;
}
2012-02-25 02:43:10 +00:00
2012-11-23 19:22:58 +00:00
bool has_commands_to_load ( ) const
{
return ! commands_to_load . empty ( ) ;
}
2012-02-25 02:43:10 +00:00
} ;
2012-01-26 02:40:08 +00:00
/* Autoloader for completions */
2012-11-19 00:30:30 +00:00
class completion_autoload_t : public autoload_t
{
2012-01-26 02:40:08 +00:00
public :
completion_autoload_t ( ) ;
virtual void command_removed ( const wcstring & cmd ) ;
} ;
static completion_autoload_t completion_autoloader ;
/** Constructor */
2012-12-09 05:41:38 +00:00
completion_autoload_t : : completion_autoload_t ( ) : autoload_t ( L " fish_complete_path " , NULL , 0 )
2012-01-26 02:40:08 +00:00
{
}
/** Callback when an autoloaded completion is removed */
2012-11-19 00:30:30 +00:00
void completion_autoload_t : : command_removed ( const wcstring & cmd )
{
complete_remove ( cmd . c_str ( ) , COMMAND , 0 , 0 ) ;
2012-01-26 02:40:08 +00:00
}
2012-01-23 19:42:41 +00:00
2013-02-18 00:14:36 +00:00
/** Create a new completion entry */
2013-05-25 22:41:18 +00:00
void append_completion ( std : : vector < completion_t > & completions , const wcstring & comp , const wcstring & desc , complete_flags_t flags , string_fuzzy_match_t match )
2007-02-17 11:05:55 +00:00
{
2013-05-25 22:41:18 +00:00
completions . push_back ( completion_t ( comp , desc , match , flags ) ) ;
2007-02-17 11:05:55 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Test if the specified script returns zero . The result is cached , so
that if multiple completions use the same condition , it needs only
be evaluated once . condition_cache_clear must be called after a
completion run to make sure that there are no stale completions .
*/
2012-11-19 00:30:30 +00:00
bool completer_t : : condition_test ( const wcstring & condition )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( condition . empty ( ) )
{
2012-11-18 10:23:22 +00:00
// fwprintf( stderr, L"No condition specified\n" );
2012-11-19 00:30:30 +00:00
return 1 ;
}
2012-11-18 10:23:22 +00:00
2013-03-06 04:54:16 +00:00
if ( this - > type ( ) = = COMPLETE_AUTOSUGGEST )
2012-02-25 02:43:10 +00:00
{
/* Autosuggestion can't support conditions */
return 0 ;
}
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-09 00:15:53 +00:00
bool test_res ;
2012-02-26 21:27:31 +00:00
condition_cache_t : : iterator cached_entry = condition_cache . find ( condition ) ;
2012-11-19 00:30:30 +00:00
if ( cached_entry = = condition_cache . end ( ) )
{
2012-02-09 00:15:53 +00:00
/* Compute new value and reinsert it */
2013-01-31 23:57:08 +00:00
test_res = ( 0 = = exec_subshell ( condition , false /* don't apply exit status */ ) ) ;
2012-02-09 00:15:53 +00:00
condition_cache [ condition ] = test_res ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-02-09 00:15:53 +00:00
/* Use the old value */
test_res = cached_entry - > second ;
}
return test_res ;
2005-09-20 13:26:39 +00:00
}
2012-02-24 20:13:35 +00:00
/** Search for an exactly matching completion entry. Must be called while locked. */
2012-11-19 00:30:30 +00:00
static completion_entry_t * complete_find_exact_entry ( const wcstring & cmd , const bool cmd_is_path )
2005-09-20 13:26:39 +00:00
{
2012-02-24 20:13:35 +00:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-04-10 03:17:06 +00:00
completion_entry_t * result = NULL ;
completion_entry_t tmp_entry ( cmd , cmd_is_path , L " " , false ) ;
completion_entry_set_t : : iterator iter = completion_set . find ( & tmp_entry ) ;
2012-11-19 00:30:30 +00:00
if ( iter ! = completion_set . end ( ) )
{
2012-04-10 03:17:06 +00:00
result = * iter ;
}
2012-11-19 00:30:30 +00:00
return result ;
2005-09-20 13:26:39 +00:00
}
2012-02-24 20:13:35 +00:00
/** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */
2012-11-19 00:30:30 +00:00
static completion_entry_t * complete_get_exact_entry ( const wcstring & cmd , bool cmd_is_path )
2005-09-20 13:26:39 +00:00
{
2012-02-24 20:13:35 +00:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-11-19 00:30:30 +00:00
completion_entry_t * c ;
2006-10-19 11:50:23 +00:00
2012-11-19 00:30:30 +00:00
c = complete_find_exact_entry ( cmd , cmd_is_path ) ;
2006-06-08 00:01:45 +00:00
2012-11-19 00:30:30 +00:00
if ( c = = NULL )
{
2012-08-07 06:34:55 +00:00
c = new completion_entry_t ( cmd , cmd_is_path , L " " , false ) ;
2012-04-10 03:17:06 +00:00
completion_set . insert ( c ) ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return c ;
2007-01-28 13:40:59 +00:00
}
2012-11-19 00:30:30 +00:00
void complete_set_authoritative ( const wchar_t * cmd , bool cmd_is_path , bool authoritative )
2007-01-28 13:40:59 +00:00
{
2012-11-19 00:30:30 +00:00
completion_entry_t * c ;
2007-01-28 13:40:59 +00:00
2012-11-19 00:30:30 +00:00
CHECK ( cmd , ) ;
2012-02-24 20:13:35 +00:00
scoped_lock lock ( completion_lock ) ;
2012-11-19 00:30:30 +00:00
c = complete_get_exact_entry ( cmd , cmd_is_path ) ;
c - > authoritative = authoritative ;
2007-01-28 13:40:59 +00:00
}
2007-01-28 03:24:16 +00:00
2007-01-28 13:40:59 +00:00
2012-11-18 12:52:21 +00:00
void complete_add ( const wchar_t * cmd ,
2012-11-19 00:30:30 +00:00
bool cmd_is_path ,
wchar_t short_opt ,
const wchar_t * long_opt ,
int old_mode ,
int result_mode ,
const wchar_t * condition ,
const wchar_t * comp ,
const wchar_t * desc ,
complete_flags_t flags )
{
CHECK ( cmd , ) ;
/* Lock the lock that allows us to edit the completion entry list */
scoped_lock lock ( completion_lock ) ;
/* Lock the lock that allows us to edit individual completion entries */
scoped_lock lock2 ( completion_entry_lock ) ;
completion_entry_t * c ;
c = complete_get_exact_entry ( cmd , cmd_is_path ) ;
/* Create our new option */
complete_entry_opt_t opt ;
if ( short_opt ! = L ' \0 ' )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
int len = 1 + ( ( result_mode & NO_COMMON ) ! = 0 ) ;
c - > get_short_opt_str ( ) . push_back ( short_opt ) ;
if ( len = = 2 )
{
c - > get_short_opt_str ( ) . push_back ( L ' : ' ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
opt . short_opt = short_opt ;
opt . result_mode = result_mode ;
opt . old_mode = old_mode ;
2012-02-09 00:15:53 +00:00
2012-11-19 00:30:30 +00:00
if ( comp ) opt . comp = comp ;
if ( condition ) opt . condition = condition ;
if ( long_opt ) opt . long_opt = long_opt ;
if ( desc ) opt . desc = desc ;
opt . flags = flags ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
c - > add_option ( opt ) ;
2005-09-20 13:26:39 +00:00
}
2006-12-13 23:58:38 +00:00
/**
Remove all completion options in the specified entry that match the
2012-02-08 22:47:50 +00:00
specified short / long option strings . Returns true if it is now
2012-02-24 20:13:35 +00:00
empty and should be deleted , false if it ' s not empty . Must be called while locked .
2006-12-13 23:58:38 +00:00
*/
2012-11-19 00:30:30 +00:00
bool completion_entry_t : : remove_option ( wchar_t short_opt , const wchar_t * long_opt )
2005-09-20 13:26:39 +00:00
{
2012-02-24 20:13:35 +00:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-02-26 22:32:06 +00:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
2012-11-19 00:30:30 +00:00
if ( ( short_opt = = 0 ) & & ( long_opt = = 0 ) )
{
2012-05-12 01:59:38 +00:00
this - > options . clear ( ) ;
2012-11-19 00:30:30 +00:00
}
else
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
for ( option_list_t : : iterator iter = this - > options . begin ( ) ; iter ! = this - > options . end ( ) ; )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
complete_entry_opt_t & o = * iter ;
if ( short_opt = = o . short_opt | | long_opt = = o . long_opt )
{
/* fwprintf( stderr,
L " remove option -%lc --%ls \n " ,
o - > short_opt ? o - > short_opt : L ' ' ,
o - > long_opt ) ;
*/
if ( o . short_opt )
{
2012-05-12 01:59:38 +00:00
wcstring & short_opt_str = this - > get_short_opt_str ( ) ;
2012-02-26 22:32:06 +00:00
size_t idx = short_opt_str . find ( o . short_opt ) ;
2012-02-08 22:47:50 +00:00
if ( idx ! = wcstring : : npos )
{
/* Consume all colons */
size_t first_non_colon = idx + 1 ;
2012-02-26 22:32:06 +00:00
while ( first_non_colon < short_opt_str . size ( ) & & short_opt_str . at ( first_non_colon ) = = L ' : ' )
2012-02-08 22:47:50 +00:00
first_non_colon + + ;
2012-02-26 22:32:06 +00:00
short_opt_str . erase ( idx , first_non_colon - idx ) ;
2012-02-08 22:47:50 +00:00
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-02-09 00:15:53 +00:00
/* Destroy this option and go to the next one */
2012-11-19 00:30:30 +00:00
iter = this - > options . erase ( iter ) ;
}
else
{
2012-02-09 00:15:53 +00:00
/* Just go to the next one */
2012-11-19 00:30:30 +00:00
+ + iter ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-05-12 01:59:38 +00:00
return this - > options . empty ( ) ;
2006-12-13 23:58:38 +00:00
}
2012-11-19 00:30:30 +00:00
void complete_remove ( const wchar_t * cmd ,
bool cmd_is_path ,
wchar_t short_opt ,
const wchar_t * long_opt )
2006-12-13 23:58:38 +00:00
{
2012-11-19 00:30:30 +00:00
CHECK ( cmd , ) ;
2012-02-24 20:13:35 +00:00
scoped_lock lock ( completion_lock ) ;
2012-04-24 18:01:04 +00:00
scoped_lock lock2 ( completion_entry_lock ) ;
2012-11-18 10:23:22 +00:00
2012-04-10 03:17:06 +00:00
completion_entry_t tmp_entry ( cmd , cmd_is_path , L " " , false ) ;
completion_entry_set_t : : iterator iter = completion_set . find ( & tmp_entry ) ;
2012-11-19 00:30:30 +00:00
if ( iter ! = completion_set . end ( ) )
{
2012-04-10 03:17:06 +00:00
completion_entry_t * entry = * iter ;
2012-05-12 01:59:38 +00:00
bool delete_it = entry - > remove_option ( short_opt , long_opt ) ;
2012-11-19 00:30:30 +00:00
if ( delete_it )
{
2012-02-08 22:47:50 +00:00
/* Delete this entry */
2012-04-10 03:17:06 +00:00
completion_set . erase ( iter ) ;
delete entry ;
2012-02-08 22:47:50 +00:00
}
2012-04-10 03:17:06 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-02-11 01:54:21 +00:00
/* Formats an error string by prepending the prefix and then appending the str in single quotes */
2012-11-19 00:30:30 +00:00
static wcstring format_error ( const wchar_t * prefix , const wcstring & str )
{
2012-02-11 01:54:21 +00:00
wcstring result = prefix ;
result . push_back ( L ' \' ' ) ;
result . append ( str ) ;
result . push_back ( L ' \' ' ) ;
return result ;
}
2012-02-08 06:44:10 +00:00
/**
Find the full path and commandname from a command string ' str ' .
*/
2012-11-19 00:30:30 +00:00
static void parse_cmd_string ( const wcstring & str , wcstring & path , wcstring & cmd )
{
if ( ! path_get_path ( str , & path ) )
{
/** Use the empty string as the 'path' for commands that can not be found. */
2012-02-08 06:44:10 +00:00
path = L " " ;
}
2012-11-18 10:23:22 +00:00
2012-02-08 06:44:10 +00:00
/* Make sure the path is not included in the command */
size_t last_slash = str . find_last_of ( L ' / ' ) ;
2012-11-19 00:30:30 +00:00
if ( last_slash ! = wcstring : : npos )
{
2012-02-08 06:44:10 +00:00
cmd = str . substr ( last_slash + 1 ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-02-08 06:44:10 +00:00
cmd = str ;
}
}
2012-11-19 00:30:30 +00:00
int complete_is_valid_option ( const wcstring & str ,
const wcstring & opt ,
wcstring_list_t * errors ,
bool allow_autoload )
2005-09-20 13:26:39 +00:00
{
2012-02-08 06:44:10 +00:00
wcstring cmd , path ;
2012-11-19 00:30:30 +00:00
bool found_match = false ;
bool authoritative = true ;
int opt_found = 0 ;
std : : set < wcstring > gnu_match_set ;
bool is_gnu_opt = false ;
bool is_old_opt = false ;
bool is_short_opt = false ;
bool is_gnu_exact = false ;
size_t gnu_opt_len = 0 ;
2012-11-18 10:23:22 +00:00
2012-08-07 06:34:55 +00:00
if ( opt . empty ( ) )
return false ;
2012-11-18 10:23:22 +00:00
2012-08-07 06:34:55 +00:00
std : : vector < bool > short_validated ;
2012-11-19 00:30:30 +00:00
/*
Check some generic things like - - and - options .
*/
switch ( opt . size ( ) )
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case 0 :
case 1 :
2012-11-19 00:30:30 +00:00
{
return true ;
}
2012-11-19 08:31:03 +00:00
case 2 :
{
if ( opt = = L " -- " )
{
return true ;
}
break ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( opt . at ( 0 ) ! = L ' - ' )
{
if ( errors )
2012-02-11 01:54:21 +00:00
errors - > push_back ( L " Option does not begin with a '-' " ) ;
2012-11-19 00:30:30 +00:00
return false ;
}
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
2012-08-07 06:34:55 +00:00
short_validated . resize ( opt . size ( ) , 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
is_gnu_opt = opt . at ( 1 ) = = L ' - ' ;
if ( is_gnu_opt )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
size_t opt_end = opt . find ( L ' = ' ) ;
if ( opt_end ! = wcstring : : npos )
{
gnu_opt_len = opt_end - 2 ;
}
else
{
gnu_opt_len = opt . size ( ) - 2 ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
parse_cmd_string ( str , path , cmd ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Make sure completions are loaded for the specified command
*/
if ( allow_autoload )
{
complete_load ( cmd , false ) ;
2012-02-27 04:11:34 +00:00
}
2012-02-24 20:13:35 +00:00
2012-11-19 00:30:30 +00:00
scoped_lock lock ( completion_lock ) ;
2012-02-26 22:32:06 +00:00
scoped_lock lock2 ( completion_entry_lock ) ;
2012-04-10 03:17:06 +00:00
for ( completion_entry_set_t : : const_iterator iter = completion_set . begin ( ) ; iter ! = completion_set . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
2012-02-08 22:47:50 +00:00
const completion_entry_t * i = * iter ;
2012-11-19 00:30:30 +00:00
const wcstring & match = i - > cmd_is_path ? path : cmd ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( ! wildcard_match ( match , i - > cmd ) )
{
continue ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
found_match = true ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! i - > authoritative )
{
authoritative = false ;
break ;
}
2005-09-20 13:26:39 +00:00
2012-02-26 22:32:06 +00:00
const option_list_t & options = i - > get_options ( ) ;
2012-11-19 00:30:30 +00:00
if ( is_gnu_opt )
{
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator iter = options . begin ( ) ; iter ! = options . end ( ) ; + + iter )
2012-02-09 00:15:53 +00:00
{
const complete_entry_opt_t & o = * iter ;
2012-11-19 00:30:30 +00:00
if ( o . old_mode )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2012-08-07 06:34:55 +00:00
if ( opt . compare ( 2 , gnu_opt_len , o . long_opt ) = = 0 )
2012-11-19 00:30:30 +00:00
{
2012-02-09 00:15:53 +00:00
gnu_match_set . insert ( o . long_opt ) ;
2012-11-19 00:30:30 +00:00
if ( opt . compare ( 2 , o . long_opt . size ( ) , o . long_opt ) )
{
is_gnu_exact = true ;
}
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
{
/* Check for old style options */
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator iter = options . begin ( ) ; iter ! = options . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
2012-02-09 00:15:53 +00:00
const complete_entry_opt_t & o = * iter ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( ! o . old_mode )
continue ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( opt . compare ( 1 , wcstring : : npos , o . long_opt ) = = 0 )
{
opt_found = true ;
is_old_opt = true ;
break ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( is_old_opt )
break ;
2005-09-20 13:26:39 +00:00
2012-08-07 06:34:55 +00:00
for ( size_t opt_idx = 1 ; opt_idx < opt . size ( ) ; opt_idx + + )
2012-11-19 00:30:30 +00:00
{
2012-02-26 22:32:06 +00:00
const wcstring & short_opt_str = i - > get_short_opt_str ( ) ;
2012-08-07 06:34:55 +00:00
size_t str_idx = short_opt_str . find ( opt . at ( opt_idx ) ) ;
2012-11-19 00:30:30 +00:00
if ( str_idx ! = wcstring : : npos )
{
if ( str_idx + 1 < short_opt_str . size ( ) & & short_opt_str . at ( str_idx + 1 ) = = L ' : ' )
{
/*
This is a short option with an embedded argument ,
call complete_is_valid_argument on the argument .
*/
2012-08-07 06:34:55 +00:00
const wcstring nopt = L " - " + opt . substr ( 1 , 1 ) ;
2012-11-19 00:30:30 +00:00
short_validated . at ( opt_idx ) =
complete_is_valid_argument ( str , nopt , opt . substr ( 2 ) ) ;
}
else
{
short_validated . at ( opt_idx ) = true ;
}
}
}
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
if ( authoritative )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! is_gnu_opt & & ! is_old_opt )
is_short_opt = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( is_short_opt )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
opt_found = 1 ;
for ( size_t j = 1 ; j < opt . size ( ) ; j + + )
{
if ( ! short_validated . at ( j ) )
{
if ( errors )
{
2012-08-07 06:34:55 +00:00
const wcstring str = opt . substr ( j , 1 ) ;
2013-02-16 08:02:40 +00:00
errors - > push_back ( format_error ( _ ( L " Unknown option: " ) , str ) ) ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
opt_found = 0 ;
break ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
}
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( is_gnu_opt )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
opt_found = is_gnu_exact | | ( gnu_match_set . size ( ) = = 1 ) ;
if ( errors & & ! opt_found )
{
const wchar_t * prefix ;
if ( gnu_match_set . empty ( ) )
{
2012-02-11 01:54:21 +00:00
prefix = _ ( L " Unknown option: " ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2012-02-11 01:54:21 +00:00
prefix = _ ( L " Multiple matches for option: " ) ;
2012-11-19 00:30:30 +00:00
}
2012-02-11 01:54:21 +00:00
errors - > push_back ( format_error ( prefix , opt ) ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
2012-08-07 06:34:55 +00:00
return ( authoritative & & found_match ) ? opt_found : true ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
bool complete_is_valid_argument ( const wcstring & str , const wcstring & opt , const wcstring & arg )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return true ;
2005-09-20 13:26:39 +00:00
}
/**
Copy any strings in possible_comp which have the specified prefix
2012-06-29 23:40:54 +00:00
to the completer ' s completion array . The prefix may contain wildcards .
The output will consist of completion_t structs .
2005-09-20 13:26:39 +00:00
There are three ways to specify descriptions for each
completion . Firstly , if a description has already been added to the
completion , it is _not_ replaced . Secondly , if the desc_func
function is specified , use it to determine a dynamic
completion . Thirdly , if none of the above are available , the desc
string is used as a description .
\ param wc_escaped the prefix , possibly containing wildcards . The wildcard should not have been unescaped , i . e . ' * ' should be used for any string , not the ANY_STRING character .
2006-01-09 17:17:25 +00:00
\ param desc the default description , used for completions with no embedded description . The description _may_ contain a COMPLETE_SEP character , if not , one will be prefixed to it
2005-09-20 13:26:39 +00:00
\ param desc_func the function that generates a description for those completions witout an embedded description
\ param possible_comp the list of possible completions to iterate over
*/
2007-02-17 11:05:55 +00:00
2012-11-19 00:30:30 +00:00
void completer_t : : complete_strings ( const wcstring & wc_escaped ,
const wchar_t * desc ,
wcstring ( * desc_func ) ( const wcstring & ) ,
std : : vector < completion_t > & possible_comp ,
complete_flags_t flags )
2007-02-17 11:05:55 +00:00
{
2012-01-30 10:23:58 +00:00
wcstring tmp = wc_escaped ;
2012-06-29 23:40:54 +00:00
if ( ! expand_one ( tmp , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this - > expand_flags ( ) ) )
2012-01-30 10:23:58 +00:00
return ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * wc = parse_util_unescape_wildcards ( tmp . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < possible_comp . size ( ) ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
wcstring temp = possible_comp . at ( i ) . completion ;
const wchar_t * next_str = temp . empty ( ) ? NULL : temp . c_str ( ) ;
if ( next_str )
{
2013-05-25 22:41:18 +00:00
wildcard_complete ( next_str , wc , desc , desc_func , this - > completions , this - > expand_flags ( ) , flags ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
free ( ( void * ) wc ) ;
2007-02-17 11:05:55 +00:00
}
2005-09-20 13:26:39 +00:00
/**
If command to complete is short enough , substitute
the description with the whatis information for the executable .
*/
2012-11-19 00:30:30 +00:00
void completer_t : : complete_cmd_desc ( const wcstring & str )
2005-09-20 13:26:39 +00:00
{
2012-02-24 20:13:35 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * cmd_start ;
int skip ;
2012-11-18 10:23:22 +00:00
2012-02-26 21:27:31 +00:00
const wchar_t * const cmd = str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
cmd_start = wcsrchr ( cmd , L ' / ' ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( cmd_start )
cmd_start + + ;
else
cmd_start = cmd ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Using apropos with a single - character search term produces far
to many results - require at least two characters if we don ' t
know the location of the whatis - database .
*/
if ( wcslen ( cmd_start ) < 2 )
return ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( wildcard_has ( cmd_start , 0 ) )
{
return ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
skip = 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < this - > completions . size ( ) ; i + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const completion_t & c = this - > completions . at ( i ) ;
if ( c . completion . empty ( ) | | ( c . completion [ c . completion . size ( ) - 1 ] ! = L ' / ' ) )
{
skip = 0 ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( skip )
{
return ;
}
2012-11-18 10:23:22 +00:00
2006-11-16 13:04:00 +00:00
2012-02-02 20:04:04 +00:00
wcstring lookup_cmd ( L " __fish_describe_command " ) ;
lookup_cmd . append ( escape_string ( cmd_start , 1 ) ) ;
2005-09-20 13:26:39 +00:00
2012-02-02 20:04:04 +00:00
std : : map < wcstring , wcstring > lookup ;
2006-02-08 15:29:09 +00:00
2012-11-18 10:23:22 +00:00
/*
2012-11-19 00:30:30 +00:00
First locate a list of possible descriptions using a single
call to apropos or a direct search if we know the location
of the whatis database . This can take some time on slower
systems with a large set of manuals , but it should be ok
since apropos is only called once .
2012-11-18 10:23:22 +00:00
*/
2012-11-19 00:30:30 +00:00
wcstring_list_t list ;
2013-01-31 23:57:08 +00:00
if ( exec_subshell ( lookup_cmd , list , false /* don't apply exit status */ ) ! = - 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
/*
Then discard anything that is not a possible completion and put
the result into a hashtable with the completion as key and the
description as value .
Should be reasonably fast , since no memory allocations are needed .
*/
for ( size_t i = 0 ; i < list . size ( ) ; i + + )
{
2012-02-02 20:04:04 +00:00
const wcstring & elstr = list . at ( i ) ;
2012-11-18 10:23:22 +00:00
2012-02-02 20:04:04 +00:00
const wcstring fullkey ( elstr , wcslen ( cmd_start ) ) ;
2012-11-18 10:23:22 +00:00
2012-02-02 20:04:04 +00:00
size_t tab_idx = fullkey . find ( L ' \t ' ) ;
2012-11-19 00:30:30 +00:00
if ( tab_idx = = wcstring : : npos )
continue ;
2012-11-18 10:23:22 +00:00
2012-02-02 20:04:04 +00:00
const wcstring key ( fullkey , 0 , tab_idx ) ;
wcstring val ( fullkey , tab_idx + 1 ) ;
2007-01-07 14:24:30 +00:00
2012-11-19 00:30:30 +00:00
/*
And once again I make sure the first character is uppercased
because I like it that way , and I get to decide these
things .
*/
2012-02-02 20:04:04 +00:00
if ( ! val . empty ( ) )
val [ 0 ] = towupper ( val [ 0 ] ) ;
2012-11-18 10:23:22 +00:00
2012-02-02 20:04:04 +00:00
lookup [ key ] = val ;
2012-11-19 00:30:30 +00:00
}
2006-01-08 02:56:56 +00:00
2012-11-19 00:30:30 +00:00
/*
Then do a lookup on every completion and if a match is found ,
change to the new description .
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
This needs to do a reallocation for every description added , but
there shouldn ' t be that many completions , so it should be ok .
*/
for ( size_t i = 0 ; i < this - > completions . size ( ) ; i + + )
{
2012-02-26 21:27:31 +00:00
completion_t & completion = this - > completions . at ( i ) ;
2012-02-02 20:04:04 +00:00
const wcstring & el = completion . completion ;
if ( el . empty ( ) )
continue ;
2012-11-18 10:23:22 +00:00
2012-02-02 20:04:04 +00:00
std : : map < wcstring , wcstring > : : iterator new_desc_iter = lookup . find ( el ) ;
if ( new_desc_iter ! = lookup . end ( ) )
completion . description = new_desc_iter - > second ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
}
2006-06-17 13:07:08 +00:00
/**
2012-05-18 02:37:46 +00:00
Returns a description for the specified function , or an empty string if none
2006-06-17 13:07:08 +00:00
*/
2012-11-19 00:30:30 +00:00
static wcstring complete_function_desc ( const wcstring & fn )
2006-01-23 23:32:13 +00:00
{
2012-05-18 02:37:46 +00:00
wcstring result ;
2012-05-18 02:46:08 +00:00
bool has_description = function_get_desc ( fn , & result ) ;
2012-11-19 00:30:30 +00:00
if ( ! has_description )
{
2012-05-18 02:37:46 +00:00
function_get_definition ( fn , & result ) ;
}
2012-11-19 00:30:30 +00:00
return result ;
2006-01-23 23:32:13 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Complete the specified command name . Search for executables in the
path , executables defined using an absolute path , functions ,
builtins and directories for implicit cd commands .
\ param cmd the command string to find completions for
2006-01-23 23:33:47 +00:00
\ param comp the list to add all completions to
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
void completer_t : : complete_cmd ( const wcstring & str_cmd , bool use_function , bool use_builtin , bool use_command )
2005-09-20 13:26:39 +00:00
{
2012-05-09 09:33:42 +00:00
/* Paranoia */
if ( str_cmd . empty ( ) )
return ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
std : : vector < completion_t > possible_comp ;
2006-01-30 19:53:10 +00:00
2012-05-06 21:53:19 +00:00
env_var_t cdpath = env_get_string ( L " CDPATH " ) ;
if ( cdpath . missing_or_empty ( ) )
cdpath = L " . " ;
2012-11-18 10:23:22 +00:00
2013-10-01 06:29:40 +00:00
if ( use_command )
2012-11-18 10:23:22 +00:00
{
2006-01-30 19:53:10 +00:00
2013-10-01 06:29:40 +00:00
if ( expand_string ( str_cmd , this - > completions , ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) ) ! = EXPAND_ERROR )
2012-11-19 00:30:30 +00:00
{
2013-10-01 06:29:40 +00:00
if ( this - > wants_descriptions ( ) )
2012-11-19 00:30:30 +00:00
{
2013-10-01 06:29:40 +00:00
this - > complete_cmd_desc ( str_cmd ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
}
2013-10-01 06:29:40 +00:00
if ( str_cmd . find ( L ' / ' ) = = wcstring : : npos & & str_cmd . at ( 0 ) ! = L ' ~ ' )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( use_command )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const env_var_t path = env_get_string ( L " PATH " ) ;
if ( ! path . missing ( ) )
{
2012-07-20 22:01:56 +00:00
wcstring base_path ;
wcstokenizer tokenizer ( path , ARRAY_SEP_STR ) ;
2012-11-19 00:30:30 +00:00
while ( tokenizer . next ( base_path ) )
{
2012-05-09 09:33:42 +00:00
if ( base_path . empty ( ) )
continue ;
2012-11-18 10:23:22 +00:00
2012-05-09 09:33:42 +00:00
/* Make sure the base path ends with a slash */
if ( base_path . at ( base_path . size ( ) - 1 ) ! = L ' / ' )
base_path . push_back ( L ' / ' ) ;
wcstring nxt_completion = base_path ;
nxt_completion . append ( str_cmd ) ;
size_t prev_count = this - > completions . size ( ) ;
2012-11-19 00:30:30 +00:00
if ( expand_string ( nxt_completion ,
this - > completions ,
ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) ) ! = EXPAND_ERROR )
{
2012-05-09 09:33:42 +00:00
/* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */
2012-11-19 00:30:30 +00:00
for ( size_t i = prev_count ; i < this - > completions . size ( ) ; i + + )
{
completion_t & c = this - > completions . at ( i ) ;
2013-03-06 04:54:16 +00:00
if ( c . flags & COMPLETE_REPLACES_TOKEN )
2012-11-19 00:30:30 +00:00
{
c . completion . erase ( 0 , base_path . size ( ) ) ;
}
}
}
}
2013-03-06 04:54:16 +00:00
if ( this - > wants_descriptions ( ) )
2012-11-19 00:30:30 +00:00
this - > complete_cmd_desc ( str_cmd ) ;
2012-11-18 10:23:22 +00:00
}
}
2013-06-02 08:14:26 +00:00
2012-11-19 00:30:30 +00:00
if ( use_function )
{
//function_get_names( &possible_comp, cmd[0] == L'_' );
wcstring_list_t names = function_get_names ( str_cmd . at ( 0 ) = = L ' _ ' ) ;
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
{
2012-02-02 00:27:14 +00:00
possible_comp . push_back ( completion_t ( names . at ( i ) ) ) ;
2012-01-14 07:44:18 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
this - > complete_strings ( str_cmd , 0 , & complete_function_desc , possible_comp , 0 ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
possible_comp . clear ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( use_builtin )
{
builtin_get_names ( possible_comp ) ;
this - > complete_strings ( str_cmd , 0 , & builtin_get_desc , possible_comp , 0 ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
}
2012-02-25 02:43:10 +00:00
2005-09-20 13:26:39 +00:00
/**
Evaluate the argument list ( as supplied by complete - a ) and insert
2006-01-09 17:40:32 +00:00
any return matching completions . Matching is done using \ c
2005-09-20 13:26:39 +00:00
copy_strings_with_prefix , meaning the completion may contain
wildcards . Logically , this is not always the right thing to do , but
2006-01-09 17:40:32 +00:00
I have yet to come up with a case where this matters .
2006-01-30 19:53:10 +00:00
2005-09-20 13:26:39 +00:00
\ param str The string to complete .
\ param args The list of option arguments to be evaluated .
\ param desc Description of the completion
\ param comp_out The list into which the results will be inserted
*/
2012-11-19 00:30:30 +00:00
void completer_t : : complete_from_args ( const wcstring & str ,
const wcstring & args ,
const wcstring & desc ,
complete_flags_t flags )
2005-09-20 13:26:39 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
std : : vector < completion_t > possible_comp ;
2006-01-30 19:53:10 +00:00
2013-03-06 04:54:16 +00:00
bool is_autosuggest = ( this - > type ( ) = = COMPLETE_AUTOSUGGEST ) ;
2012-05-06 20:51:11 +00:00
parser_t parser ( is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL , false ) ;
2012-05-05 21:33:24 +00:00
2012-11-18 10:23:22 +00:00
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
2012-05-05 21:39:08 +00:00
if ( ! is_autosuggest )
2012-02-26 02:54:49 +00:00
proc_push_interactive ( 0 ) ;
2012-11-19 00:30:30 +00:00
parser . eval_args ( args . c_str ( ) , possible_comp ) ;
2012-01-29 08:41:39 +00:00
2012-05-05 21:39:08 +00:00
if ( ! is_autosuggest )
2012-02-26 02:54:49 +00:00
proc_pop_interactive ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
this - > complete_strings ( str , desc . c_str ( ) , 0 , possible_comp , flags ) ;
2005-09-20 13:26:39 +00:00
}
/**
Match against an old style long option
*/
2012-11-19 00:30:30 +00:00
static int param_match_old ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
return ( optstr [ 0 ] = = L ' - ' ) & & ( e - > long_opt = = & optstr [ 1 ] ) ;
2005-09-20 13:26:39 +00:00
}
/**
Match a parameter
*/
2012-11-19 00:30:30 +00:00
static int param_match ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( e - > short_opt ! = L ' \0 ' & &
e - > short_opt = = optstr [ 1 ] )
return 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
if ( e - > long_opt = = & optstr [ 2 ] )
{
return 1 ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return 0 ;
2005-09-20 13:26:39 +00:00
}
/**
Test if a string is an option with an argument , like - - color = auto or - I / usr / include
*/
2012-11-19 00:30:30 +00:00
static wchar_t * param_match2 ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( e - > short_opt ! = L ' \0 ' & & e - > short_opt = = optstr [ 1 ] )
return ( wchar_t * ) & optstr [ 2 ] ;
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
size_t len = e - > long_opt . size ( ) ;
if ( wcsncmp ( e - > long_opt . c_str ( ) , & optstr [ 2 ] , len ) = = 0 )
{
if ( optstr [ len + 2 ] = = L ' = ' )
return ( wchar_t * ) & optstr [ len + 3 ] ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return 0 ;
2005-09-20 13:26:39 +00:00
}
/**
Tests whether a short option is a viable completion
*/
2012-11-19 00:30:30 +00:00
static int short_ok ( const wcstring & arg_str , wchar_t nextopt , const wcstring & allopt_str )
2005-09-20 13:26:39 +00:00
{
2012-02-08 22:47:50 +00:00
const wchar_t * arg = arg_str . c_str ( ) ;
const wchar_t * allopt = allopt_str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
const wchar_t * ptr ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( arg [ 0 ] ! = L ' - ' )
return arg [ 0 ] = = L ' \0 ' ;
if ( arg [ 1 ] = = L ' - ' )
return 0 ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( wcschr ( arg , nextopt ) ! = 0 )
return 0 ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
for ( ptr = arg + 1 ; * ptr ; ptr + + )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const wchar_t * tmp = wcschr ( allopt , * ptr ) ;
/* Unknown option */
if ( tmp = = 0 )
{
/*fwprintf( stderr, L"Unknown option %lc", *ptr );*/
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return 0 ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
if ( * ( tmp + 1 ) = = L ' : ' )
{
/* fwprintf( stderr, L"Woot %ls", allopt );*/
return 0 ;
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
return 1 ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void complete_load ( const wcstring & name , bool reload )
2006-02-08 09:20:05 +00:00
{
2012-11-19 00:30:30 +00:00
completion_autoloader . load ( name , reload ) ;
2006-02-08 09:20:05 +00:00
}
2005-09-20 13:26:39 +00:00
/**
Find completion for the argument str of command cmd_orig with
previous option popt . Insert results into comp_out . Return 0 if file
completion should be disabled , 1 otherwise .
*/
2012-11-19 00:30:30 +00:00
struct local_options_t
{
2012-05-12 01:59:38 +00:00
wcstring short_opt_str ;
option_list_t options ;
} ;
2012-11-19 00:30:30 +00:00
bool completer_t : : complete_param ( const wcstring & scmd_orig , const wcstring & spopt , const wcstring & sstr , bool use_switches )
2005-09-20 13:26:39 +00:00
{
2012-02-26 21:27:31 +00:00
const wchar_t * const cmd_orig = scmd_orig . c_str ( ) , * const popt = spopt . c_str ( ) , * const str = sstr . c_str ( ) ;
2012-11-19 00:30:30 +00:00
bool use_common = 1 , use_files = 1 ;
2005-09-20 13:26:39 +00:00
2012-02-08 22:47:50 +00:00
wcstring cmd , path ;
parse_cmd_string ( cmd_orig , path , cmd ) ;
2005-09-20 13:26:39 +00:00
2013-03-06 04:54:16 +00:00
if ( this - > type ( ) = = COMPLETE_DEFAULT )
2012-02-27 04:11:34 +00:00
{
2012-11-19 00:30:30 +00:00
complete_load ( cmd , true ) ;
2012-02-27 04:11:34 +00:00
}
2013-03-06 04:54:16 +00:00
else if ( this - > type ( ) = = COMPLETE_AUTOSUGGEST )
2012-02-27 04:11:34 +00:00
{
/* Maybe indicate we should try loading this on the main thread */
if ( ! list_contains_string ( this - > commands_to_load , cmd ) & & ! completion_autoloader . has_tried_loading ( cmd ) )
{
this - > commands_to_load . push_back ( cmd ) ;
}
}
2012-11-18 10:23:22 +00:00
2012-05-12 01:59:38 +00:00
/* Make a list of lists of all options that we care about */
std : : vector < local_options_t > all_options ;
{
scoped_lock lock ( completion_lock ) ;
scoped_lock lock2 ( completion_entry_lock ) ;
for ( completion_entry_set_t : : const_iterator iter = completion_set . begin ( ) ; iter ! = completion_set . end ( ) ; + + iter )
{
const completion_entry_t * i = * iter ;
const wcstring & match = i - > cmd_is_path ? path : cmd ;
if ( ! wildcard_match ( match , i - > cmd ) )
{
continue ;
}
2012-11-18 10:23:22 +00:00
2012-05-12 01:59:38 +00:00
/* Copy all of their options into our list */
all_options . push_back ( local_options_t ( ) ) ;
all_options . back ( ) . short_opt_str = i - > get_short_opt_str ( ) ;
all_options . back ( ) . options = i - > get_options ( ) ; //Oof, this is a lot of copying
}
}
2012-11-18 10:23:22 +00:00
2012-05-12 01:59:38 +00:00
/* Now release the lock and test each option that we captured above.
We have to do this outside the lock because callouts ( like the condition ) may add or remove completions .
See https : //github.com/ridiculousfish/fishfish/issues/2 */
2013-02-16 08:02:40 +00:00
for ( std : : vector < local_options_t > : : const_iterator iter = all_options . begin ( ) ; iter ! = all_options . end ( ) ; + + iter )
2012-05-12 01:59:38 +00:00
{
const option_list_t & options = iter - > options ;
2012-11-19 00:30:30 +00:00
use_common = 1 ;
if ( use_switches )
{
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( str [ 0 ] = = L ' - ' )
{
/* Check if we are entering a combined option and argument
( like - - color = auto or - I / usr / include ) */
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-11-19 00:30:30 +00:00
{
const complete_entry_opt_t * o = & * oiter ;
wchar_t * arg ;
if ( ( arg = param_match2 ( o , str ) ) ! = 0 & & this - > condition_test ( o - > condition ) )
{
2012-05-12 01:59:38 +00:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-11-19 00:30:30 +00:00
complete_from_args ( arg , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
}
else if ( popt [ 0 ] = = L ' - ' )
{
/* Set to true if we found a matching old-style switch */
int old_style_match = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
If we are using old style long options , check for them
first
*/
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-11-19 00:30:30 +00:00
{
2012-02-09 00:15:53 +00:00
const complete_entry_opt_t * o = & * oiter ;
2012-11-19 00:30:30 +00:00
if ( o - > old_mode )
{
if ( param_match_old ( o , popt ) & & this - > condition_test ( o - > condition ) )
{
old_style_match = 1 ;
2012-05-12 01:59:38 +00:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-11-19 00:30:30 +00:00
complete_from_args ( str , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
}
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
No old style option matched , or we are not using old
style options . We check if any short ( or gnu style
options do .
*/
if ( ! old_style_match )
{
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 00:15:53 +00:00
{
const complete_entry_opt_t * o = & * oiter ;
2012-11-19 00:30:30 +00:00
/*
Gnu - style options with _optional_ arguments must
be specified as a single token , so that it can
be differed from a regular argument .
*/
if ( ! o - > old_mode & & ! o - > long_opt . empty ( ) & & ! ( o - > result_mode & NO_COMMON ) )
continue ;
if ( param_match ( o , popt ) & & this - > condition_test ( o - > condition ) )
{
2012-05-12 01:59:38 +00:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-11-19 00:30:30 +00:00
complete_from_args ( str , o - > comp . c_str ( ) , o - > localized_desc ( ) , o - > flags ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
}
}
2012-11-18 10:23:22 +00:00
}
}
2006-01-30 19:53:10 +00:00
2012-11-19 00:30:30 +00:00
if ( use_common )
{
2005-09-20 13:26:39 +00:00
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 00:15:53 +00:00
{
const complete_entry_opt_t * o = & * oiter ;
2012-11-19 00:30:30 +00:00
/*
If this entry is for the base command ,
check if any of the arguments match
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! this - > condition_test ( o - > condition ) )
continue ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ( o - > short_opt = = L ' \0 ' ) & & ( o - > long_opt [ 0 ] = = L ' \0 ' ) )
{
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
complete_from_args ( str , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( wcslen ( str ) > 0 & & use_switches )
{
/*
Check if the short style option matches
*/
if ( o - > short_opt ! = L ' \0 ' & &
short_ok ( str , o - > short_opt , iter - > short_opt_str ) )
{
const wchar_t * desc = o - > localized_desc ( ) ;
wchar_t completion [ 2 ] ;
completion [ 0 ] = o - > short_opt ;
completion [ 1 ] = 0 ;
append_completion ( this - > completions , completion , desc , 0 ) ;
}
/*
Check if the long style option matches
*/
if ( o - > long_opt [ 0 ] ! = L ' \0 ' )
{
int match = 0 , match_no_case = 0 ;
2012-11-18 10:23:22 +00:00
2012-02-08 22:47:50 +00:00
wcstring whole_opt ;
whole_opt . append ( o - > old_mode ? L " - " : L " -- " ) ;
whole_opt . append ( o - > long_opt ) ;
2006-01-30 19:53:10 +00:00
2012-02-08 22:47:50 +00:00
match = string_prefixes_string ( str , whole_opt ) ;
2007-02-28 21:43:27 +00:00
2012-11-19 00:30:30 +00:00
if ( ! match )
{
match_no_case = wcsncasecmp ( str , whole_opt . c_str ( ) , wcslen ( str ) ) = = 0 ;
}
if ( match | | match_no_case )
{
int has_arg = 0 ; /* Does this switch have any known arguments */
int req_arg = 0 ; /* Does this switch _require_ an argument */
size_t offset = 0 ;
complete_flags_t flags = 0 ;
if ( match )
2013-03-06 04:54:16 +00:00
{
2012-11-19 00:30:30 +00:00
offset = wcslen ( str ) ;
2013-03-06 04:54:16 +00:00
}
2012-11-19 00:30:30 +00:00
else
2013-03-06 04:54:16 +00:00
{
2013-05-25 22:41:18 +00:00
flags = COMPLETE_REPLACES_TOKEN ;
2013-03-06 04:54:16 +00:00
}
2012-11-19 00:30:30 +00:00
has_arg = ! o - > comp . empty ( ) ;
req_arg = ( o - > result_mode & NO_COMMON ) ;
if ( ! o - > old_mode & & ( has_arg & & ! req_arg ) )
{
/*
Optional arguments to a switch can
only be handled using the ' = ' , so we
add it as a completion . By default
we avoid using ' = ' and instead rely
on ' - - switch switch - arg ' , since it
is more commonly supported by
homebrew getopt - like functions .
*/
wcstring completion = format_string ( L " %ls= " , whole_opt . c_str ( ) + offset ) ;
append_completion ( this - > completions ,
completion ,
C_ ( o - > desc . c_str ( ) ) ,
flags ) ;
}
append_completion ( this - > completions ,
whole_opt . c_str ( ) + offset ,
C_ ( o - > desc . c_str ( ) ) ,
flags ) ;
}
}
}
2012-11-18 10:23:22 +00:00
}
}
}
2012-11-19 00:30:30 +00:00
return use_files ;
2005-09-20 13:26:39 +00:00
}
/**
Perform file completion on the specified string
*/
2012-11-19 00:30:30 +00:00
void completer_t : : complete_param_expand ( const wcstring & sstr , bool do_file )
2005-09-20 13:26:39 +00:00
{
2012-02-26 21:27:31 +00:00
const wchar_t * const str = sstr . c_str ( ) ;
2012-11-19 00:30:30 +00:00
const wchar_t * comp_str ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( string_prefixes_string ( L " -- " , sstr ) & & ( comp_str = wcschr ( str , L ' = ' ) ) )
{
comp_str + + ;
}
else
{
comp_str = str ;
}
2012-11-18 10:23:22 +00:00
2013-09-15 06:59:53 +00:00
expand_flags_t flags = EXPAND_SKIP_CMDSUBST | ACCEPT_INCOMPLETE | this - > expand_flags ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-24 20:13:35 +00:00
if ( ! do_file )
flags | = EXPAND_SKIP_WILDCARDS ;
2012-11-18 10:23:22 +00:00
2012-08-16 01:20:44 +00:00
/* Squelch file descriptions per issue 254 */
2013-03-06 04:54:16 +00:00
if ( this - > type ( ) = = COMPLETE_AUTOSUGGEST | | do_file )
2012-02-24 20:13:35 +00:00
flags | = EXPAND_NO_DESCRIPTIONS ;
2012-11-18 10:23:22 +00:00
2013-09-15 06:59:53 +00:00
/* Don't do fuzzy matching for files if the string begins with a dash (#568). We could consider relaxing this if there was a preceding double-dash argument */
if ( string_prefixes_string ( L " - " , sstr ) )
flags & = ~ EXPAND_FUZZY_MATCH ;
2012-11-19 00:30:30 +00:00
if ( expand_string ( comp_str ,
this - > completions ,
2013-10-26 22:27:39 +00:00
flags ) = = EXPAND_ERROR )
2012-11-19 00:30:30 +00:00
{
debug ( 3 , L " Error while expanding string '%ls' " , comp_str ) ;
}
2012-05-05 21:21:21 +00:00
}
void completer_t : : debug_print_completions ( )
{
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < completions . size ( ) ; i + + )
{
2012-05-05 21:21:21 +00:00
printf ( " - Completion: %ls \n " , completions . at ( i ) . completion . c_str ( ) ) ;
}
2005-09-20 13:26:39 +00:00
}
/**
Complete the specified string as an environment variable
*/
2012-08-01 23:32:52 +00:00
bool completer_t : : complete_variable ( const wcstring & str , size_t start_offset )
2005-09-20 13:26:39 +00:00
{
2012-02-26 21:27:31 +00:00
const wchar_t * const whole_var = str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
const wchar_t * var = & whole_var [ start_offset ] ;
size_t varlen = wcslen ( var ) ;
2013-05-25 22:41:18 +00:00
bool res = false ;
2013-06-02 08:14:26 +00:00
2013-03-06 04:54:16 +00:00
const wcstring_list_t names = complete_get_variable_names ( ) ;
2012-11-19 00:30:30 +00:00
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
{
const wcstring & env_name = names . at ( i ) ;
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
string_fuzzy_match_t match = string_fuzzy_match_string ( var , env_name , this - > max_fuzzy_match_type ( ) ) ;
if ( match . type = = fuzzy_match_none )
{
// No match
2012-11-19 00:30:30 +00:00
continue ;
2013-05-25 22:41:18 +00:00
}
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
wcstring comp ;
int flags = 0 ;
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
if ( ! match_type_requires_full_replacement ( match . type ) )
2012-11-19 00:30:30 +00:00
{
2013-05-25 22:41:18 +00:00
// Take only the suffix
comp . append ( env_name . c_str ( ) + varlen ) ;
2012-11-19 00:30:30 +00:00
}
2013-05-25 22:41:18 +00:00
else
2012-11-19 00:30:30 +00:00
{
2013-05-25 22:41:18 +00:00
comp . append ( whole_var , start_offset ) ;
comp . append ( env_name ) ;
flags = COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE ;
}
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
wcstring desc ;
if ( this - > wants_descriptions ( ) )
{
env_var_t value_unescaped = env_get_string ( env_name ) ;
if ( value_unescaped . missing ( ) )
continue ;
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
wcstring value = expand_escape_variable ( value_unescaped ) ;
if ( this - > type ( ) ! = COMPLETE_AUTOSUGGEST )
desc = format_string ( COMPLETE_VAR_DESC_VAL , value . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
}
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
append_completion ( this - > completions , comp . c_str ( ) , desc . c_str ( ) , flags , match ) ;
2013-06-02 08:14:26 +00:00
2013-05-25 22:41:18 +00:00
res = true ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return res ;
2005-09-20 13:26:39 +00:00
}
/**
2006-02-19 18:19:32 +00:00
Search the specified string for the \ $ sign . If found , try to
2012-11-18 10:23:22 +00:00
complete as an environment variable .
2006-02-19 18:19:32 +00:00
\ return 0 if unable to complete , 1 otherwise
2005-09-20 13:26:39 +00:00
*/
2012-11-19 00:30:30 +00:00
bool completer_t : : try_complete_variable ( const wcstring & str )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
size_t i = str . size ( ) ;
while ( i - - )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
wchar_t c = str . at ( i ) ;
if ( c = = L ' $ ' )
{
/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/
return this - > complete_variable ( str , i + 1 ) ;
}
if ( ! isalnum ( c ) & & c ! = L ' _ ' )
{
return false ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
return false ;
2012-02-25 02:43:10 +00:00
}
2005-09-20 13:26:39 +00:00
/**
2006-02-19 18:19:32 +00:00
Try to complete the specified string as a username . This is used by
~ USER type expansion .
2005-09-20 13:26:39 +00:00
2006-02-19 18:19:32 +00:00
\ return 0 if unable to complete , 1 otherwise
*/
2012-11-19 00:30:30 +00:00
bool completer_t : : try_complete_user ( const wcstring & str )
2005-09-20 13:26:39 +00:00
{
2012-02-26 21:27:31 +00:00
const wchar_t * cmd = str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
const wchar_t * first_char = cmd ;
int res = 0 ;
double start_time = timef ( ) ;
if ( * first_char = = L ' ~ ' & & ! wcschr ( first_char , L ' / ' ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
const wchar_t * user_name = first_char + 1 ;
const wchar_t * name_end = wcschr ( user_name , L ' ~ ' ) ;
if ( name_end = = 0 )
{
struct passwd * pw ;
size_t name_len = wcslen ( user_name ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
setpwent ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( ( pw = getpwent ( ) ) ! = 0 )
{
double current_time = timef ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( current_time - start_time > 0.2 )
{
return 1 ;
}
2012-11-18 10:23:22 +00:00
2012-12-19 21:31:06 +00:00
if ( pw - > pw_name )
2012-11-19 00:30:30 +00:00
{
2012-12-19 21:31:06 +00:00
const wcstring pw_name_str = str2wcstring ( pw - > pw_name ) ;
const wchar_t * pw_name = pw_name_str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
if ( wcsncmp ( user_name , pw_name , name_len ) = = 0 )
{
2012-11-18 10:23:22 +00:00
wcstring desc = format_string ( COMPLETE_USER_DESC , pw_name ) ;
2012-11-19 00:30:30 +00:00
append_completion ( this - > completions ,
& pw_name [ name_len ] ,
desc ,
COMPLETE_NO_SPACE ) ;
res = 1 ;
}
else if ( wcsncasecmp ( user_name , pw_name , name_len ) = = 0 )
{
wcstring name = format_string ( L " ~%ls " , pw_name ) ;
2012-02-22 20:00:02 +00:00
wcstring desc = format_string ( COMPLETE_USER_DESC , pw_name ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_completion ( this - > completions ,
name ,
desc ,
2013-05-25 22:41:18 +00:00
COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ) ;
2012-11-19 00:30:30 +00:00
res = 1 ;
}
}
}
endpwent ( ) ;
2012-11-18 10:23:22 +00:00
}
}
2012-11-19 00:30:30 +00:00
return res ;
2009-02-02 22:46:45 +00:00
}
2007-02-09 09:33:50 +00:00
2013-03-06 04:54:16 +00:00
void complete ( const wcstring & cmd , std : : vector < completion_t > & comps , completion_request_flags_t flags , wcstring_list_t * commands_to_load )
2012-02-25 02:43:10 +00:00
{
/* Make our completer */
2013-03-06 04:54:16 +00:00
completer_t completer ( cmd , flags ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
const wchar_t * tok_begin , * tok_end , * cmdsubst_begin , * cmdsubst_end , * prev_begin , * prev_end ;
2012-11-23 19:12:22 +00:00
wcstring current_token , prev_token ;
2012-11-18 10:23:22 +00:00
wcstring current_command ;
2012-11-19 00:30:30 +00:00
int on_command = 0 ;
size_t pos ;
bool done = false ;
int use_command = 1 ;
int use_function = 1 ;
int use_builtin = 1 ;
int had_ddash = 0 ;
2012-11-18 10:23:22 +00:00
// debug( 1, L"Complete '%ls'", cmd );
2012-11-19 00:30:30 +00:00
size_t cursor_pos = cmd . size ( ) ;
2012-11-18 10:23:22 +00:00
2012-02-25 02:43:10 +00:00
const wchar_t * cmd_cstr = cmd . c_str ( ) ;
2012-11-19 00:30:30 +00:00
parse_util_cmdsubst_extent ( cmd_cstr , cursor_pos , & cmdsubst_begin , & cmdsubst_end ) ;
parse_util_token_extent ( cmd_cstr , cursor_pos , & tok_begin , & tok_end , & prev_begin , & prev_end ) ;
2012-02-25 02:43:10 +00:00
2012-11-19 00:30:30 +00:00
if ( ! cmdsubst_begin )
done = 1 ;
2012-02-25 02:43:10 +00:00
2012-11-19 00:30:30 +00:00
/**
If we are completing a variable name or a tilde expansion user
2012-11-23 19:12:22 +00:00
name , we do that and return . No need for any other completions .
2012-11-19 00:30:30 +00:00
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! done )
{
2012-02-25 02:43:10 +00:00
wcstring tmp = tok_begin ;
2012-11-19 00:30:30 +00:00
done = completer . try_complete_variable ( tmp ) | | completer . try_complete_user ( tmp ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( ! done )
{
pos = cursor_pos - ( cmdsubst_begin - cmd_cstr ) ;
2012-11-18 10:23:22 +00:00
2013-05-25 22:41:18 +00:00
const wcstring buff = wcstring ( cmdsubst_begin , cmdsubst_end - cmdsubst_begin ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int had_cmd = 0 ;
int end_loop = 0 ;
2012-11-23 19:12:22 +00:00
2012-11-22 01:48:35 +00:00
tokenizer_t tok ( buff . c_str ( ) , TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ) ;
2012-11-19 00:30:30 +00:00
while ( tok_has_next ( & tok ) & & ! end_loop )
{
switch ( tok_last_type ( & tok ) )
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_STRING :
2012-11-19 00:30:30 +00:00
{
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
const wcstring ncmd = tok_last ( & tok ) ;
int is_ddash = ( ncmd = = L " -- " ) & & ( ( tok_get_pos ( & tok ) + 2 ) < ( long ) pos ) ;
if ( ! had_cmd )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( parser_keywords_is_subcommand ( ncmd ) )
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( ncmd = = L " builtin " )
{
use_function = 0 ;
use_command = 0 ;
use_builtin = 1 ;
}
else if ( ncmd = = L " command " )
{
use_command = 1 ;
use_function = 0 ;
use_builtin = 0 ;
}
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
if ( ! is_ddash | |
( ( use_command & & use_function & & use_builtin ) ) )
{
current_command = ncmd ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
size_t token_end = tok_get_pos ( & tok ) + ncmd . size ( ) ;
2012-11-19 00:30:30 +00:00
2012-11-19 08:31:03 +00:00
on_command = ( pos < = token_end ) ;
had_cmd = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
}
else
2012-11-19 00:30:30 +00:00
{
2012-11-19 08:31:03 +00:00
if ( is_ddash )
{
had_ddash = 1 ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_END :
case TOK_PIPE :
case TOK_BACKGROUND :
{
had_cmd = 0 ;
had_ddash = 0 ;
use_command = 1 ;
use_function = 1 ;
use_builtin = 1 ;
break ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case TOK_ERROR :
{
end_loop = 1 ;
break ;
}
2013-10-26 22:27:39 +00:00
2013-09-30 20:57:36 +00:00
default :
{
break ;
}
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( tok_get_pos ( & tok ) > = ( long ) pos )
{
end_loop = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
tok_next ( & tok ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Get the string to complete
*/
2012-11-18 10:23:22 +00:00
2012-11-23 19:14:16 +00:00
current_token . assign ( tok_begin , cursor_pos - ( tok_begin - cmd_cstr ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-23 19:12:22 +00:00
if ( prev_begin )
{
prev_token . assign ( prev_begin , prev_end - prev_begin ) ;
}
else
{
prev_token . clear ( ) ;
}
2012-11-18 10:23:22 +00:00
// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
2012-11-19 00:30:30 +00:00
/*
Check if we are using the ' command ' or ' builtin ' builtins
_and_ we are writing a switch instead of a command . In that
case , complete using the builtins completions , not using a
subcommand .
*/
2012-11-18 10:23:22 +00:00
2012-11-23 19:12:22 +00:00
if ( ( on_command | | current_token = = L " -- " ) & &
string_prefixes_string ( L " - " , current_token ) & &
2012-11-19 00:30:30 +00:00
! ( use_command & & use_function & & use_builtin ) )
{
if ( use_command = = 0 )
current_command = L " builtin " ;
else
current_command = L " command " ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
had_cmd = 1 ;
on_command = 0 ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Use command completions if in between commands
*/
if ( ! had_cmd )
{
on_command = 1 ;
}
2012-11-18 10:23:22 +00:00
2012-11-23 19:12:22 +00:00
if ( on_command )
2012-11-19 00:30:30 +00:00
{
2012-11-23 19:12:22 +00:00
/* Complete command filename */
completer . complete_cmd ( current_token , use_function , use_builtin , use_command ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-23 19:12:22 +00:00
else
2012-11-19 00:30:30 +00:00
{
2012-11-23 19:22:58 +00:00
bool do_file = false ;
2012-11-18 10:23:22 +00:00
2012-11-23 19:12:22 +00:00
wcstring current_command_unescape = current_command ;
wcstring prev_token_unescape = prev_token ;
wcstring current_token_unescape = current_token ;
if ( unescape_string ( current_command_unescape , 0 ) & &
unescape_string ( prev_token_unescape , 0 ) & &
unescape_string ( current_token_unescape , UNESCAPE_INCOMPLETE ) )
2012-11-19 00:30:30 +00:00
{
2012-11-23 19:12:22 +00:00
do_file = completer . complete_param ( current_command_unescape ,
prev_token_unescape ,
current_token_unescape ,
! had_ddash ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-23 19:22:58 +00:00
/* If we have found no command specific completions at
2012-11-23 19:12:22 +00:00
all , fall back to using file completions .
*/
if ( completer . empty ( ) )
2012-11-23 19:22:58 +00:00
do_file = true ;
/* But if we are planning on loading commands, don't do file completions.
See https : //github.com/fish-shell/fish-shell/issues/378 */
if ( commands_to_load ! = NULL & & completer . has_commands_to_load ( ) )
do_file = false ;
2013-01-04 21:09:01 +00:00
2012-12-26 23:28:25 +00:00
/* And if we're autosuggesting, and the token is empty, don't do file suggestions */
2013-03-06 04:54:16 +00:00
if ( ( flags & COMPLETION_REQUEST_AUTOSUGGESTION ) & & current_token_unescape . empty ( ) )
2012-12-26 23:28:25 +00:00
do_file = false ;
2013-01-04 21:09:01 +00:00
2012-11-23 19:12:22 +00:00
/*
This function wants the unescaped string
*/
completer . complete_param_expand ( current_token , do_file ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
comps = completer . get_completions ( ) ;
2012-02-27 04:11:34 +00:00
completer . get_commands_to_load ( commands_to_load ) ;
2012-02-25 02:43:10 +00:00
}
2012-01-16 16:56:47 +00:00
2005-10-24 15:26:25 +00:00
/**
Print the GNU longopt style switch \ c opt , and the argument \ c
argument to the specified stringbuffer , but only if arguemnt is
non - null and longer than 0 characters .
*/
2012-11-19 00:30:30 +00:00
static void append_switch ( wcstring & out ,
const wcstring & opt ,
const wcstring & argument )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
if ( argument . empty ( ) )
return ;
2005-09-20 13:26:39 +00:00
2012-11-19 00:30:30 +00:00
wcstring esc = escape_string ( argument , 1 ) ;
append_format ( out , L " --%ls %ls " , opt . c_str ( ) , esc . c_str ( ) ) ;
2005-09-20 13:26:39 +00:00
}
2012-11-19 00:30:30 +00:00
void complete_print ( wcstring & out )
2005-09-20 13:26:39 +00:00
{
2012-02-26 21:27:31 +00:00
scoped_lock locker ( completion_lock ) ;
2012-02-26 22:32:06 +00:00
scoped_lock locker2 ( completion_entry_lock ) ;
2012-11-18 10:23:22 +00:00
2012-04-12 01:25:37 +00:00
// Get a list of all completions in a vector, then sort it by order
std : : vector < const completion_entry_t * > all_completions ( completion_set . begin ( ) , completion_set . end ( ) ) ;
sort ( all_completions . begin ( ) , all_completions . end ( ) , compare_completions_by_order ) ;
2012-11-18 10:23:22 +00:00
2012-04-12 01:25:37 +00:00
for ( std : : vector < const completion_entry_t * > : : const_iterator iter = all_completions . begin ( ) ; iter ! = all_completions . end ( ) ; + + iter )
2012-02-08 22:47:50 +00:00
{
2012-02-15 19:33:41 +00:00
const completion_entry_t * e = * iter ;
2012-02-26 22:32:06 +00:00
const option_list_t options = e - > get_options ( ) ;
2012-03-01 22:56:34 +00:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 00:15:53 +00:00
{
const complete_entry_opt_t * o = & * oiter ;
2012-11-19 00:30:30 +00:00
const wchar_t * modestr [ ] =
{
L " " ,
L " --no-files " ,
L " --require-parameter " ,
L " --exclusive "
}
;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_format ( out ,
L " complete%ls " ,
modestr [ o - > result_mode ] ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_switch ( out ,
e - > cmd_is_path ? L " path " : L " command " ,
e - > cmd ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( o - > short_opt ! = 0 )
{
append_format ( out ,
L " --short-option '%lc' " ,
o - > short_opt ) ;
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_switch ( out ,
o - > old_mode ? L " old-option " : L " long-option " ,
o - > long_opt ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_switch ( out ,
L " description " ,
C_ ( o - > desc . c_str ( ) ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_switch ( out ,
L " arguments " ,
o - > comp ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
append_switch ( out ,
L " condition " ,
o - > condition ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
out . append ( L " \n " ) ;
}
2012-11-18 10:23:22 +00:00
}
2005-09-20 13:26:39 +00:00
}