2014-02-28 10:15:24 +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"
2013-10-13 01:17:03 +00:00
# include "parse_tree.h"
2013-11-30 07:44:26 +00:00
# include "iothread.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 ) ;
2014-09-02 21:53:19 +00:00
bool remove_option ( wchar_t short_opt , const wchar_t * long_opt , int old_mode ) ;
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 ( )
{
}
2014-01-14 22:28:06 +00:00
/* Clear the COMPLETE_AUTO_SPACE flag, and set COMPLETE_NO_SPACE appropriately depending on the suffix of the string */
static complete_flags_t resolve_auto_space ( const wcstring & comp , complete_flags_t flags )
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 ;
2014-01-14 22:28:06 +00:00
size_t len = comp . 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 ;
}
2014-01-14 22:28:06 +00:00
return flags ;
}
2012-07-18 14:18:19 +00:00
2014-01-14 22:28:06 +00:00
/* completion_t functions. Note that the constructor resolves flags! */
completion_t : : completion_t ( const wcstring & comp , const wcstring & desc , string_fuzzy_match_t mat , complete_flags_t flags_val ) :
completion ( comp ) ,
description ( desc ) ,
match ( mat ) ,
flags ( resolve_auto_space ( comp , flags_val ) )
{
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-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 ,
2014-09-20 07:26:10 +00:00
bool use_command ,
bool use_implicit_cd ) ;
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 )
2013-11-29 21:31:18 +00:00
result | = EXPAND_SKIP_CMDSUBST ;
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-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 )
{
2014-09-02 21:53:19 +00:00
complete_remove ( cmd . c_str ( ) , COMMAND , 0 , 0 , 0 ) ;
2012-01-26 02:40:08 +00:00
}
2012-01-23 19:42:41 +00:00
2014-01-14 22:28:06 +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
{
2014-01-14 22:28:06 +00:00
/* If we just constructed the completion and used push_back, we would get two string copies. Try to avoid that by making a stubby completion in the vector first, and then copying our string in. Note that completion_t's constructor will munge 'flags' so it's important that we pass those to the constructor.
2014-01-15 09:40:40 +00:00
2014-01-14 22:28:06 +00:00
Nasty hack for # 1241 - since the constructor needs the completion string to resolve AUTO_SPACE , and we aren ' t providing it with the completion , we have to do the resolution ourselves . We should get this resolving out of the constructor .
*/
const wcstring empty ;
completions . push_back ( completion_t ( empty , empty , match , resolve_auto_space ( comp , flags ) ) ) ;
2014-01-07 22:57:58 +00:00
completion_t * last = & completions . back ( ) ;
last - > completion = comp ;
last - > description = desc ;
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
*/
2014-09-02 21:53:19 +00:00
bool completion_entry_t : : remove_option ( wchar_t short_opt , const wchar_t * long_opt , int old_mode )
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 ;
2014-09-02 21:51:40 +00:00
if ( ( short_opt & & short_opt = = o . short_opt ) | |
2014-09-02 21:53:19 +00:00
( long_opt & & long_opt = = o . long_opt & & old_mode = = o . old_mode ) )
2012-11-19 00:30:30 +00:00
{
/* 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 ,
2014-09-02 21:53:19 +00:00
const wchar_t * long_opt ,
int old_mode )
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 ;
2014-09-02 21:53:19 +00:00
bool delete_it = entry - > remove_option ( short_opt , long_opt , old_mode ) ;
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 ;
2014-03-22 00:13:33 +00:00
if ( ! expand_one ( tmp , EXPAND_SKIP_CMDSUBST | EXPAND_SKIP_WILDCARDS | this - > expand_flags ( ) , NULL ) )
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
*/
2014-09-20 07:26:10 +00:00
void completer_t : : complete_cmd ( const wcstring & str_cmd , bool use_function , bool use_builtin , bool use_command , bool use_implicit_cd )
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
2014-03-22 00:13:33 +00:00
if ( expand_string ( str_cmd , this - > completions , ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) , NULL ) ! = 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
}
2014-09-20 07:26:10 +00:00
if ( use_implicit_cd )
{
( void ) expand_string ( str_cmd , this - > completions , ACCEPT_INCOMPLETE | DIRECTORIES_ONLY | this - > expand_flags ( ) , NULL ) ;
}
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 ,
2014-03-22 00:13:33 +00:00
ACCEPT_INCOMPLETE | EXECUTABLES_ONLY | this - > expand_flags ( ) , NULL ) ! = EXPAND_ERROR )
2012-11-19 00:30:30 +00:00
{
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 + + )
{
2014-01-07 22:57:58 +00:00
append_completion ( possible_comp , 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 ) ;
2014-01-01 23:29:56 +00:00
parser_t parser ( is_autosuggest ? PARSER_TYPE_COMPLETIONS_ONLY : PARSER_TYPE_GENERAL , false /* don't show errors */ ) ;
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 ) ;
2014-03-18 21:14:32 +00:00
parser . expand_argument_list ( args , 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
2013-12-20 11:28:20 +00:00
this - > complete_strings ( escape_string ( str , ESCAPE_ALL ) , 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
2013-11-30 07:44:26 +00:00
// Performed on main thread, from background thread. Return type is ignored.
static int complete_load_no_reload ( wcstring * name )
{
assert ( name ! = NULL ) ;
ASSERT_IS_MAIN_THREAD ( ) ;
complete_load ( * name , false ) ;
return 0 ;
}
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
{
2013-10-13 01:17:03 +00:00
const wchar_t * const cmd_orig = scmd_orig . c_str ( ) ;
const wchar_t * const popt = spopt . c_str ( ) ;
const wchar_t * const str = sstr . c_str ( ) ;
2012-02-26 21:27:31 +00:00
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
{
2013-11-30 07:44:26 +00:00
ASSERT_IS_MAIN_THREAD ( ) ;
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
{
2013-11-30 07:44:26 +00:00
/* Maybe load this command (on the main thread) */
if ( ! completion_autoloader . has_tried_loading ( cmd ) )
2012-02-27 04:11:34 +00:00
{
2013-11-30 07:44:26 +00:00
iothread_perform_on_main ( complete_load_no_reload , & cmd ) ;
2012-02-27 04:11:34 +00:00
}
}
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 ;
2014-01-12 21:33:35 +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
}
}
}
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 ' ) )
{
2014-08-16 01:14:36 +00:00
use_files = use_files & & ( ( o - > result_mode & NO_FILES ) = = 0 ) ;
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
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 ,
2014-03-22 00:13:33 +00:00
flags , NULL ) = = 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
2014-01-12 21:33:35 +00:00
append_completion ( this - > completions , comp , desc , 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
}
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
{
2014-02-09 23:27:04 +00:00
enum { e_unquoted , e_single_quoted , e_double_quoted } mode = e_unquoted ;
const size_t len = str . size ( ) ;
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
/* Get the position of the dollar heading a run of valid variable characters. -1 means none. */
size_t variable_start = - 1 ;
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
for ( size_t in_pos = 0 ; in_pos < len ; in_pos + + )
2012-11-18 10:23:22 +00:00
{
2014-02-09 23:27:04 +00:00
wchar_t c = str . at ( in_pos ) ;
if ( ! wcsvarchr ( c ) )
2012-11-19 00:30:30 +00:00
{
2014-02-09 23:27:04 +00:00
/* This character cannot be in a variable, reset the dollar */
variable_start = - 1 ;
2012-11-19 00:30:30 +00:00
}
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
switch ( c )
2012-11-19 00:30:30 +00:00
{
2014-02-09 23:27:04 +00:00
case L ' \\ ' :
in_pos + + ;
break ;
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
case L ' $ ' :
if ( mode = = e_unquoted | | mode = = e_double_quoted )
{
variable_start = in_pos ;
}
break ;
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
case L ' \' ' :
if ( mode = = e_single_quoted )
{
mode = e_unquoted ;
}
else if ( mode = = e_unquoted )
{
mode = e_single_quoted ;
}
break ;
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
case L ' " ' :
if ( mode = = e_double_quoted )
{
mode = e_unquoted ;
}
else if ( mode = = e_unquoted )
{
mode = e_double_quoted ;
}
break ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2014-03-31 17:01:39 +00:00
2014-02-09 23:27:04 +00:00
/* Now complete if we have a variable start that's also not the last character */
bool result = false ;
if ( variable_start ! = static_cast < size_t > ( - 1 ) & & variable_start + 1 < len )
{
result = this - > complete_variable ( str , variable_start + 1 ) ;
}
return result ;
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-12-07 20:54:43 +00:00
void complete ( const wcstring & cmd_with_subcmds , std : : vector < completion_t > & comps , completion_request_flags_t flags )
2012-02-25 02:43:10 +00:00
{
2013-10-13 01:17:03 +00:00
/* Determine the innermost subcommand */
const wchar_t * cmdsubst_begin , * cmdsubst_end ;
parse_util_cmdsubst_extent ( cmd_with_subcmds . c_str ( ) , cmd_with_subcmds . size ( ) , & cmdsubst_begin , & cmdsubst_end ) ;
assert ( cmdsubst_begin ! = NULL & & cmdsubst_end ! = NULL & & cmdsubst_end > = cmdsubst_begin ) ;
const wcstring cmd = wcstring ( cmdsubst_begin , cmdsubst_end - cmdsubst_begin ) ;
2014-01-15 09:40:40 +00:00
2012-02-25 02:43:10 +00:00
/* Make our completer */
2013-03-06 04:54:16 +00:00
completer_t completer ( cmd , flags ) ;
2014-01-15 09:40:40 +00:00
2012-11-18 10:23:22 +00:00
wcstring current_command ;
2013-10-13 01:17:03 +00:00
const size_t pos = cmd . size ( ) ;
2012-11-19 00:30:30 +00:00
bool done = false ;
2013-10-13 01:17:03 +00:00
bool use_command = 1 ;
bool use_function = 1 ;
bool use_builtin = 1 ;
2014-09-20 07:26:10 +00:00
bool use_implicit_cd = 1 ;
2014-01-15 09:40:40 +00:00
2014-03-31 17:01:39 +00:00
//debug( 1, L"Complete '%ls'", cmd.c_str() );
2014-01-15 09:40:40 +00:00
2012-02-25 02:43:10 +00:00
const wchar_t * cmd_cstr = cmd . c_str ( ) ;
2013-10-13 01:17:03 +00:00
const wchar_t * tok_begin = NULL , * prev_begin = NULL , * prev_end = NULL ;
parse_util_token_extent ( cmd_cstr , cmd . size ( ) , & tok_begin , NULL , & prev_begin , & prev_end ) ;
2014-01-15 09:40:40 +00:00
2012-11-19 00:30:30 +00:00
/**
2013-11-25 07:21:00 +00:00
If we are completing a variable name or a tilde expansion user
name , we do that and return . No need for any other completions .
*/
2013-10-13 01:17:03 +00:00
const wcstring current_token = tok_begin ;
2014-01-15 09:40:40 +00:00
2014-02-22 03:55:55 +00:00
/* Unconditionally complete variables and processes. This is a little weird since we will happily complete variables even in e.g. command position, despite the fact that they are invalid there. */
2012-11-19 00:30:30 +00:00
if ( ! done )
{
2013-10-13 01:17:03 +00:00
done = completer . try_complete_variable ( current_token ) | | completer . try_complete_user ( current_token ) ;
2012-11-19 00:30:30 +00:00
}
2014-01-15 09:40:40 +00:00
2012-11-19 00:30:30 +00:00
if ( ! done )
{
2013-10-13 01:17:03 +00:00
//const size_t prev_token_len = (prev_begin ? prev_end - prev_begin : 0);
//const wcstring prev_token(prev_begin, prev_token_len);
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
parse_node_tree_t tree ;
2014-08-24 21:27:58 +00:00
parse_tree_from_string ( cmd , parse_flag_continue_after_error | parse_flag_accept_incomplete_tokens | parse_flag_include_comments , & tree , NULL ) ;
2014-03-31 17:01:39 +00:00
2014-02-22 03:55:55 +00:00
/* Find any plain statement that contains the position. We have to backtrack past spaces (#1261). So this will be at either the last space character, or after the end of the string */
2014-01-24 02:07:21 +00:00
size_t adjusted_pos = pos ;
while ( adjusted_pos > 0 & & cmd . at ( adjusted_pos - 1 ) = = L ' ' )
2014-02-22 03:55:55 +00:00
{
2014-01-24 02:07:21 +00:00
adjusted_pos - - ;
2014-02-22 03:55:55 +00:00
}
2014-03-31 17:01:39 +00:00
2014-01-24 02:07:21 +00:00
const parse_node_t * plain_statement = tree . find_node_matching_source_location ( symbol_plain_statement , adjusted_pos , NULL ) ;
2014-02-22 03:55:55 +00:00
if ( plain_statement = = NULL )
{
2014-08-24 21:27:58 +00:00
/* Not part of a plain statement. This could be e.g. a for loop header, case expression, etc. Do generic file completions (#1309). If we had to backtrack, it means there was whitespace; don't do an autosuggestion in that case. Also don't do it if we are just after a pipe, semicolon, or & (#1631), or in a comment.
Overall this logic is a total mess . A better approach would be to return the " possible next token " from the parse tree directly ( this data is available as the first of the sequence of nodes without source locations at the very end of the parse tree ) . */
bool do_file = true ;
if ( flags & COMPLETION_REQUEST_AUTOSUGGESTION )
{
if ( adjusted_pos < pos )
{
do_file = false ;
}
else if ( pos > 0 )
{
// If the previous character is in one of these types, we don't do file suggestions
parse_token_type_t bad_types [ ] = { parse_token_type_pipe , parse_token_type_end , parse_token_type_background , parse_special_type_comment } ;
for ( size_t i = 0 ; i < sizeof bad_types / sizeof * bad_types ; i + + )
{
if ( tree . find_node_matching_source_location ( bad_types [ i ] , pos - 1 , NULL ) )
{
do_file = false ;
break ;
}
}
}
}
completer . complete_param_expand ( current_token , do_file ) ;
2014-02-22 03:55:55 +00:00
}
else
2012-11-19 00:30:30 +00:00
{
2013-10-13 01:17:03 +00:00
assert ( plain_statement - > has_source ( ) & & plain_statement - > type = = symbol_plain_statement ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
/* Get the command node */
const parse_node_t * cmd_node = tree . get_child ( * plain_statement , 0 , parse_token_type_string ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
/* Get the actual command string */
if ( cmd_node ! = NULL )
current_command = cmd_node - > get_source ( cmd ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
/* Check the decoration */
switch ( tree . decoration_for_plain_statement ( * plain_statement ) )
2012-11-19 00:30:30 +00:00
{
2013-10-13 01:17:03 +00:00
case parse_statement_decoration_none :
use_command = true ;
2013-10-13 23:46:02 +00:00
use_function = true ;
use_builtin = true ;
2014-09-20 07:26:10 +00:00
use_implicit_cd = true ;
2013-10-13 01:17:03 +00:00
break ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
case parse_statement_decoration_command :
2014-02-13 18:08:04 +00:00
case parse_statement_decoration_exec :
2013-10-13 01:17:03 +00:00
use_command = true ;
use_function = false ;
use_builtin = false ;
2014-09-20 07:26:10 +00:00
use_implicit_cd = false ;
2013-10-13 01:17:03 +00:00
break ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
case parse_statement_decoration_builtin :
use_command = false ;
use_function = false ;
use_builtin = true ;
2014-09-20 07:26:10 +00:00
use_implicit_cd = false ;
2013-10-13 01:17:03 +00:00
break ;
}
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
if ( cmd_node & & cmd_node - > location_in_or_at_end_of_source_range ( pos ) )
{
/* Complete command filename */
2014-09-20 07:26:10 +00:00
completer . complete_cmd ( current_token , use_function , use_builtin , use_command , use_implicit_cd ) ;
2013-10-13 01:17:03 +00:00
}
else
{
/* Get all the arguments */
const parse_node_tree_t : : parse_node_list_t all_arguments = tree . find_nodes ( * plain_statement , symbol_argument ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
/* See whether we are in an argument. We may also be in a redirection, or nothing at all. */
size_t matching_arg_index = - 1 ;
for ( size_t i = 0 ; i < all_arguments . size ( ) ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-10-13 01:17:03 +00:00
const parse_node_t * node = all_arguments . at ( i ) ;
if ( node - > location_in_or_at_end_of_source_range ( pos ) )
2012-11-19 00:30:30 +00:00
{
2013-10-13 01:17:03 +00:00
matching_arg_index = i ;
break ;
2012-11-19 08:31:03 +00:00
}
2013-10-13 01:17:03 +00:00
}
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
bool had_ddash = false ;
wcstring current_argument , previous_argument ;
if ( matching_arg_index ! = ( size_t ) ( - 1 ) )
{
/* Get the current argument and the previous argument, if we have one */
current_argument = all_arguments . at ( matching_arg_index ) - > get_source ( cmd ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
if ( matching_arg_index > 0 )
previous_argument = all_arguments . at ( matching_arg_index - 1 ) - > get_source ( cmd ) ;
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +00:00
/* Check to see if we have a preceding double-dash */
for ( size_t i = 0 ; i < matching_arg_index ; i + + )
2012-11-19 00:30:30 +00:00
{
2013-10-13 01:17:03 +00:00
if ( all_arguments . at ( i ) - > get_source ( cmd ) = = L " -- " )
2012-11-19 08:31:03 +00:00
{
2013-10-13 01:17:03 +00:00
had_ddash = true ;
break ;
2012-11-19 08:31:03 +00:00
}
2012-11-19 00:30:30 +00:00
}
2012-11-19 08:31:03 +00:00
}
2014-05-02 08:22:39 +00:00
/* If we are not in an argument, we may be in a redirection */
bool in_redirection = false ;
if ( matching_arg_index = = ( size_t ) ( - 1 ) )
{
2014-05-03 07:15:13 +00:00
const parse_node_t * redirection = tree . find_node_matching_source_location ( symbol_redirection , adjusted_pos , plain_statement ) ;
2014-05-02 08:22:39 +00:00
in_redirection = ( redirection ! = NULL ) ;
}
2013-10-13 01:17:03 +00:00
bool do_file = false ;
2014-05-02 08:22:39 +00:00
if ( in_redirection )
2013-09-30 20:57:36 +00:00
{
2014-05-02 08:22:39 +00:00
do_file = true ;
2013-09-30 20:57:36 +00:00
}
2014-05-02 08:22:39 +00:00
else
{
wcstring current_command_unescape , previous_argument_unescape , current_argument_unescape ;
if ( unescape_string ( current_command , & current_command_unescape , UNESCAPE_DEFAULT ) & &
unescape_string ( previous_argument , & previous_argument_unescape , UNESCAPE_DEFAULT ) & &
unescape_string ( current_argument , & current_argument_unescape , UNESCAPE_INCOMPLETE ) )
{
2014-08-16 01:14:36 +00:00
// Have to walk over the command and its entire wrap chain
// If any command disables do_file, then they all do
do_file = true ;
const wcstring_list_t wrap_chain = complete_get_wrap_chain ( current_command_unescape ) ;
for ( size_t i = 0 ; i < wrap_chain . size ( ) ; i + + )
{
// Hackish, this. The first command in the chain is always the given command. For every command past the first, we need to create a transient commandline for builtin_commandline. But not for COMPLETION_REQUEST_AUTOSUGGESTION, which may occur on background threads.
builtin_commandline_scoped_transient_t * transient_cmd = NULL ;
if ( i = = 0 )
{
assert ( wrap_chain . at ( i ) = = current_command_unescape ) ;
}
else if ( ! ( flags & COMPLETION_REQUEST_AUTOSUGGESTION ) )
{
assert ( cmd_node ! = NULL ) ;
wcstring faux_cmdline = cmd ;
faux_cmdline . replace ( cmd_node - > source_start , cmd_node - > source_length , wrap_chain . at ( i ) ) ;
transient_cmd = new builtin_commandline_scoped_transient_t ( faux_cmdline ) ;
}
if ( ! completer . complete_param ( wrap_chain . at ( i ) ,
2014-05-02 08:22:39 +00:00
previous_argument_unescape ,
current_argument_unescape ,
2014-08-16 01:14:36 +00:00
! had_ddash ) )
{
do_file = false ;
}
delete transient_cmd ; //may be null
}
2014-05-02 08:22:39 +00:00
}
2014-01-15 09:40:40 +00:00
2014-05-02 08:22:39 +00:00
/* If we have found no command specific completions at all, fall back to using file completions. */
if ( completer . empty ( ) )
do_file = true ;
2014-01-15 09:40:40 +00:00
2014-05-02 08:22:39 +00:00
/* And if we're autosuggesting, and the token is empty, don't do file suggestions */
if ( ( flags & COMPLETION_REQUEST_AUTOSUGGESTION ) & & current_argument_unescape . empty ( ) )
{
do_file = false ;
}
2014-02-22 03:55:55 +00:00
}
2014-01-15 09:40:40 +00:00
2013-10-13 01:17:03 +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
}
2014-01-15 09:40:40 +00:00
2012-11-19 00:30:30 +00:00
comps = completer . get_completions ( ) ;
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 " ,
2014-08-17 02:25:36 +00:00
escape_string ( e - > cmd , ESCAPE_ALL ) ) ;
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
}
2014-08-16 01:14:36 +00:00
/* Append wraps. This is a wonky interface where even values are the commands, and odd values are the targets that they wrap. */
const wcstring_list_t wrap_pairs = complete_get_wrap_pairs ( ) ;
assert ( wrap_pairs . size ( ) % 2 = = 0 ) ;
for ( size_t i = 0 ; i < wrap_pairs . size ( ) ; )
{
const wcstring & cmd = wrap_pairs . at ( i + + ) ;
const wcstring & target = wrap_pairs . at ( i + + ) ;
append_format ( out , L " complete --command %ls --wraps %ls \n " , cmd . c_str ( ) , target . c_str ( ) ) ;
}
}
/* Completion "wrapper" support. The map goes from wrapping-command to wrapped-command-list */
static pthread_mutex_t wrapper_lock = PTHREAD_MUTEX_INITIALIZER ;
typedef std : : map < wcstring , wcstring_list_t > wrapper_map_t ;
static wrapper_map_t & wrap_map ( )
{
ASSERT_IS_LOCKED ( wrapper_lock ) ;
// A pointer is a little more efficient than an object as a static because we can elide the thread-safe initialization
static wrapper_map_t * wrapper_map = NULL ;
if ( wrapper_map = = NULL )
{
wrapper_map = new wrapper_map_t ( ) ;
}
return * wrapper_map ;
}
/* Add a new target that is wrapped by command. Example: sgrep (command) wraps grep (target). */
bool complete_add_wrapper ( const wcstring & command , const wcstring & new_target )
{
if ( command . empty ( ) | | new_target . empty ( ) )
{
return false ;
}
scoped_lock locker ( wrapper_lock ) ;
wrapper_map_t & wraps = wrap_map ( ) ;
wcstring_list_t * targets = & wraps [ command ] ;
// If it's already present, we do nothing
if ( std : : find ( targets - > begin ( ) , targets - > end ( ) , new_target ) = = targets - > end ( ) )
{
targets - > push_back ( new_target ) ;
}
return true ;
}
bool complete_remove_wrapper ( const wcstring & command , const wcstring & target_to_remove )
{
if ( command . empty ( ) | | target_to_remove . empty ( ) )
{
return false ;
}
scoped_lock locker ( wrapper_lock ) ;
wrapper_map_t & wraps = wrap_map ( ) ;
bool result = false ;
wrapper_map_t : : iterator current_targets_iter = wraps . find ( command ) ;
if ( current_targets_iter ! = wraps . end ( ) )
{
wcstring_list_t * targets = & current_targets_iter - > second ;
wcstring_list_t : : iterator where = std : : find ( targets - > begin ( ) , targets - > end ( ) , target_to_remove ) ;
if ( where ! = targets - > end ( ) )
{
targets - > erase ( where ) ;
result = true ;
}
}
return result ;
}
wcstring_list_t complete_get_wrap_chain ( const wcstring & command )
{
if ( command . empty ( ) )
{
return wcstring_list_t ( ) ;
}
scoped_lock locker ( wrapper_lock ) ;
const wrapper_map_t & wraps = wrap_map ( ) ;
wcstring_list_t result ;
std : : set < wcstring > visited ; // set of visited commands
wcstring_list_t to_visit ( 1 , command ) ; // stack of remaining-to-visit commands
wcstring target ;
while ( ! to_visit . empty ( ) )
{
// Grab the next command to visit, put it in target
target . swap ( to_visit . back ( ) ) ;
to_visit . pop_back ( ) ;
// Try inserting into visited. If it was already present, we skip it; this is how we avoid loops.
if ( ! visited . insert ( target ) . second )
{
continue ;
}
// Insert the target in the result. Note this is the command itself, if this is the first iteration of the loop.
result . push_back ( target ) ;
// Enqueue its children
wrapper_map_t : : const_iterator target_children_iter = wraps . find ( target ) ;
if ( target_children_iter ! = wraps . end ( ) )
{
const wcstring_list_t & children = target_children_iter - > second ;
to_visit . insert ( to_visit . end ( ) , children . begin ( ) , children . end ( ) ) ;
}
}
return result ;
}
wcstring_list_t complete_get_wrap_pairs ( )
{
wcstring_list_t result ;
scoped_lock locker ( wrapper_lock ) ;
const wrapper_map_t & wraps = wrap_map ( ) ;
for ( wrapper_map_t : : const_iterator outer = wraps . begin ( ) ; outer ! = wraps . end ( ) ; + + outer )
{
const wcstring & cmd = outer - > first ;
const wcstring_list_t & targets = outer - > second ;
for ( size_t i = 0 ; i < targets . size ( ) ; i + + )
{
result . push_back ( cmd ) ;
result . push_back ( targets . at ( i ) ) ;
}
}
return result ;
2005-09-20 13:26:39 +00:00
}