Some cleanup of completions in preparation for more multithreading

This commit is contained in:
ridiculousfish 2012-02-26 13:27:31 -08:00
parent 8f637975a4
commit b30090f946
5 changed files with 124 additions and 438 deletions

View file

@ -156,13 +156,15 @@ typedef struct complete_entry_opt
Struct describing a command completion Struct describing a command completion
*/ */
typedef std::list<complete_entry_opt_t> option_list_t; typedef std::list<complete_entry_opt_t> option_list_t;
struct completion_entry_t class completion_entry_t
{ {
/** True if command is a path */ public:
int cmd_type;
/** Command string */ /** Command string */
wcstring cmd; wcstring cmd;
/** True if command is a path */
bool cmd_is_path;
/** String containing all short option characters */ /** String containing all short option characters */
wcstring short_opt_str; wcstring short_opt_str;
@ -170,7 +172,15 @@ struct completion_entry_t
option_list_t options; option_list_t options;
/** True if no other options than the ones supplied are possible */ /** True if no other options than the ones supplied are possible */
int authoritative; bool authoritative;
completion_entry_t(const wcstring &c, bool type, const wcstring &options, bool author) :
cmd(c),
cmd_is_path(type),
short_opt_str(options),
authoritative(author)
{
}
}; };
/** Linked list of all completion entries */ /** Linked list of all completion entries */
@ -178,22 +188,21 @@ typedef std::list<completion_entry_t *> completion_entry_list_t;
static completion_entry_list_t completion_entries; static completion_entry_list_t completion_entries;
static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER;
/**
Table of completions conditions that have already been tested and
the corresponding test results
*/
static std::map<wcstring, bool> condition_cache;
/** Class representing an attempt to compute completions */ /** Class representing an attempt to compute completions */
class completer_t { class completer_t {
const complete_type_t type; const complete_type_t type;
const wcstring cmd; const wcstring initial_cmd;
std::vector<completion_t> completions; std::vector<completion_t> completions;
/** 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;
public: public:
completer_t(const wcstring &c, complete_type_t t) : completer_t(const wcstring &c, complete_type_t t) :
type(t), type(t),
cmd(c), initial_cmd(c),
completions() completions()
{ {
} }
@ -215,8 +224,17 @@ class completer_t {
bool use_function, bool use_function,
bool use_builtin, bool use_builtin,
bool use_command); bool use_command);
void complete_from_args( const wcstring &str,
const wcstring &args,
const wcstring &desc,
complete_flags_t flags );
void complete_cmd_desc( const wcstring &str );
bool complete_variable(const wcstring &str, int start_offset); bool complete_variable(const wcstring &str, int start_offset);
bool condition_test( const wcstring &condition );
}; };
@ -251,28 +269,13 @@ void completion_allocate(std::vector<completion_t> &completions, const wcstring
completions.push_back(completion_t(comp, desc, flags)); completions.push_back(completion_t(comp, desc, flags));
} }
/**
The init function for the completion code. Does nothing.
*/
static void complete_init()
{
}
/**
This command clears the cache of condition tests created by \c condition_test().
*/
static void condition_cache_clear()
{
condition_cache.clear();
}
/** /**
Test if the specified script returns zero. The result is cached, so Test if the specified script returns zero. The result is cached, so
that if multiple completions use the same condition, it needs only that if multiple completions use the same condition, it needs only
be evaluated once. condition_cache_clear must be called after a be evaluated once. condition_cache_clear must be called after a
completion run to make sure that there are no stale completions. completion run to make sure that there are no stale completions.
*/ */
static int condition_test( const wcstring &condition, complete_type_t type ) bool completer_t::condition_test( const wcstring &condition )
{ {
if( condition.empty() ) if( condition.empty() )
{ {
@ -280,7 +283,7 @@ static int condition_test( const wcstring &condition, complete_type_t type )
return 1; return 1;
} }
if (type == COMPLETE_AUTOSUGGEST) if (this->type == COMPLETE_AUTOSUGGEST)
{ {
/* Autosuggestion can't support conditions */ /* Autosuggestion can't support conditions */
return 0; return 0;
@ -288,11 +291,8 @@ static int condition_test( const wcstring &condition, complete_type_t type )
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
printf("condition_test %ls\n", condition.c_str());
bool test_res; bool test_res;
std::map<wcstring, bool>::iterator cached_entry = condition_cache.find(condition); condition_cache_t::iterator cached_entry = condition_cache.find(condition);
if (cached_entry == condition_cache.end()) { if (cached_entry == condition_cache.end()) {
/* Compute new value and reinsert it */ /* Compute new value and reinsert it */
test_res = (0 == exec_subshell( condition)); test_res = (0 == exec_subshell( condition));
@ -306,57 +306,49 @@ static int condition_test( const wcstring &condition, complete_type_t type )
/** Search for an exactly matching completion entry. Must be called while locked. */ /** Search for an exactly matching completion entry. Must be called while locked. */
static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd, const int cmd_type ) static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd, const bool cmd_is_path )
{ {
ASSERT_IS_LOCKED(completion_lock); ASSERT_IS_LOCKED(completion_lock);
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{ {
completion_entry_t *entry = *iter; completion_entry_t *entry = *iter;
if (entry->cmd == cmd && cmd_type == entry->cmd_type) if (entry->cmd == cmd && cmd_is_path == entry->cmd_is_path)
return entry; return entry;
} }
return NULL; return NULL;
} }
/** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */ /** Locate the specified entry. Create it if it doesn't exist. Must be called while locked. */
static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd, int cmd_type ) static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd, bool cmd_is_path )
{ {
ASSERT_IS_LOCKED(completion_lock); ASSERT_IS_LOCKED(completion_lock);
completion_entry_t *c; completion_entry_t *c;
complete_init(); c = complete_find_exact_entry( cmd, cmd_is_path );
c = complete_find_exact_entry( cmd, cmd_type );
if( c == NULL ) if( c == NULL )
{ {
c = new completion_entry_t(); c = new completion_entry_t(cmd, cmd_is_path, L"", true);
completion_entries.push_front(c); completion_entries.push_front(c);
c->cmd = cmd;
c->cmd_type = cmd_type;
c->short_opt_str = L"";
c->authoritative = 1;
} }
return c; return c;
} }
void complete_set_authoritative( const wchar_t *cmd, void complete_set_authoritative( const wchar_t *cmd, bool cmd_is_path, bool authoritative )
int cmd_type,
int authoritative )
{ {
completion_entry_t *c; completion_entry_t *c;
CHECK( cmd, ); CHECK( cmd, );
scoped_lock lock(completion_lock); scoped_lock lock(completion_lock);
c = complete_get_exact_entry( cmd, cmd_type ); c = complete_get_exact_entry( cmd, cmd_is_path );
c->authoritative = authoritative; c->authoritative = authoritative;
} }
void complete_add( const wchar_t *cmd, void complete_add( const wchar_t *cmd,
int cmd_type, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt, const wchar_t *long_opt,
int old_mode, int old_mode,
@ -370,7 +362,7 @@ void complete_add( const wchar_t *cmd,
scoped_lock lock(completion_lock); scoped_lock lock(completion_lock);
completion_entry_t *c; completion_entry_t *c;
c = complete_get_exact_entry( cmd, cmd_type ); c = complete_get_exact_entry( cmd, cmd_is_path );
c->options.push_front(complete_entry_opt_t()); c->options.push_front(complete_entry_opt_t());
complete_entry_opt_t &opt = c->options.front(); complete_entry_opt_t &opt = c->options.front();
@ -449,7 +441,7 @@ static bool complete_remove_entry( completion_entry_t *e, wchar_t short_opt, con
void complete_remove( const wchar_t *cmd, void complete_remove( const wchar_t *cmd,
int cmd_type, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt ) const wchar_t *long_opt )
{ {
@ -458,7 +450,7 @@ void complete_remove( const wchar_t *cmd,
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); ) { for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); ) {
completion_entry_t *e = *iter; completion_entry_t *e = *iter;
bool delete_it = false; bool delete_it = false;
if(cmd_type == e->cmd_type && cmd == e->cmd) { if(cmd_is_path == e->cmd_is_path && cmd == e->cmd) {
delete_it = complete_remove_entry( e, short_opt, long_opt ); delete_it = complete_remove_entry( e, short_opt, long_opt );
} }
@ -579,7 +571,7 @@ int complete_is_valid_option( const wchar_t *str,
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{ {
const completion_entry_t *i = *iter; const completion_entry_t *i = *iter;
const wcstring &match = i->cmd_type?path:cmd; const wcstring &match = i->cmd_is_path ? path : cmd;
const wchar_t *a; const wchar_t *a;
if( !wildcard_match( match, i->cmd ) ) if( !wildcard_match( match, i->cmd ) )
@ -785,7 +777,7 @@ static void complete_strings( std::vector<completion_t> &comp_out,
If command to complete is short enough, substitute If command to complete is short enough, substitute
the description with the whatis information for the executable. the description with the whatis information for the executable.
*/ */
static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &comp ) void completer_t::complete_cmd_desc( const wcstring &str )
{ {
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
@ -793,9 +785,7 @@ static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &co
int cmd_len; int cmd_len;
int skip; int skip;
if( !cmd ) const wchar_t * const cmd = str.c_str();
return;
cmd_start=wcsrchr(cmd, L'/'); cmd_start=wcsrchr(cmd, L'/');
if( cmd_start ) if( cmd_start )
@ -820,9 +810,9 @@ static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &co
skip = 1; skip = 1;
for( size_t i=0; i< comp.size(); i++ ) for( size_t i=0; i< this->completions.size(); i++ )
{ {
const completion_t &c = comp.at ( i ); const completion_t &c = this->completions.at ( i );
if( c.completion.empty() || (c.completion[c.completion.size()-1] != L'/' )) if( c.completion.empty() || (c.completion[c.completion.size()-1] != L'/' ))
{ {
@ -892,9 +882,9 @@ static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &co
This needs to do a reallocation for every description added, but This needs to do a reallocation for every description added, but
there shouldn't be that many completions, so it should be ok. there shouldn't be that many completions, so it should be ok.
*/ */
for( size_t i=0; i<comp.size(); i++ ) for( size_t i=0; i<this->completions.size(); i++ )
{ {
completion_t &completion = comp.at(i); completion_t &completion = this->completions.at(i);
const wcstring &el = completion.completion; const wcstring &el = completion.completion;
if (el.empty()) if (el.empty())
continue; continue;
@ -930,13 +920,9 @@ static const wchar_t *complete_function_desc( const wcstring &fn )
\param comp the list to add all completions to \param comp the list to add all completions to
*/ */
static void complete_cmd( const wchar_t *cmd, void completer_t::complete_cmd( const wcstring &str, bool use_function, bool use_builtin, bool use_command)
std::vector<completion_t> &comp,
int use_function,
int use_builtin,
int use_command,
complete_type_t type )
{ {
const wchar_t * const cmd = str.c_str();
wchar_t *path_cpy; wchar_t *path_cpy;
wchar_t *nxt_path; wchar_t *nxt_path;
wchar_t *state; wchar_t *state;
@ -953,9 +939,9 @@ static void complete_cmd( const wchar_t *cmd,
if( use_command && wants_description ) if( use_command && wants_description )
{ {
if( expand_string(cmd, comp, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) != EXPAND_ERROR ) if( expand_string(str, this->completions, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) != EXPAND_ERROR )
{ {
complete_cmd_desc( cmd, comp ); this->complete_cmd_desc( str );
} }
} }
} }
@ -990,16 +976,16 @@ static void complete_cmd( const wchar_t *cmd,
if( ! nxt_completion ) if( ! nxt_completion )
continue; continue;
prev_count = comp.size() ; prev_count = this->completions.size() ;
if( expand_string( nxt_completion, if( expand_string( nxt_completion,
comp, this->completions,
ACCEPT_INCOMPLETE | ACCEPT_INCOMPLETE |
EXECUTABLES_ONLY ) != EXPAND_ERROR ) EXECUTABLES_ONLY ) != EXPAND_ERROR )
{ {
for( size_t i=prev_count; i< comp.size(); i++ ) for( size_t i=prev_count; i< this->completions.size(); i++ )
{ {
completion_t &c = comp.at( i ); completion_t &c = this->completions.at( i );
if(c.flags & COMPLETE_NO_CASE ) if(c.flags & COMPLETE_NO_CASE )
{ {
c.completion += add_slash ; c.completion += add_slash ;
@ -1009,7 +995,7 @@ static void complete_cmd( const wchar_t *cmd,
} }
free( path_cpy ); free( path_cpy );
if (wants_description) if (wants_description)
complete_cmd_desc( cmd, comp ); this->complete_cmd_desc( str );
} }
} }
@ -1025,7 +1011,7 @@ static void complete_cmd( const wchar_t *cmd,
possible_comp.push_back(completion_t(names.at(i))); possible_comp.push_back(completion_t(names.at(i)));
} }
complete_strings( comp, cmd, 0, &complete_function_desc, possible_comp, 0 ); complete_strings( this->completions, cmd, 0, &complete_function_desc, possible_comp, 0 );
} }
possible_comp.clear(); possible_comp.clear();
@ -1033,7 +1019,7 @@ static void complete_cmd( const wchar_t *cmd,
if( use_builtin ) if( use_builtin )
{ {
builtin_get_names( possible_comp ); builtin_get_names( possible_comp );
complete_strings( comp, cmd, 0, &builtin_get_desc, possible_comp, 0 ); complete_strings( this->completions, cmd, 0, &builtin_get_desc, possible_comp, 0 );
} }
// al_destroy( &possible_comp ); // al_destroy( &possible_comp );
@ -1059,8 +1045,8 @@ static void complete_cmd( const wchar_t *cmd,
continue; continue;
} }
if( expand_string( nxt_completion, if( expand_string( nxt_completion,
comp, this->completions,
ACCEPT_INCOMPLETE | DIRECTORIES_ONLY ) != EXPAND_ERROR ) ACCEPT_INCOMPLETE | DIRECTORIES_ONLY ) != EXPAND_ERROR )
{ {
} }
@ -1071,10 +1057,6 @@ static void complete_cmd( const wchar_t *cmd,
free( cdpath_cpy ); free( cdpath_cpy );
} }
void completer_t::complete_cmd( const wcstring &str, bool use_function, bool use_builtin, bool use_command)
{
::complete_cmd( str.c_str(), this->completions, use_function, use_builtin, use_command, this->type);
}
/** /**
Evaluate the argument list (as supplied by complete -a) and insert Evaluate the argument list (as supplied by complete -a) and insert
@ -1088,12 +1070,10 @@ void completer_t::complete_cmd( const wcstring &str, bool use_function, bool use
\param desc Description of the completion \param desc Description of the completion
\param comp_out The list into which the results will be inserted \param comp_out The list into which the results will be inserted
*/ */
static void complete_from_args( const wcstring &str, void completer_t::complete_from_args( const wcstring &str,
const wcstring &args, const wcstring &args,
const wcstring &desc, const wcstring &desc,
std::vector<completion_t> &comp_out, complete_flags_t flags )
complete_type_t type,
complete_flags_t flags )
{ {
/* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */ /* If type is COMPLETE_AUTOSUGGEST, it means we're on a background thread, so don't call proc_push_interactive */
@ -1101,15 +1081,15 @@ static void complete_from_args( const wcstring &str,
parser_t parser(PARSER_TYPE_COMPLETIONS_ONLY); parser_t parser(PARSER_TYPE_COMPLETIONS_ONLY);
if (type != COMPLETE_AUTOSUGGEST) if (this->type != COMPLETE_AUTOSUGGEST)
proc_push_interactive(0); proc_push_interactive(0);
parser.eval_args( args.c_str(), possible_comp ); parser.eval_args( args.c_str(), possible_comp );
if (type != COMPLETE_AUTOSUGGEST) if (this->type != COMPLETE_AUTOSUGGEST)
proc_pop_interactive(); proc_pop_interactive();
complete_strings( comp_out, str.c_str(), desc.c_str(), 0, possible_comp, flags ); complete_strings( this->completions, str.c_str(), desc.c_str(), 0, possible_comp, flags );
} }
/** /**
@ -1212,14 +1192,11 @@ void complete_load( const wcstring &name, bool reload )
previous option popt. Insert results into comp_out. Return 0 if file previous option popt. Insert results into comp_out. Return 0 if file
completion should be disabled, 1 otherwise. completion should be disabled, 1 otherwise.
*/ */
static int complete_param( const wchar_t *cmd_orig, bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches)
const wchar_t *popt,
const wchar_t *str,
int use_switches,
complete_type_t type,
std::vector<completion_t> &comp_out)
{ {
const wchar_t * const cmd_orig = scmd_orig.c_str(), * const popt = spopt.c_str(), * const str = sstr.c_str();
int use_common=1, use_files=1; int use_common=1, use_files=1;
wcstring cmd, path; wcstring cmd, path;
@ -1228,10 +1205,11 @@ static int complete_param( const wchar_t *cmd_orig,
if (type == COMPLETE_DEFAULT) if (type == COMPLETE_DEFAULT)
complete_load( cmd, true ); complete_load( cmd, true );
scoped_lock lock(completion_lock);
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{ {
completion_entry_t *i = *iter; completion_entry_t *i = *iter;
const wcstring &match = i->cmd_type?path:cmd; const wcstring &match = i->cmd_is_path ? path : cmd;
if( ( (!wildcard_match( match, i->cmd ) ) ) ) if( ( (!wildcard_match( match, i->cmd ) ) ) )
{ {
@ -1250,11 +1228,11 @@ static int complete_param( const wchar_t *cmd_orig,
{ {
const complete_entry_opt_t *o = &*oiter; const complete_entry_opt_t *o = &*oiter;
wchar_t *arg; wchar_t *arg;
if( (arg=param_match2( o, str ))!=0 && condition_test( o->condition, type )) if( (arg=param_match2( o, str ))!=0 && this->condition_test( o->condition ))
{ {
use_common &= ((o->result_mode & NO_COMMON )==0); use_common &= ((o->result_mode & NO_COMMON )==0);
use_files &= ((o->result_mode & NO_FILES )==0); use_files &= ((o->result_mode & NO_FILES )==0);
complete_from_args( arg, o->comp, o->localized_desc(), comp_out, type, o->flags ); complete_from_args( arg, o->comp, o->localized_desc(), o->flags );
} }
} }
@ -1273,12 +1251,12 @@ static int complete_param( const wchar_t *cmd_orig,
const complete_entry_opt_t *o = &*oiter; const complete_entry_opt_t *o = &*oiter;
if( o->old_mode ) if( o->old_mode )
{ {
if( param_match_old( o, popt ) && condition_test( o->condition, type )) if( param_match_old( o, popt ) && this->condition_test( o->condition ))
{ {
old_style_match = 1; old_style_match = 1;
use_common &= ((o->result_mode & NO_COMMON )==0); use_common &= ((o->result_mode & NO_COMMON )==0);
use_files &= ((o->result_mode & NO_FILES )==0); use_files &= ((o->result_mode & NO_FILES )==0);
complete_from_args( str, o->comp, o->localized_desc(), comp_out, type, o->flags ); complete_from_args( str, o->comp, o->localized_desc(), o->flags );
} }
} }
} }
@ -1301,11 +1279,11 @@ static int complete_param( const wchar_t *cmd_orig,
if( !o->old_mode && ! o->long_opt.empty() && !(o->result_mode & NO_COMMON) ) if( !o->old_mode && ! o->long_opt.empty() && !(o->result_mode & NO_COMMON) )
continue; continue;
if( param_match( o, popt ) && condition_test( o->condition, type )) if( param_match( o, popt ) && this->condition_test( o->condition ))
{ {
use_common &= ((o->result_mode & NO_COMMON )==0); use_common &= ((o->result_mode & NO_COMMON )==0);
use_files &= ((o->result_mode & NO_FILES )==0); use_files &= ((o->result_mode & NO_FILES )==0);
complete_from_args( str, o->comp.c_str(), o->localized_desc(), comp_out, type, o->flags ); complete_from_args( str, o->comp.c_str(), o->localized_desc(), o->flags );
} }
} }
@ -1324,14 +1302,14 @@ static int complete_param( const wchar_t *cmd_orig,
check if any of the arguments match check if any of the arguments match
*/ */
if( !condition_test( o->condition, type )) if( !this->condition_test( o->condition ))
continue; continue;
if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0')) if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0'))
{ {
use_files &= ((o->result_mode & NO_FILES )==0); use_files &= ((o->result_mode & NO_FILES )==0);
complete_from_args( str, o->comp, o->localized_desc(), comp_out, type, o->flags ); complete_from_args( str, o->comp, o->localized_desc(), o->flags );
} }
if( wcslen(str) > 0 && use_switches ) if( wcslen(str) > 0 && use_switches )
@ -1347,7 +1325,7 @@ static int complete_param( const wchar_t *cmd_orig,
completion[0] = o->short_opt; completion[0] = o->short_opt;
completion[1] = 0; completion[1] = 0;
completion_allocate( comp_out, completion, desc, 0 ); completion_allocate( this->completions, completion, desc, 0 );
} }
@ -1399,14 +1377,14 @@ static int complete_param( const wchar_t *cmd_orig,
homebrew getopt-like functions. homebrew getopt-like functions.
*/ */
wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset); wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset);
completion_allocate( comp_out, completion_allocate( this->completions,
completion, completion,
C_(o->desc.c_str()), C_(o->desc.c_str()),
flags ); flags );
} }
completion_allocate( comp_out, completion_allocate( this->completions,
whole_opt.c_str() + offset, whole_opt.c_str() + offset,
C_(o->desc.c_str()), C_(o->desc.c_str()),
flags ); flags );
@ -1420,16 +1398,12 @@ static int complete_param( const wchar_t *cmd_orig,
return use_files; return use_files;
} }
bool completer_t::complete_param( const wcstring &cmd_orig, const wcstring &popt, const wcstring &str, bool use_switches)
{
return ::complete_param(cmd_orig.c_str(), popt.c_str(), str.c_str(), use_switches, this->type, this->completions);
}
/** /**
Perform file completion on the specified string Perform file completion on the specified string
*/ */
static void complete_param_expand( const wchar_t *str, std::vector<completion_t> &comp_out, int do_file, complete_type_t type ) void completer_t::complete_param_expand( const wcstring &sstr, bool do_file)
{ {
const wchar_t * const str = sstr.c_str();
const wchar_t *comp_str; const wchar_t *comp_str;
if( (wcsncmp( str, L"--", 2 )) == 0 && (comp_str = wcschr(str, L'=' ) ) ) if( (wcsncmp( str, L"--", 2 )) == 0 && (comp_str = wcschr(str, L'=' ) ) )
@ -1450,7 +1424,7 @@ static void complete_param_expand( const wchar_t *str, std::vector<completion_t>
flags |= EXPAND_NO_DESCRIPTIONS; flags |= EXPAND_NO_DESCRIPTIONS;
if( expand_string( comp_str, if( expand_string( comp_str,
comp_out, this->completions,
flags ) == EXPAND_ERROR ) flags ) == EXPAND_ERROR )
{ {
debug( 3, L"Error while expanding string '%ls'", comp_str ); debug( 3, L"Error while expanding string '%ls'", comp_str );
@ -1458,19 +1432,12 @@ static void complete_param_expand( const wchar_t *str, std::vector<completion_t>
} }
void completer_t::complete_param_expand( const wcstring &str, bool do_file)
{
::complete_param_expand(str.c_str(), this->completions, do_file, this->type);
}
/** /**
Complete the specified string as an environment variable Complete the specified string as an environment variable
*/ */
static int complete_variable( const wchar_t *whole_var, bool completer_t::complete_variable(const wcstring &str, int start_offset)
int start_offset,
std::vector<completion_t> &comp_list,
complete_type_t type)
{ {
const wchar_t * const whole_var = str.c_str();
const wchar_t *var = &whole_var[start_offset]; const wchar_t *var = &whole_var[start_offset];
int varlen = wcslen( var ); int varlen = wcslen( var );
int res = 0; int res = 0;
@ -1523,10 +1490,7 @@ static int complete_variable( const wchar_t *whole_var,
desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str()); desc = format_string(COMPLETE_VAR_DESC_VAL, value.c_str());
} }
completion_allocate( comp_list, completion_allocate( this->completions, comp.c_str(), desc.c_str(), flags );
comp.c_str(),
desc.c_str(),
flags );
res =1; res =1;
} }
@ -1535,40 +1499,29 @@ static int complete_variable( const wchar_t *whole_var,
return res; return res;
} }
bool completer_t::complete_variable(const wcstring &str, int start_offset)
{
return ::complete_variable(str.c_str(), start_offset, this->completions, this->type);
}
/** /**
Search the specified string for the \$ sign. If found, try to Search the specified string for the \$ sign. If found, try to
complete as an environment variable. complete as an environment variable.
\return 0 if unable to complete, 1 otherwise \return 0 if unable to complete, 1 otherwise
*/ */
static int try_complete_variable( const wchar_t *cmd, std::vector<completion_t> &comp, complete_type_t type )
{
int len = wcslen( cmd );
int i;
for( i=len-1; i>=0; i-- )
{
if( cmd[i] == L'$' )
{
/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/
return complete_variable( cmd, i+1, comp, type);
}
if( !isalnum(cmd[i]) && cmd[i]!=L'_' )
{
return 0;
}
}
return 0;
}
bool completer_t::try_complete_variable( const wcstring &str ) bool completer_t::try_complete_variable( const wcstring &str )
{ {
return ::try_complete_variable(str.c_str(), this->completions, this->type) > 0; size_t i, len = str.size();
for( i=len-1; i>=0; i-- )
{
wchar_t c = str.at(i);
if( c == L'$' )
{
/* wprintf( L"Var prefix \'%ls\'\n", &cmd[i+1] );*/
return this->complete_variable( str, i+1 );
}
if( !isalnum(c) && c != L'_' )
{
return false;
}
}
return false;
} }
/** /**
@ -1577,8 +1530,9 @@ bool completer_t::try_complete_variable( const wcstring &str )
\return 0 if unable to complete, 1 otherwise \return 0 if unable to complete, 1 otherwise
*/ */
static int try_complete_user( const wchar_t *cmd, std::vector<completion_t> &comp ) bool completer_t::try_complete_user( const wcstring &str )
{ {
const wchar_t *cmd = str.c_str();
const wchar_t *first_char=cmd; const wchar_t *first_char=cmd;
int res=0; int res=0;
double start_time = timef(); double start_time = timef();
@ -1611,7 +1565,7 @@ static int try_complete_user( const wchar_t *cmd, std::vector<completion_t> &com
if( wcsncmp( user_name, pw_name, name_len )==0 ) if( wcsncmp( user_name, pw_name, name_len )==0 )
{ {
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
completion_allocate( comp, completion_allocate( this->completions,
&pw_name[name_len], &pw_name[name_len],
desc, desc,
COMPLETE_NO_SPACE ); COMPLETE_NO_SPACE );
@ -1623,7 +1577,7 @@ static int try_complete_user( const wchar_t *cmd, std::vector<completion_t> &com
wcstring name = format_string(L"~%ls", pw_name); wcstring name = format_string(L"~%ls", pw_name);
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
completion_allocate( comp, completion_allocate( this->completions,
name, name,
desc, desc,
COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE ); COMPLETE_NO_CASE | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE );
@ -1639,12 +1593,7 @@ static int try_complete_user( const wchar_t *cmd, std::vector<completion_t> &com
return res; return res;
} }
bool completer_t::try_complete_user( const wcstring &str ) void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_type_t type )
{
return ::try_complete_user(str.c_str(), this->completions) > 0;
}
void complete2( const wcstring &cmd, std::vector<completion_t> &comps, complete_type_t type )
{ {
/* Make our completer */ /* Make our completer */
completer_t completer(cmd, type); completer_t completer(cmd, type);
@ -1663,10 +1612,6 @@ void complete2( const wcstring &cmd, std::vector<completion_t> &comps, complete_
int use_builtin = 1; int use_builtin = 1;
int had_ddash = 0; int had_ddash = 0;
// CHECK( comp, );
complete_init();
// debug( 1, L"Complete '%ls'", cmd ); // debug( 1, L"Complete '%ls'", cmd );
cursor_pos = cmd.size(); cursor_pos = cmd.size();
@ -1888,266 +1833,6 @@ void complete2( const wcstring &cmd, std::vector<completion_t> &comps, complete_
comps = completer.get_completions(); comps = completer.get_completions();
} }
void complete( const wchar_t *cmd, std::vector<completion_t> &comp, complete_type_t type )
{
const wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
wcstring buff;
tokenizer tok;
const wchar_t *current_token=0, *current_command=0, *prev_token=0;
int on_command=0;
int pos;
int done=0;
int cursor_pos;
int use_command = 1;
int use_function = 1;
int use_builtin = 1;
int had_ddash = 0;
CHECK( cmd, );
// CHECK( comp, );
complete_init();
// debug( 1, L"Complete '%ls'", cmd );
cursor_pos = wcslen(cmd );
parse_util_cmdsubst_extent( cmd, cursor_pos, &cmdsubst_begin, &cmdsubst_end );
parse_util_token_extent( cmd, 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 )
{
if( try_complete_variable( tok_begin, comp, type ) || try_complete_user( tok_begin, comp ))
{
done=1;
}
}
if( !done )
{
pos = cursor_pos-(cmdsubst_begin-cmd);
buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin );
}
if( !done )
{
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 ) ) )
{
int token_end;
free( (void *)current_command );
current_command = wcsdup( ncmd.c_str() );
token_end = tok_get_pos( &tok ) + ncmd.size();
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) );
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 ) )
{
free( (void *)current_command );
if( use_command == 0 )
current_command = wcsdup( L"builtin" );
else
current_command = wcsdup( 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( !current_command )
{
current_command = wcsdup(L"");
}
if( !prev_token )
{
prev_token = wcsdup(L"");
}
if( current_token && current_command && prev_token )
{
if( on_command )
{
/* Complete command filename */
complete_cmd( current_token, comp, use_function, use_builtin, use_command, type );
}
else
{
int do_file=0;
wchar_t *current_command_unescape = unescape( current_command, 0 );
wchar_t *prev_token_unescape = unescape( prev_token, 0 );
wchar_t *current_token_unescape = unescape( current_token, UNESCAPE_INCOMPLETE );
if( current_token_unescape && prev_token_unescape && current_token_unescape )
{
do_file = complete_param( current_command_unescape,
prev_token_unescape,
current_token_unescape,
!had_ddash,
type,
comp );
}
free( current_command_unescape );
free( prev_token_unescape );
free( current_token_unescape );
/*
If we have found no command specific completions at
all, fall back to using file completions.
*/
if( comp.empty() )
do_file = 1;
/*
This function wants the unescaped string
*/
complete_param_expand( current_token, comp, do_file, type );
}
}
}
free( (void *)current_token );
free( (void *)current_command );
free( (void *)prev_token );
condition_cache_clear();
}
/** /**
@ -2168,6 +1853,7 @@ static void append_switch( wcstring &out,
void complete_print( wcstring &out ) void complete_print( wcstring &out )
{ {
scoped_lock locker(completion_lock);
for (completion_entry_list_t::const_iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) for (completion_entry_list_t::const_iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{ {
const completion_entry_t *e = *iter; const completion_entry_t *e = *iter;
@ -2188,7 +1874,7 @@ void complete_print( wcstring &out )
modestr[o->result_mode] ); modestr[o->result_mode] );
append_switch( out, append_switch( out,
e->cmd_type?L"path":L"command", e->cmd_is_path ? L"path" : L"command",
e->cmd ); e->cmd );

View file

@ -198,7 +198,7 @@ enum complete_type_t {
\param flags A set of completion flags \param flags A set of completion flags
*/ */
void complete_add( const wchar_t *cmd, void complete_add( const wchar_t *cmd,
int cmd_type, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt, const wchar_t *long_opt,
int long_mode, int long_mode,
@ -212,22 +212,19 @@ void complete_add( const wchar_t *cmd,
true, any options not matching one of the provided options will be true, any options not matching one of the provided options will be
flagged as an error by syntax highlighting. flagged as an error by syntax highlighting.
*/ */
void complete_set_authoritative( const wchar_t *cmd, void complete_set_authoritative( const wchar_t *cmd, bool cmd_type, bool authoritative );
int cmd_type,
int authoritative );
/** /**
Remove a previously defined completion Remove a previously defined completion
*/ */
void complete_remove( const wchar_t *cmd, void complete_remove( const wchar_t *cmd,
int cmd_type, bool cmd_is_path,
wchar_t short_opt, wchar_t short_opt,
const wchar_t *long_opt ); const wchar_t *long_opt );
/** Find all completions of the command cmd, insert them into out. */ /** Find all completions of the command cmd, insert them into out. */
void complete( const wchar_t* cmd, std::vector<completion_t> &out, complete_type_t type); void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type );
void complete2( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type );
/** /**
Print a list of all current completions into the string_buffer_t. Print a list of all current completions into the string_buffer_t.

View file

@ -1667,6 +1667,7 @@ void exec( parser_t &parser, job_t *j )
static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst ) static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst )
{ {
ASSERT_IS_MAIN_THREAD();
char *begin, *end; char *begin, *end;
char z=0; char z=0;
int prev_subshell = is_subshell; int prev_subshell = is_subshell;
@ -1767,10 +1768,12 @@ static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst )
int exec_subshell( const wcstring &cmd, std::vector<wcstring> &outputs ) int exec_subshell( const wcstring &cmd, std::vector<wcstring> &outputs )
{ {
ASSERT_IS_MAIN_THREAD();
return exec_subshell_internal(cmd, &outputs); return exec_subshell_internal(cmd, &outputs);
} }
__warn_unused int exec_subshell( const wcstring &cmd ) __warn_unused int exec_subshell( const wcstring &cmd )
{ {
ASSERT_IS_MAIN_THREAD();
return exec_subshell_internal(cmd, NULL); return exec_subshell_internal(cmd, NULL);
} }

View file

@ -133,7 +133,7 @@ void reader_pop();
- The command to be completed as a null terminated array of wchar_t - The command to be completed as a null terminated array of wchar_t
- An array_list_t in which completions will be inserted. - An array_list_t in which completions will be inserted.
*/ */
typedef void (*complete_function_t)( const wchar_t *, std::vector<completion_t> &, complete_type_t ); typedef void (*complete_function_t)( const wcstring &, std::vector<completion_t> &, complete_type_t );
void reader_set_complete_function( complete_function_t ); void reader_set_complete_function( complete_function_t );
/** /**

View file

@ -69,7 +69,7 @@ enum
*/ */
int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector<completion_t> &out ); int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector<completion_t> &out );
/** /**
Test whether the given wildcard matches the string Test whether the given wildcard matches the string. Does not perform any I/O.
\param str The string to test \param str The string to test
\param wc The wildcard to test against \param wc The wildcard to test against