2006-11-29 22:21:02 +08:00
/** \file complete.c Functions related to tab-completion.
2005-09-20 21:26:39 +08:00
2009-02-03 06:46:45 +08:00
These functions are used for storing and retrieving tab - completion data , as well as for performing tab - completion .
2005-09-20 21:26:39 +08:00
*/
2006-08-11 09:18:35 +08:00
# include "config.h"
2005-09-20 21:26:39 +08: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 23:29:09 +08:00
# include <wchar.h>
2012-02-25 04:13:35 +08:00
# include <pthread.h>
2012-04-24 10:29:44 +08:00
# include <algorithm>
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08: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-31 00:51:50 +08:00
# include "parse_util.h"
2007-04-22 17:50:26 +08:00
# include "parser_keywords.h"
2005-09-20 21:26:39 +08:00
# include "wutil.h"
2006-10-19 19:50:23 +08:00
# include "path.h"
2012-01-24 03:42:41 +08:00
# include "builtin_scripts.h"
2005-09-20 21:26:39 +08:00
/*
2006-02-04 05:30:06 +08:00
Completion description strings , mostly for different types of files , such as sockets , block devices , etc .
2005-09-20 21:26:39 +08:00
2006-02-04 05:30:06 +08: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 21:26:39 +08:00
*/
/**
Description for ~ USER completion
*/
2008-01-14 03:32:21 +08:00
# define COMPLETE_USER_DESC _( L"Home for %ls" )
2005-09-20 21:26:39 +08:00
/**
Description for short variables . The value is concatenated to this description
*/
2007-02-17 19:05:55 +08:00
# define COMPLETE_VAR_DESC_VAL _( L"Variable: %ls" )
2005-09-20 21:26:39 +08: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-04 05:30:06 +08: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 21:26:39 +08:00
*/
# define CC_NOT_TESTED 0
/**
2006-02-04 05:30:06 +08: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 21:26:39 +08:00
*/
# define CC_TRUE L"true"
/**
2006-02-04 05:30:06 +08: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 21:26:39 +08:00
*/
# define CC_FALSE L"false"
2006-06-08 07:56:01 +08: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 22:18:22 +08:00
# ifdef USE_GETTEXT
2007-10-14 05:26:06 +08:00
# define C_(wstr) ((wcscmp(wstr, L"")==0)?L"":wgettext(wstr))
2006-11-29 22:18:22 +08:00
# else
# define C_(string) (string)
# endif
2006-06-08 07:56:01 +08:00
2009-02-03 06:46:45 +08:00
/**
The maximum amount of time that we ' re willing to spend doing
username tilde completion . This special limit has been coded in
because user lookup can be extremely slow in cases of a humongous
LDAP database . ( Google , I ' m looking at you )
*/
# define MAX_USER_LOOKUP_TIME 0.2
2005-09-20 21:26:39 +08:00
/**
2006-01-31 03:53:10 +08:00
Struct describing a completion option entry .
2005-09-20 21:26:39 +08: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-04 05:30:06 +08:00
of non - switch arguments that may only follow directly after the
2011-12-27 11:18:46 +08:00
specified switch .
2005-09-20 21:26:39 +08:00
*/
typedef struct complete_entry_opt
{
/** Short style option */
wchar_t short_opt ;
/** Long style option */
2012-02-09 08:15:53 +08:00
wcstring long_opt ;
2005-09-20 21:26:39 +08:00
/** Arguments to the option */
2012-02-09 08:15:53 +08:00
wcstring comp ;
2005-09-20 21:26:39 +08:00
/** Description of the completion */
2012-02-09 08:15:53 +08:00
wcstring desc ;
2005-09-20 21:26:39 +08:00
/** Condition under which to use the option */
2012-02-09 08:15:53 +08:00
wcstring condition ;
2006-02-04 05:30:06 +08:00
/** 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 . */
2005-09-20 21:26:39 +08:00
int result_mode ;
/** True if old style long options are used */
int old_mode ;
2007-03-01 05:43:27 +08:00
/** Completion flags */
2012-02-26 10:54:49 +08:00
complete_flags_t flags ;
2012-02-09 08:15:53 +08:00
const wchar_t * localized_desc ( ) const
{
const wchar_t * tmp = desc . c_str ( ) ;
return C_ ( tmp ) ;
}
} complete_entry_opt_t ;
2005-09-20 21:26:39 +08:00
2012-04-12 09:25:37 +08:00
/* Last value used in the order field of completion_entry_t */
static unsigned int kCompleteOrder = 0 ;
2005-09-20 21:26:39 +08:00
/**
Struct describing a command completion
*/
2012-02-09 08:15:53 +08:00
typedef std : : list < complete_entry_opt_t > option_list_t ;
2012-02-27 05:27:31 +08:00
class completion_entry_t
2012-05-12 09:59:38 +08:00
{
public :
2012-02-27 06:32:06 +08:00
/** List of all options */
option_list_t options ;
2012-02-27 05:27:31 +08:00
2005-09-20 21:26:39 +08:00
/** String containing all short option characters */
2012-02-09 06:47:50 +08:00
wcstring short_opt_str ;
2012-02-09 08:15:53 +08:00
2012-02-27 06:32:06 +08:00
public :
/** Command string */
const wcstring cmd ;
2012-02-09 08:15:53 +08:00
2012-02-27 06:32:06 +08:00
/** True if command is a path */
const bool cmd_is_path ;
2012-04-12 09:25:37 +08:00
2005-09-20 21:26:39 +08:00
/** True if no other options than the ones supplied are possible */
2012-02-27 05:27:31 +08:00
bool authoritative ;
2012-04-12 09:25:37 +08:00
/** Order for when this completion was created. This aids in outputting completions sorted by time. */
const unsigned int order ;
2012-02-27 06:32:06 +08:00
/** Getters for option list. */
const option_list_t & get_options ( ) const ;
2012-05-12 09:59:38 +08: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-02-27 06:32:06 +08:00
/** Getter for short_opt_str. */
wcstring & get_short_opt_str ( ) ;
const wcstring & get_short_opt_str ( ) const ;
2012-02-27 05:27:31 +08:00
completion_entry_t ( const wcstring & c , bool type , const wcstring & options , bool author ) :
2012-02-27 06:32:06 +08:00
short_opt_str ( options ) ,
2012-02-27 05:27:31 +08:00
cmd ( c ) ,
cmd_is_path ( type ) ,
2012-04-12 09:25:37 +08:00
authoritative ( author ) ,
order ( + + kCompleteOrder )
2012-02-27 05:27:31 +08:00
{
}
2012-02-09 06:47:50 +08:00
} ;
2005-09-20 21:26:39 +08:00
2012-04-10 11:17:06 +08:00
/** Set of all completion entries */
struct completion_entry_set_comparer {
/** Comparison for std::set */
bool operator ( ) ( completion_entry_t * p1 , completion_entry_t * p2 ) const {
/* Paths always come last for no particular reason */
if ( p1 - > cmd_is_path ! = p2 - > cmd_is_path ) {
return p1 - > cmd_is_path < p2 - > cmd_is_path ;
} else {
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-27 06:32:06 +08:00
2012-04-12 09:25:37 +08:00
// Comparison function to sort completions by their order field
static bool compare_completions_by_order ( const completion_entry_t * p1 , const completion_entry_t * p2 ) {
return p1 - > order < p2 - > order ;
}
2012-02-27 06:32:06 +08:00
/** The lock that guards the list of completion entries */
2012-02-25 04:13:35 +08:00
static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER ;
2005-09-20 21:26:39 +08:00
2012-02-27 06:32:06 +08: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. */
static pthread_mutex_t completion_entry_lock = PTHREAD_MUTEX_INITIALIZER ;
2012-05-12 09:59:38 +08:00
void completion_entry_t : : add_option ( const complete_entry_opt_t & opt ) {
2012-02-27 06:32:06 +08:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
2012-05-12 09:59:38 +08:00
options . push_front ( opt ) ;
2012-02-27 06:32:06 +08:00
}
const option_list_t & completion_entry_t : : get_options ( ) const {
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
return options ;
}
wcstring & completion_entry_t : : get_short_opt_str ( ) {
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
return short_opt_str ;
}
const wcstring & completion_entry_t : : get_short_opt_str ( ) const {
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
return short_opt_str ;
}
2005-09-20 21:26:39 +08:00
2012-07-18 03:47:01 +08:00
/* completion_t functions */
completion_t : : completion_t ( const wcstring & comp , const wcstring & desc , int flags_val ) : completion ( comp ) , description ( desc ) , flags ( flags_val )
{
2012-07-18 22:18:19 +08:00
if ( flags & COMPLETE_AUTO_SPACE )
{
flags = flags & ~ COMPLETE_AUTO_SPACE ;
size_t len = completion . size ( ) ;
if ( len > 0 & & ( wcschr ( L " /=@: " , comp . at ( len - 1 ) ) ! = 0 ) )
flags | = COMPLETE_NO_SPACE ;
}
2012-07-18 03:47:01 +08:00
}
completion_t : : completion_t ( const completion_t & him ) : completion ( him . completion ) , description ( him . description ) , flags ( him . flags )
{
}
completion_t & completion_t : : operator = ( const completion_t & him )
{
if ( this ! = & him )
{
this - > completion = him . completion ;
this - > description = him . description ;
this - > flags = him . flags ;
}
return * this ;
}
2012-08-22 08:18:52 +08:00
bool completion_t : : operator < ( const completion_t & rhs ) const
{
return this - > completion < rhs . completion ;
}
bool completion_t : : operator = = ( const completion_t & rhs ) const
{
return this - > completion = = rhs . completion ;
}
bool completion_t : : operator ! = ( const completion_t & rhs ) const
{
return ! ( * this = = rhs ) ;
}
2012-07-18 03:47:01 +08:00
wcstring_list_t completions_to_wcstring_list ( const std : : vector < completion_t > & list )
{
wcstring_list_t strings ;
strings . reserve ( list . size ( ) ) ;
for ( std : : vector < completion_t > : : const_iterator iter = list . begin ( ) ; iter ! = list . end ( ) ; + + iter ) {
strings . push_back ( iter - > completion ) ;
}
return strings ;
}
void sort_completions ( std : : vector < completion_t > & completions )
{
std : : sort ( completions . begin ( ) , completions . end ( ) ) ;
}
2012-02-25 10:43:10 +08:00
/** Class representing an attempt to compute completions */
class completer_t {
const complete_type_t type ;
2012-02-27 05:27:31 +08:00
const wcstring initial_cmd ;
2012-02-25 10:43:10 +08:00
std : : vector < completion_t > completions ;
2012-02-27 12:11:34 +08:00
wcstring_list_t commands_to_load ;
2012-02-25 10:43:10 +08:00
2012-02-27 05:27:31 +08: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 ;
2012-02-25 10:43:10 +08:00
public :
completer_t ( const wcstring & c , complete_type_t t ) :
type ( t ) ,
2012-02-27 12:11:34 +08:00
initial_cmd ( c )
2012-02-25 10:43:10 +08:00
{
}
2012-02-26 10:54:49 +08:00
bool empty ( ) const { return completions . empty ( ) ; }
const std : : vector < completion_t > & get_completions ( void ) { return completions ; }
2012-02-25 10:43:10 +08:00
bool try_complete_variable ( const wcstring & str ) ;
bool try_complete_user ( const wcstring & str ) ;
bool complete_param ( const wcstring & cmd_orig ,
const wcstring & popt ,
const wcstring & str ,
bool use_switches ) ;
2012-02-26 10:54:49 +08:00
void complete_param_expand ( const wcstring & str , bool do_file ) ;
2012-02-25 10:43:10 +08:00
2012-05-06 05:21:21 +08:00
void debug_print_completions ( ) ;
2012-02-25 10:43:10 +08:00
void complete_cmd ( const wcstring & str ,
2012-02-26 10:54:49 +08:00
bool use_function ,
bool use_builtin ,
bool use_command ) ;
2012-02-27 05:27:31 +08:00
void complete_from_args ( const wcstring & str ,
const wcstring & args ,
const wcstring & desc ,
complete_flags_t flags ) ;
void complete_cmd_desc ( const wcstring & str ) ;
2012-02-25 10:43:10 +08:00
2012-08-02 07:32:52 +08:00
bool complete_variable ( const wcstring & str , size_t start_offset ) ;
2012-02-27 05:27:31 +08:00
bool condition_test ( const wcstring & condition ) ;
2012-02-27 12:11:34 +08:00
2012-06-30 07:40:54 +08: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-03-10 12:16:26 +08:00
expand_flags_t expand_flags ( ) const {
2012-08-07 15:01:48 +08: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 12:16:26 +08:00
expand_flags_t result = 0 ;
if ( type = = COMPLETE_AUTOSUGGEST )
2012-08-07 15:01:48 +08:00
result | = EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_JOBS ;
2012-03-10 12:16:26 +08:00
return result ;
}
2012-02-27 12:11:34 +08:00
void get_commands_to_load ( wcstring_list_t * lst ) {
if ( lst )
lst - > insert ( lst - > end ( ) , commands_to_load . begin ( ) , commands_to_load . end ( ) ) ;
}
2012-02-25 10:43:10 +08:00
} ;
2012-01-26 10:40:08 +08:00
/* Autoloader for completions */
class completion_autoload_t : public autoload_t {
public :
completion_autoload_t ( ) ;
virtual void command_removed ( const wcstring & cmd ) ;
} ;
static completion_autoload_t completion_autoloader ;
/** Constructor */
completion_autoload_t : : completion_autoload_t ( ) : autoload_t ( L " fish_complete_path " ,
internal_completion_scripts ,
sizeof internal_completion_scripts / sizeof * internal_completion_scripts )
{
}
/** Callback when an autoloaded completion is removed */
void completion_autoload_t : : command_removed ( const wcstring & cmd ) {
complete_remove ( cmd . c_str ( ) , COMMAND , 0 , 0 ) ;
}
2012-01-24 03:42:41 +08:00
2007-02-17 19:05:55 +08:00
/**
Create a new completion entry
*/
2012-07-18 03:47:01 +08:00
void append_completion ( std : : vector < completion_t > & completions , const wcstring & comp , const wcstring & desc , complete_flags_t flags )
2007-02-17 19:05:55 +08:00
{
2012-02-02 08:27:14 +08:00
completions . push_back ( completion_t ( comp , desc , flags ) ) ;
2007-02-17 19:05:55 +08:00
}
2005-09-20 21:26:39 +08: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-02-27 05:27:31 +08:00
bool completer_t : : condition_test ( const wcstring & condition )
2012-02-25 10:43:10 +08:00
{
2012-02-09 08:15:53 +08:00
if ( condition . empty ( ) )
2005-09-20 21:26:39 +08:00
{
// fwprintf( stderr, L"No condition specified\n" );
return 1 ;
}
2012-02-25 10:43:10 +08:00
2012-02-27 05:27:31 +08:00
if ( this - > type = = COMPLETE_AUTOSUGGEST )
2012-02-25 10:43:10 +08:00
{
/* Autosuggestion can't support conditions */
return 0 ;
}
ASSERT_IS_MAIN_THREAD ( ) ;
2012-02-09 08:15:53 +08:00
bool test_res ;
2012-02-27 05:27:31 +08:00
condition_cache_t : : iterator cached_entry = condition_cache . find ( condition ) ;
2012-02-09 08:15:53 +08:00
if ( cached_entry = = condition_cache . end ( ) ) {
/* Compute new value and reinsert it */
test_res = ( 0 = = exec_subshell ( condition ) ) ;
condition_cache [ condition ] = test_res ;
} else {
/* Use the old value */
test_res = cached_entry - > second ;
}
return test_res ;
2005-09-20 21:26:39 +08:00
}
2012-02-25 04:13:35 +08:00
/** Search for an exactly matching completion entry. Must be called while locked. */
2012-08-07 14:34:55 +08:00
static completion_entry_t * complete_find_exact_entry ( const wcstring & cmd , const bool cmd_is_path )
2005-09-20 21:26:39 +08:00
{
2012-02-25 04:13:35 +08:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-04-10 11:17:06 +08: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 ) ;
if ( iter ! = completion_set . end ( ) ) {
result = * iter ;
}
return result ;
2005-09-20 21:26:39 +08:00
}
2012-02-25 04:13:35 +08:00
/** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */
2012-08-07 14:34:55 +08:00
static completion_entry_t * complete_get_exact_entry ( const wcstring & cmd , bool cmd_is_path )
2005-09-20 21:26:39 +08:00
{
2012-02-25 04:13:35 +08:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-02-09 06:47:50 +08:00
completion_entry_t * c ;
2006-10-19 19:50:23 +08:00
2012-02-27 05:27:31 +08:00
c = complete_find_exact_entry ( cmd , cmd_is_path ) ;
2006-06-08 08:01:45 +08:00
2012-02-09 06:47:50 +08:00
if ( c = = NULL )
2005-09-20 21:26:39 +08:00
{
2012-08-07 14:34:55 +08:00
c = new completion_entry_t ( cmd , cmd_is_path , L " " , false ) ;
2012-04-10 11:17:06 +08:00
completion_set . insert ( c ) ;
2005-09-20 21:26:39 +08:00
}
2007-01-28 21:40:59 +08:00
return c ;
}
2012-02-27 05:27:31 +08:00
void complete_set_authoritative ( const wchar_t * cmd , bool cmd_is_path , bool authoritative )
2007-01-28 21:40:59 +08:00
{
2012-02-09 06:47:50 +08:00
completion_entry_t * c ;
2007-01-28 21:40:59 +08:00
CHECK ( cmd , ) ;
2012-02-25 04:13:35 +08:00
scoped_lock lock ( completion_lock ) ;
2012-02-27 05:27:31 +08:00
c = complete_get_exact_entry ( cmd , cmd_is_path ) ;
2007-08-02 01:35:24 +08:00
c - > authoritative = authoritative ;
2007-01-28 21:40:59 +08:00
}
2007-01-28 11:24:16 +08:00
2007-01-28 21:40:59 +08:00
void complete_add ( const wchar_t * cmd ,
2012-02-27 05:27:31 +08:00
bool cmd_is_path ,
2007-01-28 21:40:59 +08:00
wchar_t short_opt ,
const wchar_t * long_opt ,
int old_mode ,
int result_mode ,
const wchar_t * condition ,
const wchar_t * comp ,
2007-03-01 05:43:27 +08:00
const wchar_t * desc ,
2012-02-26 10:54:49 +08:00
complete_flags_t flags )
2007-01-28 21:40:59 +08:00
{
CHECK ( cmd , ) ;
2012-02-25 04:13:35 +08:00
2012-02-27 06:32:06 +08:00
/* Lock the lock that allows us to edit the completion entry list */
2012-02-25 04:13:35 +08:00
scoped_lock lock ( completion_lock ) ;
2012-02-09 08:15:53 +08:00
2012-02-27 06:32:06 +08:00
/* Lock the lock that allows us to edit individual completion entries */
scoped_lock lock2 ( completion_entry_lock ) ;
2012-05-12 09:59:38 +08:00
completion_entry_t * c ;
c = complete_get_exact_entry ( cmd , cmd_is_path ) ;
/* Create our new option */
complete_entry_opt_t opt ;
2007-01-28 21:40:59 +08:00
if ( short_opt ! = L ' \0 ' )
{
int len = 1 + ( ( result_mode & NO_COMMON ) ! = 0 ) ;
2012-02-09 06:47:50 +08:00
2012-02-27 06:32:06 +08:00
c - > get_short_opt_str ( ) . push_back ( short_opt ) ;
2007-01-28 21:40:59 +08:00
if ( len = = 2 )
2006-02-20 02:19:32 +08:00
{
2012-02-27 06:32:06 +08:00
c - > get_short_opt_str ( ) . push_back ( L ' : ' ) ;
2006-02-20 02:19:32 +08:00
}
2007-01-28 21:40:59 +08:00
}
2011-12-27 11:18:46 +08:00
2012-02-09 08:15:53 +08:00
opt . short_opt = short_opt ;
opt . result_mode = result_mode ;
opt . old_mode = old_mode ;
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-05-12 09:59:38 +08:00
c - > add_option ( opt ) ;
2005-09-20 21:26:39 +08:00
}
2006-12-14 07:58:38 +08:00
/**
Remove all completion options in the specified entry that match the
2012-02-09 06:47:50 +08:00
specified short / long option strings . Returns true if it is now
2012-02-25 04:13:35 +08:00
empty and should be deleted , false if it ' s not empty . Must be called while locked .
2006-12-14 07:58:38 +08:00
*/
2012-05-12 09:59:38 +08:00
bool completion_entry_t : : remove_option ( wchar_t short_opt , const wchar_t * long_opt )
2005-09-20 21:26:39 +08:00
{
2012-02-25 04:13:35 +08:00
ASSERT_IS_LOCKED ( completion_lock ) ;
2012-02-27 06:32:06 +08:00
ASSERT_IS_LOCKED ( completion_entry_lock ) ;
2006-12-14 07:58:38 +08:00
if ( ( short_opt = = 0 ) & & ( long_opt = = 0 ) )
2005-09-20 21:26:39 +08:00
{
2012-05-12 09:59:38 +08:00
this - > options . clear ( ) ;
2006-12-14 07:58:38 +08:00
}
else
{
2012-05-12 09:59:38 +08:00
for ( option_list_t : : iterator iter = this - > options . begin ( ) ; iter ! = this - > options . end ( ) ; )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
complete_entry_opt_t & o = * iter ;
if ( short_opt = = o . short_opt | | long_opt = = o . long_opt )
2005-09-20 21:26:39 +08:00
{
2006-12-14 07:58:38 +08:00
/* fwprintf( stderr,
2009-02-03 06:46:45 +08:00
L " remove option -%lc --%ls \n " ,
o - > short_opt ? o - > short_opt : L ' ' ,
o - > long_opt ) ;
2006-12-14 07:58:38 +08:00
*/
2012-02-09 08:15:53 +08:00
if ( o . short_opt )
2005-09-20 21:26:39 +08:00
{
2012-05-12 09:59:38 +08:00
wcstring & short_opt_str = this - > get_short_opt_str ( ) ;
2012-02-27 06:32:06 +08:00
size_t idx = short_opt_str . find ( o . short_opt ) ;
2012-02-09 06:47:50 +08:00
if ( idx ! = wcstring : : npos )
{
/* Consume all colons */
size_t first_non_colon = idx + 1 ;
2012-02-27 06:32:06 +08:00
while ( first_non_colon < short_opt_str . size ( ) & & short_opt_str . at ( first_non_colon ) = = L ' : ' )
2012-02-09 06:47:50 +08:00
first_non_colon + + ;
2012-02-27 06:32:06 +08:00
short_opt_str . erase ( idx , first_non_colon - idx ) ;
2012-02-09 06:47:50 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-02-09 08:15:53 +08:00
/* Destroy this option and go to the next one */
2012-05-12 09:59:38 +08:00
iter = this - > options . erase ( iter ) ;
2005-09-20 21:26:39 +08:00
}
2006-12-14 07:58:38 +08:00
else
{
2012-02-09 08:15:53 +08:00
/* Just go to the next one */
2012-03-02 06:56:34 +08:00
+ + iter ;
2006-12-14 07:58:38 +08:00
}
}
}
2012-05-12 09:59:38 +08:00
return this - > options . empty ( ) ;
2006-12-14 07:58:38 +08:00
}
void complete_remove ( const wchar_t * cmd ,
2012-02-27 05:27:31 +08:00
bool cmd_is_path ,
2006-12-14 07:58:38 +08:00
wchar_t short_opt ,
const wchar_t * long_opt )
{
CHECK ( cmd , ) ;
2012-02-25 04:13:35 +08:00
scoped_lock lock ( completion_lock ) ;
2012-04-25 02:01:04 +08:00
scoped_lock lock2 ( completion_entry_lock ) ;
2012-04-10 11:17:06 +08:00
completion_entry_t tmp_entry ( cmd , cmd_is_path , L " " , false ) ;
completion_entry_set_t : : iterator iter = completion_set . find ( & tmp_entry ) ;
if ( iter ! = completion_set . end ( ) ) {
completion_entry_t * entry = * iter ;
2012-05-12 09:59:38 +08:00
bool delete_it = entry - > remove_option ( short_opt , long_opt ) ;
2012-02-09 06:47:50 +08:00
if ( delete_it ) {
/* Delete this entry */
2012-04-10 11:17:06 +08:00
completion_set . erase ( iter ) ;
delete entry ;
2012-02-09 06:47:50 +08:00
}
2012-04-10 11:17:06 +08:00
}
2005-09-20 21:26:39 +08:00
}
2012-02-11 09:54:21 +08:00
/* Formats an error string by prepending the prefix and then appending the str in single quotes */
static wcstring format_error ( const wchar_t * prefix , const wcstring & str ) {
wcstring result = prefix ;
result . push_back ( L ' \' ' ) ;
result . append ( str ) ;
result . push_back ( L ' \' ' ) ;
return result ;
}
2012-02-08 14:44:10 +08:00
/**
Find the full path and commandname from a command string ' str ' .
*/
static void parse_cmd_string ( const wcstring & str , wcstring & path , wcstring & cmd ) {
2012-07-21 11:39:31 +08:00
if ( ! path_get_path ( str , & path ) ) {
2012-02-08 14:44:10 +08:00
/** Use the empty string as the 'path' for commands that can not be found. */
path = L " " ;
}
/* Make sure the path is not included in the command */
size_t last_slash = str . find_last_of ( L ' / ' ) ;
if ( last_slash ! = wcstring : : npos ) {
cmd = str . substr ( last_slash + 1 ) ;
} else {
cmd = str ;
}
}
2012-08-07 14:34:55 +08:00
int complete_is_valid_option ( const wcstring & str ,
const wcstring & opt ,
2012-02-11 09:54:21 +08:00
wcstring_list_t * errors ,
2012-01-16 06:24:58 +08:00
bool allow_autoload )
2005-09-20 21:26:39 +08:00
{
2012-02-08 14:44:10 +08:00
wcstring cmd , path ;
2012-08-07 14:34:55 +08:00
bool found_match = false ;
bool authoritative = true ;
2005-09-20 21:26:39 +08:00
int opt_found = 0 ;
2012-02-09 08:15:53 +08:00
std : : set < wcstring > gnu_match_set ;
2012-08-07 14:34:55 +08:00
bool is_gnu_opt = false ;
bool is_old_opt = false ;
bool is_short_opt = false ;
bool is_gnu_exact = false ;
2012-03-26 16:21:10 +08:00
size_t gnu_opt_len = 0 ;
2012-02-08 14:44:10 +08:00
2012-08-07 14:34:55 +08:00
if ( opt . empty ( ) )
return false ;
std : : vector < bool > short_validated ;
2005-09-20 21:26:39 +08:00
/*
Check some generic things like - - and - options .
*/
2012-08-07 14:34:55 +08:00
switch ( opt . size ( ) )
2005-09-20 21:26:39 +08:00
{
case 0 :
case 1 :
2006-02-20 02:19:32 +08:00
{
2012-08-07 14:34:55 +08:00
return true ;
2006-02-20 02:19:32 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
case 2 :
2006-02-20 02:19:32 +08:00
{
2012-08-07 14:34:55 +08:00
if ( opt = = L " -- " )
2006-02-20 02:19:32 +08:00
{
2012-08-07 14:34:55 +08:00
return true ;
2006-02-20 02:19:32 +08:00
}
break ;
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2012-08-07 14:34:55 +08:00
if ( opt . at ( 0 ) ! = L ' - ' )
2005-09-20 21:26:39 +08:00
{
if ( errors )
2012-02-11 09:54:21 +08:00
errors - > push_back ( L " Option does not begin with a '-' " ) ;
2012-08-07 14:34:55 +08:00
return false ;
2005-09-20 21:26:39 +08:00
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
2012-08-07 14:34:55 +08:00
short_validated . resize ( opt . size ( ) , 0 ) ;
2012-02-08 14:44:10 +08:00
2012-08-07 14:34:55 +08:00
is_gnu_opt = opt . at ( 1 ) = = L ' - ' ;
2005-09-20 21:26:39 +08:00
if ( is_gnu_opt )
{
2012-08-07 14:34:55 +08:00
size_t opt_end = opt . find ( L ' = ' ) ;
if ( opt_end ! = wcstring : : npos )
2006-02-20 02:19:32 +08:00
{
2012-08-07 14:34:55 +08:00
gnu_opt_len = opt_end - 2 ;
2006-02-20 02:19:32 +08:00
}
2005-09-20 21:26:39 +08:00
else
2006-02-20 02:19:32 +08:00
{
2012-08-07 14:34:55 +08:00
gnu_opt_len = opt . size ( ) - 2 ;
2006-02-20 02:19:32 +08:00
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2012-02-08 14:44:10 +08:00
parse_cmd_string ( str , path , cmd ) ;
2005-09-20 21:26:39 +08:00
/*
Make sure completions are loaded for the specified command
*/
2012-02-27 12:11:34 +08:00
if ( allow_autoload ) {
complete_load ( cmd , false ) ;
}
2012-02-25 04:13:35 +08:00
scoped_lock lock ( completion_lock ) ;
2012-02-27 06:32:06 +08:00
scoped_lock lock2 ( completion_entry_lock ) ;
2012-04-10 11:17:06 +08:00
for ( completion_entry_set_t : : const_iterator iter = completion_set . begin ( ) ; iter ! = completion_set . end ( ) ; + + iter )
2005-09-20 21:26:39 +08:00
{
2012-02-09 06:47:50 +08:00
const completion_entry_t * i = * iter ;
2012-02-27 05:27:31 +08:00
const wcstring & match = i - > cmd_is_path ? path : cmd ;
2005-09-20 21:26:39 +08:00
if ( ! wildcard_match ( match , i - > cmd ) )
2006-02-22 23:41:52 +08:00
{
2005-09-20 21:26:39 +08:00
continue ;
2006-02-22 23:41:52 +08:00
}
2011-12-27 11:18:46 +08:00
2012-08-07 14:34:55 +08:00
found_match = true ;
2005-09-20 21:26:39 +08:00
2012-08-07 14:34:55 +08:00
if ( ! i - > authoritative )
2005-09-20 21:26:39 +08:00
{
2012-08-07 14:34:55 +08:00
authoritative = false ;
2005-09-20 21:26:39 +08:00
break ;
}
2012-02-27 06:32:06 +08:00
const option_list_t & options = i - > get_options ( ) ;
2005-09-20 21:26:39 +08:00
if ( is_gnu_opt )
{
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator iter = options . begin ( ) ; iter ! = options . end ( ) ; + + iter )
2012-02-09 08:15:53 +08:00
{
const complete_entry_opt_t & o = * iter ;
2012-03-26 16:21:10 +08:00
if ( o . old_mode )
2006-02-22 23:41:52 +08:00
{
2005-09-20 21:26:39 +08:00
continue ;
2006-02-22 23:41:52 +08:00
}
2011-12-27 11:18:46 +08:00
2012-08-07 14:34:55 +08:00
if ( opt . compare ( 2 , gnu_opt_len , o . long_opt ) = = 0 )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
gnu_match_set . insert ( o . long_opt ) ;
2012-08-07 14:34:55 +08:00
if ( opt . compare ( 2 , o . long_opt . size ( ) , o . long_opt ) )
2006-02-22 23:41:52 +08:00
{
2012-08-07 14:34:55 +08:00
is_gnu_exact = true ;
2006-02-22 23:41:52 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
}
else
{
/* Check for old style options */
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator iter = options . begin ( ) ; iter ! = options . end ( ) ; + + iter )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
const complete_entry_opt_t & o = * iter ;
if ( ! o . old_mode )
2005-09-20 21:26:39 +08:00
continue ;
2012-08-07 14:34:55 +08:00
if ( opt . compare ( 1 , wcstring : : npos , o . long_opt ) = = 0 )
2005-09-20 21:26:39 +08:00
{
2012-08-07 14:34:55 +08:00
opt_found = true ;
is_old_opt = true ;
2005-09-20 21:26:39 +08:00
break ;
}
}
if ( is_old_opt )
break ;
2012-08-07 14:34:55 +08:00
for ( size_t opt_idx = 1 ; opt_idx < opt . size ( ) ; opt_idx + + )
2005-09-20 21:26:39 +08:00
{
2012-02-27 06:32:06 +08:00
const wcstring & short_opt_str = i - > get_short_opt_str ( ) ;
2012-08-07 14:34:55 +08:00
size_t str_idx = short_opt_str . find ( opt . at ( opt_idx ) ) ;
2012-02-27 06:32:06 +08:00
if ( str_idx ! = wcstring : : npos )
2005-09-20 21:26:39 +08:00
{
2012-02-27 06:32:06 +08:00
if ( str_idx + 1 < short_opt_str . size ( ) & & short_opt_str . at ( str_idx + 1 ) = = L ' : ' )
2005-09-20 21:26:39 +08:00
{
/*
This is a short option with an embedded argument ,
call complete_is_valid_argument on the argument .
*/
2012-08-07 14:34:55 +08:00
const wcstring nopt = L " - " + opt . substr ( 1 , 1 ) ;
short_validated . at ( opt_idx ) =
complete_is_valid_argument ( str , nopt , opt . substr ( 2 ) ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2012-08-07 14:34:55 +08:00
short_validated . at ( opt_idx ) = true ;
2005-09-20 21:26:39 +08:00
}
}
}
}
}
2007-08-02 01:35:24 +08:00
if ( authoritative )
2005-09-20 21:26:39 +08:00
{
if ( ! is_gnu_opt & & ! is_old_opt )
is_short_opt = 1 ;
if ( is_short_opt )
{
opt_found = 1 ;
2012-08-07 14:34:55 +08:00
for ( size_t j = 1 ; j < opt . size ( ) ; j + + )
2005-09-20 21:26:39 +08:00
{
2012-02-08 14:44:10 +08:00
if ( ! short_validated . at ( j ) )
2005-09-20 21:26:39 +08:00
{
if ( errors )
{
2012-08-07 14:34:55 +08:00
const wcstring str = opt . substr ( j , 1 ) ;
errors - > push_back ( format_error ( _ ( L " Unknown option: " ) , str . c_str ( ) ) ) ;
2005-09-20 21:26:39 +08:00
}
opt_found = 0 ;
break ;
}
}
}
if ( is_gnu_opt )
{
2012-02-09 08:15:53 +08:00
opt_found = is_gnu_exact | | ( gnu_match_set . size ( ) = = 1 ) ;
2005-09-20 21:26:39 +08:00
if ( errors & & ! opt_found )
{
2012-02-11 09:54:21 +08:00
const wchar_t * prefix ;
if ( gnu_match_set . empty ( ) )
2005-09-20 21:26:39 +08:00
{
2012-02-11 09:54:21 +08:00
prefix = _ ( L " Unknown option: " ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2012-02-11 09:54:21 +08:00
prefix = _ ( L " Multiple matches for option: " ) ;
2005-09-20 21:26:39 +08:00
}
2012-02-11 09:54:21 +08:00
errors - > push_back ( format_error ( prefix , opt ) ) ;
2005-09-20 21:26:39 +08:00
}
}
}
2012-08-07 14:34:55 +08:00
return ( authoritative & & found_match ) ? opt_found : true ;
2005-09-20 21:26:39 +08:00
}
2012-08-07 14:34:55 +08:00
bool complete_is_valid_argument ( const wcstring & str , const wcstring & opt , const wcstring & arg )
2005-09-20 21:26:39 +08:00
{
2012-08-07 14:34:55 +08:00
return true ;
2005-09-20 21:26:39 +08:00
}
/**
Copy any strings in possible_comp which have the specified prefix
2012-06-30 07:40:54 +08:00
to the completer ' s completion array . The prefix may contain wildcards .
The output will consist of completion_t structs .
2005-09-20 21:26:39 +08: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-10 01:17:25 +08: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 21:26:39 +08: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 19:05:55 +08:00
2012-06-30 07:40:54 +08: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 19:05:55 +08:00
{
2012-01-30 18:23:58 +08:00
wcstring tmp = wc_escaped ;
2012-06-30 07:40:54 +08:00
if ( ! expand_one ( tmp , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this - > expand_flags ( ) ) )
2012-01-30 18:23:58 +08:00
return ;
const wchar_t * wc = parse_util_unescape_wildcards ( tmp . c_str ( ) ) ;
2011-12-27 11:18:46 +08:00
2012-01-30 14:06:58 +08:00
for ( size_t i = 0 ; i < possible_comp . size ( ) ; i + + )
2007-02-17 19:05:55 +08:00
{
2012-01-17 00:56:47 +08:00
wcstring temp = possible_comp . at ( i ) . completion ;
const wchar_t * next_str = temp . empty ( ) ? NULL : temp . c_str ( ) ;
2007-04-21 03:55:06 +08:00
2007-02-17 19:05:55 +08:00
if ( next_str )
2007-02-24 16:11:31 +08:00
{
2012-06-30 07:40:54 +08:00
wildcard_complete ( next_str , wc , desc , desc_func , this - > completions , flags ) ;
2007-02-24 16:11:31 +08:00
}
2007-02-17 19:05:55 +08:00
}
2012-01-30 18:23:58 +08:00
free ( ( void * ) wc ) ;
2007-02-17 19:05:55 +08:00
}
2005-09-20 21:26:39 +08:00
/**
If command to complete is short enough , substitute
the description with the whatis information for the executable .
*/
2012-02-27 05:27:31 +08:00
void completer_t : : complete_cmd_desc ( const wcstring & str )
2005-09-20 21:26:39 +08:00
{
2012-02-25 04:13:35 +08:00
ASSERT_IS_MAIN_THREAD ( ) ;
2005-09-20 21:26:39 +08:00
const wchar_t * cmd_start ;
2006-11-16 21:04:00 +08:00
int skip ;
2011-12-27 11:18:46 +08:00
2012-02-27 05:27:31 +08:00
const wchar_t * const cmd = str . c_str ( ) ;
2006-01-10 01:17:25 +08:00
cmd_start = wcsrchr ( cmd , L ' / ' ) ;
if ( cmd_start )
cmd_start + + ;
else
2005-09-20 21:26:39 +08:00
cmd_start = cmd ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08: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 .
*/
2012-07-29 08:49:46 +08:00
if ( wcslen ( cmd_start ) < 2 )
2005-09-20 21:26:39 +08:00
return ;
if ( wildcard_has ( cmd_start , 0 ) )
{
return ;
}
2006-01-31 03:53:10 +08:00
2006-11-16 21:04:00 +08:00
skip = 1 ;
2011-12-27 11:18:46 +08:00
2012-02-27 05:27:31 +08:00
for ( size_t i = 0 ; i < this - > completions . size ( ) ; i + + )
2006-11-16 21:04:00 +08:00
{
2012-02-27 05:27:31 +08:00
const completion_t & c = this - > completions . at ( i ) ;
2011-12-27 11:18:46 +08:00
2012-01-17 00:56:47 +08:00
if ( c . completion . empty ( ) | | ( c . completion [ c . completion . size ( ) - 1 ] ! = L ' / ' ) )
2006-11-16 21:04:00 +08:00
{
skip = 0 ;
break ;
}
2011-12-27 11:18:46 +08:00
2006-11-16 21:04:00 +08:00
}
2011-12-27 11:18:46 +08:00
2006-11-16 21:04:00 +08:00
if ( skip )
{
return ;
}
2011-12-27 11:18:46 +08:00
2006-11-16 21:04:00 +08:00
2012-02-03 04:04:04 +08:00
wcstring lookup_cmd ( L " __fish_describe_command " ) ;
lookup_cmd . append ( escape_string ( cmd_start , 1 ) ) ;
2005-09-20 21:26:39 +08:00
2012-02-03 04:04:04 +08:00
std : : map < wcstring , wcstring > lookup ;
2006-02-08 23:29:09 +08:00
2006-01-08 10:56:56 +08: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-02-03 04:04:04 +08:00
wcstring_list_t list ;
2012-02-08 15:35:41 +08:00
if ( exec_subshell ( lookup_cmd , list ) ! = - 1 )
2006-10-19 23:36:03 +08:00
{
2011-12-27 11:18:46 +08:00
2006-10-19 23:36:03 +08: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 .
2006-01-31 03:53:10 +08:00
2006-10-19 23:36:03 +08:00
Should be reasonably fast , since no memory allocations are needed .
*/
2012-02-03 04:04:04 +08:00
for ( size_t i = 0 ; i < list . size ( ) ; i + + )
2006-10-19 23:36:03 +08:00
{
2012-02-03 04:04:04 +08:00
const wcstring & elstr = list . at ( i ) ;
const wcstring fullkey ( elstr , wcslen ( cmd_start ) ) ;
size_t tab_idx = fullkey . find ( L ' \t ' ) ;
if ( tab_idx = = wcstring : : npos )
2006-10-19 23:36:03 +08:00
continue ;
2012-02-03 04:04:04 +08:00
const wcstring key ( fullkey , 0 , tab_idx ) ;
wcstring val ( fullkey , tab_idx + 1 ) ;
2007-01-07 22:24:30 +08:00
2006-10-19 23:36:03 +08: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-03 04:04:04 +08:00
if ( ! val . empty ( ) )
val [ 0 ] = towupper ( val [ 0 ] ) ;
2011-12-27 11:18:46 +08:00
2012-02-03 04:04:04 +08:00
lookup [ key ] = val ;
2006-10-19 23:36:03 +08:00
}
2006-01-08 10:56:56 +08:00
2006-10-19 23:36:03 +08:00
/*
Then do a lookup on every completion and if a match is found ,
change to the new description .
2006-01-31 03:53:10 +08:00
2006-10-19 23:36:03 +08:00
This needs to do a reallocation for every description added , but
there shouldn ' t be that many completions , so it should be ok .
*/
2012-02-27 05:27:31 +08:00
for ( size_t i = 0 ; i < this - > completions . size ( ) ; i + + )
2006-10-19 23:36:03 +08:00
{
2012-02-27 05:27:31 +08:00
completion_t & completion = this - > completions . at ( i ) ;
2012-02-03 04:04:04 +08:00
const wcstring & el = completion . completion ;
if ( el . empty ( ) )
continue ;
std : : map < wcstring , wcstring > : : iterator new_desc_iter = lookup . find ( el ) ;
if ( new_desc_iter ! = lookup . end ( ) )
completion . description = new_desc_iter - > second ;
2005-09-20 21:26:39 +08:00
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
2006-06-17 21:07:08 +08:00
/**
2012-05-18 10:37:46 +08:00
Returns a description for the specified function , or an empty string if none
2006-06-17 21:07:08 +08:00
*/
2012-05-18 10:37:46 +08:00
static wcstring complete_function_desc ( const wcstring & fn )
2006-01-24 07:32:13 +08:00
{
2012-05-18 10:37:46 +08:00
wcstring result ;
2012-05-18 10:46:08 +08:00
bool has_description = function_get_desc ( fn , & result ) ;
if ( ! has_description ) {
2012-05-18 10:37:46 +08:00
function_get_definition ( fn , & result ) ;
}
return result ;
2006-01-24 07:32:13 +08:00
}
2005-09-20 21:26:39 +08: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-24 07:33:47 +08:00
\ param comp the list to add all completions to
2005-09-20 21:26:39 +08:00
*/
2012-05-09 17:33:42 +08:00
void completer_t : : complete_cmd ( const wcstring & str_cmd , bool use_function , bool use_builtin , bool use_command )
2005-09-20 21:26:39 +08:00
{
2012-05-09 17:33:42 +08:00
/* Paranoia */
if ( str_cmd . empty ( ) )
return ;
2012-01-17 00:56:47 +08:00
std : : vector < completion_t > possible_comp ;
2006-01-31 03:53:10 +08:00
2012-05-07 05:53:19 +08:00
env_var_t cdpath = env_get_string ( L " CDPATH " ) ;
if ( cdpath . missing_or_empty ( ) )
cdpath = L " . " ;
2012-02-25 04:13:35 +08:00
const bool wants_description = ( type = = COMPLETE_DEFAULT ) ;
2012-05-09 17:33:42 +08:00
if ( str_cmd . find ( L ' / ' ) ! = wcstring : : npos | | str_cmd . at ( 0 ) = = L ' ~ ' )
2005-09-20 21:26:39 +08:00
{
2006-01-31 03:53:10 +08:00
2012-03-28 10:22:33 +08:00
if ( use_command )
2005-12-04 00:43:56 +08:00
{
2011-12-27 11:18:46 +08:00
2012-05-09 17:33:42 +08:00
if ( expand_string ( str_cmd , this - > completions , ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) ) ! = EXPAND_ERROR )
2006-12-14 08:03:26 +08:00
{
2012-03-28 10:22:33 +08:00
if ( wants_description ) {
2012-05-09 17:33:42 +08:00
this - > complete_cmd_desc ( str_cmd ) ;
2012-03-28 10:22:33 +08:00
}
2006-12-14 08:03:26 +08:00
}
2005-12-04 00:43:56 +08:00
}
2005-09-20 21:26:39 +08:00
}
else
{
2006-12-14 08:03:26 +08:00
if ( use_command )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
2012-01-14 18:42:17 +08:00
const env_var_t path = env_get_string ( L " PATH " ) ;
if ( ! path . missing ( ) )
2005-09-20 21:26:39 +08:00
{
2012-07-21 06:01:56 +08:00
wcstring base_path ;
wcstokenizer tokenizer ( path , ARRAY_SEP_STR ) ;
while ( tokenizer . next ( base_path ) )
2006-12-14 08:03:26 +08:00
{
2012-05-09 17:33:42 +08:00
if ( base_path . empty ( ) )
continue ;
/* 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-02-25 04:13:35 +08:00
if ( expand_string ( nxt_completion ,
2012-02-27 05:27:31 +08:00
this - > completions ,
2012-03-10 12:16:26 +08:00
ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) ) ! = EXPAND_ERROR )
2006-10-05 05:42:04 +08:00
{
2012-05-09 17:33:42 +08:00
/* For all new completions, if COMPLETE_NO_CASE is set, then use only the last path component */
2012-02-27 05:27:31 +08:00
for ( size_t i = prev_count ; i < this - > completions . size ( ) ; i + + )
2007-10-28 17:06:05 +08:00
{
2012-02-27 05:27:31 +08:00
completion_t & c = this - > completions . at ( i ) ;
2012-01-17 00:56:47 +08:00
if ( c . flags & COMPLETE_NO_CASE )
2007-10-28 17:06:05 +08:00
{
2012-05-09 17:33:42 +08:00
c . completion . erase ( 0 , base_path . size ( ) ) ;
2007-10-28 17:06:05 +08:00
}
}
2006-10-05 05:42:04 +08:00
}
2006-12-14 08:03:26 +08:00
}
2012-02-25 04:13:35 +08:00
if ( wants_description )
2012-05-09 17:33:42 +08:00
this - > complete_cmd_desc ( str_cmd ) ;
2006-10-18 23:51:51 +08:00
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/*
These return the original strings - don ' t free them
*/
2006-12-14 08:03:26 +08:00
if ( use_function )
{
2012-01-14 15:44:18 +08:00
//function_get_names( &possible_comp, cmd[0] == L'_' );
2012-05-09 17:33:42 +08:00
wcstring_list_t names = function_get_names ( str_cmd . at ( 0 ) = = L ' _ ' ) ;
2012-01-14 15:44:18 +08:00
for ( size_t i = 0 ; i < names . size ( ) ; i + + ) {
2012-02-02 08:27:14 +08:00
possible_comp . push_back ( completion_t ( names . at ( i ) ) ) ;
2012-01-14 15:44:18 +08:00
}
2012-06-30 07:40:54 +08:00
this - > complete_strings ( str_cmd , 0 , & complete_function_desc , possible_comp , 0 ) ;
2006-12-14 08:03:26 +08:00
}
2012-01-17 00:56:47 +08:00
possible_comp . clear ( ) ;
2011-12-27 11:18:46 +08:00
2006-12-14 08:03:26 +08:00
if ( use_builtin )
{
2012-02-01 11:47:56 +08:00
builtin_get_names ( possible_comp ) ;
2012-06-30 07:40:54 +08:00
this - > complete_strings ( str_cmd , 0 , & builtin_get_desc , possible_comp , 0 ) ;
2006-12-14 08:03:26 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
2012-02-25 10:43:10 +08:00
2005-09-20 21:26:39 +08:00
/**
Evaluate the argument list ( as supplied by complete - a ) and insert
2006-01-10 01:40:32 +08:00
any return matching completions . Matching is done using \ c
2005-09-20 21:26:39 +08:00
copy_strings_with_prefix , meaning the completion may contain
wildcards . Logically , this is not always the right thing to do , but
2006-01-10 01:40:32 +08:00
I have yet to come up with a case where this matters .
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08: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-02-27 05:27:31 +08:00
void completer_t : : complete_from_args ( const wcstring & str ,
const wcstring & args ,
const wcstring & desc ,
complete_flags_t flags )
2005-09-20 21:26:39 +08:00
{
2012-02-26 10:54:49 +08:00
2012-01-17 00:56:47 +08:00
std : : vector < completion_t > possible_comp ;
2006-01-31 03:53:10 +08:00
2012-05-06 05:33:24 +08:00
bool is_autosuggest = ( this - > type = = COMPLETE_AUTOSUGGEST ) ;
2012-05-07 04:51:11 +08:00
parser_t parser ( is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL , false ) ;
2012-05-06 05:33:24 +08:00
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
2012-05-06 05:39:08 +08:00
if ( ! is_autosuggest )
2012-02-26 10:54:49 +08:00
proc_push_interactive ( 0 ) ;
2012-02-09 08:15:53 +08:00
parser . eval_args ( args . c_str ( ) , possible_comp ) ;
2012-01-29 16:41:39 +08:00
2012-05-06 05:39:08 +08:00
if ( ! is_autosuggest )
2012-02-26 10:54:49 +08:00
proc_pop_interactive ( ) ;
2011-12-27 11:18:46 +08:00
2012-06-30 07:40:54 +08:00
this - > complete_strings ( str , desc . c_str ( ) , 0 , possible_comp , flags ) ;
2005-09-20 21:26:39 +08:00
}
/**
Match against an old style long option
*/
2012-02-09 08:15:53 +08:00
static int param_match_old ( const complete_entry_opt_t * e ,
2007-01-19 00:45:28 +08:00
const wchar_t * optstr )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
return ( optstr [ 0 ] = = L ' - ' ) & & ( e - > long_opt = = & optstr [ 1 ] ) ;
2005-09-20 21:26:39 +08:00
}
/**
Match a parameter
*/
2007-01-19 00:45:28 +08:00
static int param_match ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 21:26:39 +08:00
{
if ( e - > short_opt ! = L ' \0 ' & &
e - > short_opt = = optstr [ 1 ] )
return 1 ;
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
{
2012-02-09 08:15:53 +08:00
if ( e - > long_opt = = & optstr [ 2 ] )
2005-09-20 21:26:39 +08:00
{
return 1 ;
}
}
return 0 ;
}
/**
Test if a string is an option with an argument , like - - color = auto or - I / usr / include
*/
2007-01-19 00:45:28 +08:00
static wchar_t * param_match2 ( const complete_entry_opt_t * e ,
const wchar_t * optstr )
2005-09-20 21:26:39 +08:00
{
if ( e - > short_opt ! = L ' \0 ' & & e - > short_opt = = optstr [ 1 ] )
2007-01-19 00:45:28 +08:00
return ( wchar_t * ) & optstr [ 2 ] ;
2005-09-20 21:26:39 +08:00
if ( ! e - > old_mode & & ( wcsncmp ( L " -- " , optstr , 2 ) = = 0 ) )
{
2012-02-09 08:15:53 +08:00
size_t len = e - > long_opt . size ( ) ;
2005-09-20 21:26:39 +08:00
2012-02-09 08:15:53 +08:00
if ( wcsncmp ( e - > long_opt . c_str ( ) , & optstr [ 2 ] , len ) = = 0 )
2005-09-20 21:26:39 +08:00
{
if ( optstr [ len + 2 ] = = L ' = ' )
2007-01-19 00:45:28 +08:00
return ( wchar_t * ) & optstr [ len + 3 ] ;
2005-09-20 21:26:39 +08:00
}
}
return 0 ;
}
/**
Tests whether a short option is a viable completion
*/
2012-02-09 06:47:50 +08:00
static int short_ok ( const wcstring & arg_str , wchar_t nextopt , const wcstring & allopt_str )
2005-09-20 21:26:39 +08:00
{
2012-02-09 06:47:50 +08:00
const wchar_t * arg = arg_str . c_str ( ) ;
const wchar_t * allopt = allopt_str . c_str ( ) ;
2007-01-19 00:45:28 +08:00
const wchar_t * ptr ;
2005-09-20 21:26:39 +08:00
if ( arg [ 0 ] ! = L ' - ' )
return arg [ 0 ] = = L ' \0 ' ;
if ( arg [ 1 ] = = L ' - ' )
return 0 ;
if ( wcschr ( arg , nextopt ) ! = 0 )
return 0 ;
for ( ptr = arg + 1 ; * ptr ; ptr + + )
{
2012-01-06 05:58:48 +08:00
const wchar_t * tmp = wcschr ( allopt , * ptr ) ;
2005-09-20 21:26:39 +08:00
/* Unknown option */
if ( tmp = = 0 )
{
/*fwprintf( stderr, L"Unknown option %lc", *ptr );*/
return 0 ;
}
if ( * ( tmp + 1 ) = = L ' : ' )
{
/* fwprintf( stderr, L"Woot %ls", allopt );*/
return 0 ;
}
}
return 1 ;
}
2012-02-08 14:44:10 +08:00
void complete_load ( const wcstring & name , bool reload )
2006-02-08 17:20:05 +08:00
{
2012-01-26 10:40:08 +08:00
completion_autoloader . load ( name , reload ) ;
2006-02-08 17:20:05 +08:00
}
2005-09-20 21:26:39 +08: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-05-12 09:59:38 +08:00
struct local_options_t {
wcstring short_opt_str ;
option_list_t options ;
} ;
2012-02-27 05:27:31 +08:00
bool completer_t : : complete_param ( const wcstring & scmd_orig , const wcstring & spopt , const wcstring & sstr , bool use_switches )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
const wchar_t * const cmd_orig = scmd_orig . c_str ( ) , * const popt = spopt . c_str ( ) , * const str = sstr . c_str ( ) ;
2012-05-12 09:59:38 +08:00
bool use_common = 1 , use_files = 1 ;
2005-09-20 21:26:39 +08:00
2012-02-09 06:47:50 +08:00
wcstring cmd , path ;
parse_cmd_string ( cmd_orig , path , cmd ) ;
2005-09-20 21:26:39 +08:00
2012-02-27 12:11:34 +08:00
if ( this - > type = = COMPLETE_DEFAULT )
{
2012-02-25 04:13:35 +08:00
complete_load ( cmd , true ) ;
2012-02-27 12:11:34 +08:00
}
else if ( this - > type = = COMPLETE_AUTOSUGGEST )
{
/* 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-05-12 09:59:38 +08: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 ;
}
/* 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
}
}
/* 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 */
for ( std : : vector < local_options_t > : : const_iterator iter = all_options . begin ( ) ; iter ! = all_options . end ( ) ; iter + + )
{
const option_list_t & options = iter - > options ;
2007-01-10 20:45:28 +08:00
use_common = 1 ;
if ( use_switches )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
2007-01-10 20:45:28 +08:00
if ( str [ 0 ] = = L ' - ' )
2005-09-20 21:26:39 +08:00
{
2007-01-10 20:45:28 +08:00
/* Check if we are entering a combined option and argument
( like - - color = auto or - I / usr / include ) */
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
const complete_entry_opt_t * o = & * oiter ;
2007-01-10 20:45:28 +08:00
wchar_t * arg ;
2012-02-27 05:27:31 +08:00
if ( ( arg = param_match2 ( o , str ) ) ! = 0 & & this - > condition_test ( o - > condition ) )
2005-09-20 21:26:39 +08:00
{
2012-05-12 09:59:38 +08:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-02-27 05:27:31 +08:00
complete_from_args ( arg , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
2005-09-20 21:26:39 +08:00
}
2007-01-10 20:45:28 +08:00
2005-09-20 21:26:39 +08:00
}
}
2007-01-10 20:45:28 +08:00
else if ( popt [ 0 ] = = L ' - ' )
2005-09-20 21:26:39 +08:00
{
2007-01-10 20:45:28 +08:00
/* Set to true if we found a matching old-style switch */
int old_style_match = 0 ;
2011-12-27 11:18:46 +08:00
2007-01-10 20:45:28 +08:00
/*
If we are using old style long options , check for them
first
*/
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
const complete_entry_opt_t * o = & * oiter ;
2007-01-10 20:45:28 +08:00
if ( o - > old_mode )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
if ( param_match_old ( o , popt ) & & this - > condition_test ( o - > condition ) )
2007-01-10 20:45:28 +08:00
{
old_style_match = 1 ;
2012-05-12 09:59:38 +08:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-02-27 05:27:31 +08:00
complete_from_args ( str , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
2007-01-10 20:45:28 +08:00
}
}
}
2011-12-27 11:18:46 +08:00
2007-01-10 20:45:28 +08: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-02 06:56:34 +08:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 08:15:53 +08:00
{
const complete_entry_opt_t * o = & * oiter ;
2007-01-10 20:45:28 +08:00
/*
Gnu - style options with _optional_ arguments must
be specified as a single token , so that it can
be differed from a regular argument .
*/
2012-02-09 08:15:53 +08:00
if ( ! o - > old_mode & & ! o - > long_opt . empty ( ) & & ! ( o - > result_mode & NO_COMMON ) )
2007-01-10 20:45:28 +08:00
continue ;
2012-02-27 05:27:31 +08:00
if ( param_match ( o , popt ) & & this - > condition_test ( o - > condition ) )
2007-01-10 20:45:28 +08:00
{
2012-05-12 09:59:38 +08:00
if ( o - > result_mode & NO_COMMON ) use_common = false ;
if ( o - > result_mode & NO_FILES ) use_files = false ;
2012-02-27 05:27:31 +08:00
complete_from_args ( str , o - > comp . c_str ( ) , o - > localized_desc ( ) , o - > flags ) ;
2006-01-31 03:53:10 +08:00
2007-01-10 20:45:28 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( use_common )
{
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 08:15:53 +08:00
{
const complete_entry_opt_t * o = & * oiter ;
2005-09-20 21:26:39 +08:00
/*
If this entry is for the base command ,
check if any of the arguments match
*/
2006-01-31 03:53:10 +08:00
2012-02-27 05:27:31 +08:00
if ( ! this - > condition_test ( o - > condition ) )
2005-09-20 21:26:39 +08:00
continue ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( ( o - > short_opt = = L ' \0 ' ) & & ( o - > long_opt [ 0 ] = = L ' \0 ' ) )
{
use_files & = ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
2012-02-27 05:27:31 +08:00
complete_from_args ( str , o - > comp , o - > localized_desc ( ) , o - > flags ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2007-01-10 20:45:28 +08:00
if ( wcslen ( str ) > 0 & & use_switches )
2005-09-20 21:26:39 +08:00
{
/*
Check if the short style option matches
*/
if ( o - > short_opt ! = L ' \0 ' & &
2012-05-12 09:59:38 +08:00
short_ok ( str , o - > short_opt , iter - > short_opt_str ) )
2005-09-20 21:26:39 +08:00
{
2012-02-09 08:15:53 +08:00
const wchar_t * desc = o - > localized_desc ( ) ;
2007-02-18 20:08:41 +08:00
wchar_t completion [ 2 ] ;
completion [ 0 ] = o - > short_opt ;
completion [ 1 ] = 0 ;
2011-12-27 11:18:46 +08:00
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions , completion , desc , 0 ) ;
2007-02-18 20:08:41 +08:00
2005-09-20 21:26:39 +08:00
}
/*
Check if the long style option matches
*/
if ( o - > long_opt [ 0 ] ! = L ' \0 ' )
{
2007-03-01 05:43:27 +08:00
int match = 0 , match_no_case = 0 ;
2011-12-27 11:18:46 +08:00
2012-02-09 06:47:50 +08:00
wcstring whole_opt ;
whole_opt . append ( o - > old_mode ? L " - " : L " -- " ) ;
whole_opt . append ( o - > long_opt ) ;
2006-01-31 03:53:10 +08:00
2012-02-09 06:47:50 +08:00
match = string_prefixes_string ( str , whole_opt ) ;
2007-03-01 05:43:27 +08:00
if ( ! match )
{
2012-02-09 06:47:50 +08:00
match_no_case = wcsncasecmp ( str , whole_opt . c_str ( ) , wcslen ( str ) ) = = 0 ;
2007-03-01 05:43:27 +08:00
}
2011-12-27 11:18:46 +08:00
2007-03-01 05:43:27 +08:00
if ( match | | match_no_case )
2006-01-31 03:53:10 +08:00
{
2007-02-18 20:08:41 +08:00
int has_arg = 0 ; /* Does this switch have any known arguments */
int req_arg = 0 ; /* Does this switch _require_ an argument */
2005-09-20 21:26:39 +08:00
2012-08-02 07:32:52 +08:00
size_t offset = 0 ;
2012-02-26 10:54:49 +08:00
complete_flags_t flags = 0 ;
2007-03-01 05:43:27 +08:00
2011-12-27 11:18:46 +08:00
2007-03-01 05:43:27 +08:00
if ( match )
offset = wcslen ( str ) ;
else
flags = COMPLETE_NO_CASE ;
2012-02-09 08:15:53 +08:00
has_arg = ! o - > comp . empty ( ) ;
2006-11-08 02:19:11 +08:00
req_arg = ( o - > result_mode & NO_COMMON ) ;
2006-01-31 03:53:10 +08:00
2006-11-08 02:19:11 +08:00
if ( ! o - > old_mode & & ( has_arg & & ! req_arg ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
2007-02-18 20:08:41 +08:00
/*
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 .
*/
2012-02-23 04:00:02 +08:00
wcstring completion = format_string ( L " %ls= " , whole_opt . c_str ( ) + offset ) ;
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions ,
2012-02-23 04:00:02 +08:00
completion ,
2012-02-09 08:15:53 +08:00
C_ ( o - > desc . c_str ( ) ) ,
2011-12-27 11:18:46 +08:00
flags ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions ,
2012-02-09 06:47:50 +08:00
whole_opt . c_str ( ) + offset ,
2012-02-09 08:15:53 +08:00
C_ ( o - > desc . c_str ( ) ) ,
2007-03-01 05:43:27 +08:00
flags ) ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
}
}
}
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
return use_files ;
}
/**
Perform file completion on the specified string
*/
2012-02-27 05:27:31 +08:00
void completer_t : : complete_param_expand ( const wcstring & sstr , bool do_file )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
const wchar_t * const str = sstr . c_str ( ) ;
2012-02-06 16:57:43 +08:00
const wchar_t * comp_str ;
2011-12-27 11:18:46 +08:00
2012-05-09 17:33:42 +08:00
if ( string_prefixes_string ( L " -- " , sstr ) & & ( comp_str = wcschr ( str , L ' = ' ) ) )
2005-09-20 21:26:39 +08:00
{
comp_str + + ;
}
else
2006-02-20 02:19:32 +08:00
{
2005-09-20 21:26:39 +08:00
comp_str = str ;
2006-02-20 02:19:32 +08:00
}
2012-02-25 04:13:35 +08:00
2012-02-26 10:54:49 +08:00
expand_flags_t flags = EXPAND_SKIP_CMDSUBST | ACCEPT_INCOMPLETE ;
2012-02-25 04:13:35 +08:00
if ( ! do_file )
flags | = EXPAND_SKIP_WILDCARDS ;
2012-08-16 09:20:44 +08:00
/* Squelch file descriptions per issue 254 */
if ( type = = COMPLETE_AUTOSUGGEST | | do_file )
2012-02-25 04:13:35 +08:00
flags | = EXPAND_NO_DESCRIPTIONS ;
2011-12-27 11:18:46 +08:00
2012-02-14 11:39:20 +08:00
if ( expand_string ( comp_str ,
2012-02-27 05:27:31 +08:00
this - > completions ,
2012-03-10 12:16:26 +08:00
flags | this - > expand_flags ( ) ) = = EXPAND_ERROR )
2006-10-19 23:36:03 +08:00
{
2006-10-24 19:03:52 +08:00
debug ( 3 , L " Error while expanding string '%ls' " , comp_str ) ;
2012-05-06 05:21:21 +08:00
}
}
void completer_t : : debug_print_completions ( )
{
for ( size_t i = 0 ; i < completions . size ( ) ; i + + ) {
printf ( " - Completion: %ls \n " , completions . at ( i ) . completion . c_str ( ) ) ;
}
2005-09-20 21:26:39 +08:00
}
/**
Complete the specified string as an environment variable
*/
2012-08-02 07:32:52 +08:00
bool completer_t : : complete_variable ( const wcstring & str , size_t start_offset )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
const wchar_t * const whole_var = str . c_str ( ) ;
2007-03-25 03:07:38 +08:00
const wchar_t * var = & whole_var [ start_offset ] ;
2012-08-02 07:32:52 +08:00
size_t varlen = wcslen ( var ) ;
2005-09-20 21:26:39 +08:00
int res = 0 ;
2012-02-26 10:54:49 +08:00
bool wants_description = ( type ! = COMPLETE_AUTOSUGGEST ) ;
2012-02-02 08:27:14 +08:00
const wcstring_list_t names = env_get_names ( 0 ) ;
for ( size_t i = 0 ; i < names . size ( ) ; i + + )
2005-09-20 21:26:39 +08:00
{
2012-02-02 08:27:14 +08:00
const wcstring & env_name = names . at ( i ) ;
2012-08-02 07:32:52 +08:00
size_t namelen = env_name . size ( ) ;
2011-12-27 11:18:46 +08:00
int match = 0 , match_no_case = 0 ;
2006-02-20 02:19:32 +08:00
2005-09-20 21:26:39 +08:00
if ( varlen > namelen )
continue ;
2012-02-02 08:27:14 +08:00
match = string_prefixes_string ( var , env_name ) ;
2011-12-27 11:18:46 +08:00
2007-03-25 03:07:38 +08:00
if ( ! match )
{
2012-02-02 08:27:14 +08:00
match_no_case = ( wcsncasecmp ( var , env_name . c_str ( ) , varlen ) = = 0 ) ;
2007-03-25 03:07:38 +08:00
}
if ( match | | match_no_case )
2005-09-20 21:26:39 +08:00
{
2012-02-26 10:54:49 +08:00
wcstring comp ;
int flags = 0 ;
if ( match )
{
comp . append ( env_name . c_str ( ) + varlen ) ;
}
else
{
comp . append ( whole_var , start_offset ) ;
comp . append ( env_name ) ;
flags = COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE ;
}
wcstring desc ;
if ( wants_description )
{
env_var_t value_unescaped = env_get_string ( env_name ) ;
if ( value_unescaped . missing ( ) )
continue ;
2012-02-02 08:27:14 +08:00
2012-02-26 10:54:49 +08:00
wcstring value = expand_escape_variable ( value_unescaped ) ;
if ( type ! = COMPLETE_AUTOSUGGEST )
desc = format_string ( COMPLETE_VAR_DESC_VAL , value . c_str ( ) ) ;
}
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions , comp . c_str ( ) , desc . c_str ( ) , flags ) ;
2012-02-26 10:54:49 +08:00
res = 1 ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
}
return res ;
}
/**
2006-02-20 02:19:32 +08:00
Search the specified string for the \ $ sign . If found , try to
2011-12-27 11:18:46 +08:00
complete as an environment variable .
2006-02-20 02:19:32 +08:00
\ return 0 if unable to complete , 1 otherwise
2005-09-20 21:26:39 +08:00
*/
2012-02-27 05:27:31 +08:00
bool completer_t : : try_complete_variable ( const wcstring & str )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:46:21 +08:00
size_t i = str . size ( ) ;
while ( i - - )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
wchar_t c = str . at ( i ) ;
if ( c = = L ' $ ' )
2005-09-20 21:26:39 +08:00
{
/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/
2012-02-27 05:27:31 +08:00
return this - > complete_variable ( str , i + 1 ) ;
2005-09-20 21:26:39 +08:00
}
2012-02-27 05:27:31 +08:00
if ( ! isalnum ( c ) & & c ! = L ' _ ' )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
return false ;
2005-09-20 21:26:39 +08:00
}
}
2012-02-27 05:27:31 +08:00
return false ;
2012-02-25 10:43:10 +08:00
}
2005-09-20 21:26:39 +08:00
/**
2006-02-20 02:19:32 +08:00
Try to complete the specified string as a username . This is used by
~ USER type expansion .
2005-09-20 21:26:39 +08:00
2006-02-20 02:19:32 +08:00
\ return 0 if unable to complete , 1 otherwise
*/
2012-02-27 05:27:31 +08:00
bool completer_t : : try_complete_user ( const wcstring & str )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
const wchar_t * cmd = str . c_str ( ) ;
2009-02-03 06:46:45 +08:00
const wchar_t * first_char = cmd ;
int res = 0 ;
double start_time = timef ( ) ;
2011-12-27 11:18:46 +08:00
2009-02-03 06:46:45 +08:00
if ( * first_char = = L ' ~ ' & & ! wcschr ( first_char , L ' / ' ) )
2005-09-20 21:26:39 +08:00
{
2009-02-03 06:46:45 +08:00
const wchar_t * user_name = first_char + 1 ;
2012-01-06 05:58:48 +08:00
const wchar_t * name_end = wcschr ( user_name , L ' ~ ' ) ;
2009-02-03 06:46:45 +08:00
if ( name_end = = 0 )
2005-09-20 21:26:39 +08:00
{
2009-02-03 06:46:45 +08:00
struct passwd * pw ;
2012-08-02 07:32:52 +08:00
size_t name_len = wcslen ( user_name ) ;
2011-12-27 11:18:46 +08:00
2009-02-03 06:46:45 +08:00
setpwent ( ) ;
2011-12-27 11:18:46 +08:00
2009-02-03 06:46:45 +08:00
while ( ( pw = getpwent ( ) ) ! = 0 )
2005-09-20 21:26:39 +08:00
{
2009-02-03 06:46:45 +08:00
double current_time = timef ( ) ;
wchar_t * pw_name ;
2005-09-20 21:26:39 +08:00
2011-12-27 11:18:46 +08:00
if ( current_time - start_time > 0.2 )
2009-02-03 06:46:45 +08:00
{
return 1 ;
}
2011-12-27 11:18:46 +08:00
2009-02-03 06:46:45 +08:00
pw_name = str2wcs ( pw - > pw_name ) ;
2005-09-20 21:26:39 +08:00
2009-02-03 06:46:45 +08:00
if ( pw_name )
2005-09-20 21:26:39 +08:00
{
2009-02-03 06:46:45 +08:00
if ( wcsncmp ( user_name , pw_name , name_len ) = = 0 )
2005-09-20 21:26:39 +08:00
{
2012-02-23 04:00:02 +08:00
wcstring desc = format_string ( COMPLETE_USER_DESC , pw_name ) ;
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions ,
2009-02-03 06:46:45 +08:00
& pw_name [ name_len ] ,
2012-02-23 04:00:02 +08:00
desc ,
2009-02-03 06:46:45 +08:00
COMPLETE_NO_SPACE ) ;
2011-12-27 11:18:46 +08:00
2009-02-03 06:46:45 +08:00
res = 1 ;
}
else if ( wcsncasecmp ( user_name , pw_name , name_len ) = = 0 )
{
2012-02-23 04:00:02 +08:00
wcstring name = format_string ( L " ~%ls " , pw_name ) ;
wcstring desc = format_string ( COMPLETE_USER_DESC , pw_name ) ;
2011-12-27 11:18:46 +08:00
2012-07-18 03:47:01 +08:00
append_completion ( this - > completions ,
2012-02-23 04:00:02 +08:00
name ,
desc ,
2009-02-03 06:46:45 +08:00
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ) ;
2012-02-23 04:00:02 +08:00
res = 1 ;
2005-09-20 21:26:39 +08:00
}
2009-02-03 06:46:45 +08:00
free ( pw_name ) ;
2005-09-20 21:26:39 +08:00
}
}
2009-02-03 06:46:45 +08:00
endpwent ( ) ;
2005-09-20 21:26:39 +08:00
}
}
2009-02-03 06:46:45 +08:00
return res ;
}
2007-02-09 17:33:50 +08:00
2012-02-27 12:11:34 +08:00
void complete ( const wcstring & cmd , std : : vector < completion_t > & comps , complete_type_t type , wcstring_list_t * commands_to_load )
2012-02-25 10:43:10 +08:00
{
/* Make our completer */
completer_t completer ( cmd , type ) ;
const wchar_t * tok_begin , * tok_end , * cmdsubst_begin , * cmdsubst_end , * prev_begin , * prev_end ;
tokenizer tok ;
const wchar_t * current_token = 0 , * prev_token = 0 ;
wcstring current_command ;
int on_command = 0 ;
2012-08-05 02:07:42 +08:00
size_t pos ;
2012-02-25 10:43:10 +08:00
bool done = false ;
int use_command = 1 ;
int use_function = 1 ;
int use_builtin = 1 ;
int had_ddash = 0 ;
// debug( 1, L"Complete '%ls'", cmd );
2012-08-05 02:07:42 +08:00
size_t cursor_pos = cmd . size ( ) ;
2012-02-25 10:43:10 +08:00
const wchar_t * cmd_cstr = cmd . c_str ( ) ;
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 ) ;
if ( ! cmdsubst_begin )
done = 1 ;
/**
If we are completing a variable name or a tilde expansion user
name , we do that and return . No need for any other competions .
*/
if ( ! done )
{
wcstring tmp = tok_begin ;
done = completer . try_complete_variable ( tmp ) | | completer . try_complete_user ( tmp ) ;
}
if ( ! done )
{
pos = cursor_pos - ( cmdsubst_begin - cmd_cstr ) ;
2012-08-05 02:07:42 +08:00
wcstring buff = wcstring ( cmdsubst_begin , cmdsubst_end - cmdsubst_begin ) ;
2012-02-25 10:43:10 +08:00
int had_cmd = 0 ;
int end_loop = 0 ;
tok_init ( & tok , buff . c_str ( ) , TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS ) ;
while ( tok_has_next ( & tok ) & & ! end_loop )
{
switch ( tok_last_type ( & tok ) )
{
case TOK_STRING :
{
const wcstring ncmd = tok_last ( & tok ) ;
int is_ddash = ( ncmd = = L " -- " ) & & ( ( tok_get_pos ( & tok ) + 2 ) < pos ) ;
if ( ! had_cmd )
{
if ( parser_keywords_is_subcommand ( ncmd ) )
{
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 ;
}
if ( ! is_ddash | |
( ( use_command & & use_function & & use_builtin ) ) )
{
current_command = ncmd ;
2012-08-05 02:07:42 +08:00
size_t token_end = tok_get_pos ( & tok ) + ncmd . size ( ) ;
2012-02-25 10:43:10 +08:00
on_command = ( pos < = token_end ) ;
had_cmd = 1 ;
}
}
else
{
if ( is_ddash )
{
had_ddash = 1 ;
}
}
break ;
}
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 ;
}
case TOK_ERROR :
{
end_loop = 1 ;
break ;
}
}
if ( tok_get_pos ( & tok ) > = pos )
{
end_loop = 1 ;
}
tok_next ( & tok ) ;
}
tok_destroy ( & tok ) ;
/*
Get the string to complete
*/
current_token = wcsndup ( tok_begin , cursor_pos - ( tok_begin - cmd_cstr ) ) ;
prev_token = prev_begin ? wcsndup ( prev_begin , prev_end - prev_begin ) : wcsdup ( L " " ) ;
// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
/*
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 .
*/
if ( ( on_command | | ( wcscmp ( current_token , L " -- " ) = = 0 ) ) & &
( current_token [ 0 ] = = L ' - ' ) & &
! ( use_command & & use_function & & use_builtin ) )
{
if ( use_command = = 0 )
current_command = L " builtin " ;
else
current_command = L " command " ;
had_cmd = 1 ;
on_command = 0 ;
}
/*
Use command completions if in between commands
*/
if ( ! had_cmd )
{
on_command = 1 ;
}
/*
We don ' t want these to be null
*/
if ( ! current_token )
{
current_token = wcsdup ( L " " ) ;
}
if ( ! prev_token )
{
prev_token = wcsdup ( L " " ) ;
}
if ( current_token & & prev_token )
{
if ( on_command )
{
/* Complete command filename */
completer . complete_cmd ( current_token , use_function , use_builtin , use_command ) ;
}
else
{
int do_file = 0 ;
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 ) )
{
do_file = completer . complete_param ( current_command_unescape ,
prev_token_unescape ,
current_token_unescape ,
! had_ddash ) ;
}
/*
If we have found no command specific completions at
all , fall back to using file completions .
*/
if ( completer . empty ( ) )
do_file = 1 ;
/*
This function wants the unescaped string
*/
completer . complete_param_expand ( current_token , do_file ) ;
}
}
}
free ( ( void * ) current_token ) ;
free ( ( void * ) prev_token ) ;
comps = completer . get_completions ( ) ;
2012-02-27 12:11:34 +08:00
completer . get_commands_to_load ( commands_to_load ) ;
2012-02-25 10:43:10 +08:00
}
2012-01-17 00:56:47 +08:00
2005-10-24 23:26:25 +08: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-02-23 02:51:06 +08:00
static void append_switch ( wcstring & out ,
2012-02-09 06:47:50 +08:00
const wcstring & opt ,
const wcstring & argument )
2005-09-20 21:26:39 +08:00
{
2012-02-09 06:47:50 +08:00
if ( argument . empty ( ) )
2005-09-20 21:26:39 +08:00
return ;
2012-02-09 06:47:50 +08:00
wcstring esc = escape_string ( argument , 1 ) ;
2012-02-23 02:51:06 +08:00
append_format ( out , L " --%ls %ls " , opt . c_str ( ) , esc . c_str ( ) ) ;
2005-09-20 21:26:39 +08:00
}
2012-02-23 02:51:06 +08:00
void complete_print ( wcstring & out )
2005-09-20 21:26:39 +08:00
{
2012-02-27 05:27:31 +08:00
scoped_lock locker ( completion_lock ) ;
2012-02-27 06:32:06 +08:00
scoped_lock locker2 ( completion_entry_lock ) ;
2012-04-12 09:25:37 +08: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 ) ;
for ( std : : vector < const completion_entry_t * > : : const_iterator iter = all_completions . begin ( ) ; iter ! = all_completions . end ( ) ; + + iter )
2012-02-09 06:47:50 +08:00
{
2012-02-16 03:33:41 +08:00
const completion_entry_t * e = * iter ;
2012-02-27 06:32:06 +08:00
const option_list_t options = e - > get_options ( ) ;
2012-03-02 06:56:34 +08:00
for ( option_list_t : : const_iterator oiter = options . begin ( ) ; oiter ! = options . end ( ) ; + + oiter )
2012-02-09 08:15:53 +08:00
{
const complete_entry_opt_t * o = & * oiter ;
2012-01-14 15:44:18 +08:00
const wchar_t * modestr [ ] =
2005-09-20 21:26:39 +08:00
{
L " " ,
L " --no-files " ,
L " --require-parameter " ,
L " --exclusive "
}
;
2012-02-23 02:51:06 +08:00
append_format ( out ,
2005-09-20 21:26:39 +08:00
L " complete%ls " ,
modestr [ o - > result_mode ] ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
append_switch ( out ,
2012-02-27 05:27:31 +08:00
e - > cmd_is_path ? L " path " : L " command " ,
2005-09-20 21:26:39 +08:00
e - > cmd ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
if ( o - > short_opt ! = 0 )
{
2012-02-23 02:51:06 +08:00
append_format ( out ,
2005-09-20 21:26:39 +08:00
L " --short-option '%lc' " ,
o - > short_opt ) ;
}
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
append_switch ( out ,
o - > old_mode ? L " old-option " : L " long-option " ,
o - > long_opt ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
append_switch ( out ,
L " description " ,
2012-02-09 08:15:53 +08:00
C_ ( o - > desc . c_str ( ) ) ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
append_switch ( out ,
L " arguments " ,
o - > comp ) ;
2006-01-31 03:53:10 +08:00
2005-09-20 21:26:39 +08:00
append_switch ( out ,
L " condition " ,
o - > condition ) ;
2006-01-31 03:53:10 +08:00
2012-02-23 02:51:06 +08:00
out . append ( L " \n " ) ;
2005-09-20 21:26:39 +08:00
}
}
}