mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 13:39:02 +00:00
Some cleanup of completions in preparation for more multithreading
This commit is contained in:
parent
8f637975a4
commit
b30090f946
5 changed files with 124 additions and 438 deletions
544
complete.cpp
544
complete.cpp
|
@ -156,13 +156,15 @@ typedef struct complete_entry_opt
|
|||
Struct describing a command completion
|
||||
*/
|
||||
typedef std::list<complete_entry_opt_t> option_list_t;
|
||||
struct completion_entry_t
|
||||
class completion_entry_t
|
||||
{
|
||||
/** True if command is a path */
|
||||
int cmd_type;
|
||||
|
||||
public:
|
||||
/** Command string */
|
||||
wcstring cmd;
|
||||
|
||||
/** True if command is a path */
|
||||
bool cmd_is_path;
|
||||
|
||||
/** String containing all short option characters */
|
||||
wcstring short_opt_str;
|
||||
|
||||
|
@ -170,7 +172,15 @@ struct completion_entry_t
|
|||
option_list_t options;
|
||||
|
||||
/** 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 */
|
||||
|
@ -178,22 +188,21 @@ typedef std::list<completion_entry_t *> completion_entry_list_t;
|
|||
static completion_entry_list_t completion_entries;
|
||||
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 completer_t {
|
||||
const complete_type_t type;
|
||||
const wcstring cmd;
|
||||
const wcstring initial_cmd;
|
||||
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:
|
||||
completer_t(const wcstring &c, complete_type_t t) :
|
||||
type(t),
|
||||
cmd(c),
|
||||
initial_cmd(c),
|
||||
completions()
|
||||
{
|
||||
}
|
||||
|
@ -216,8 +225,17 @@ class completer_t {
|
|||
bool use_builtin,
|
||||
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 condition_test( const wcstring &condition );
|
||||
|
||||
};
|
||||
|
||||
/* Autoloader for completions */
|
||||
|
@ -251,28 +269,13 @@ void completion_allocate(std::vector<completion_t> &completions, const wcstring
|
|||
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
|
||||
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.
|
||||
*/
|
||||
static int condition_test( const wcstring &condition, complete_type_t type )
|
||||
bool completer_t::condition_test( const wcstring &condition )
|
||||
{
|
||||
if( condition.empty() )
|
||||
{
|
||||
|
@ -280,7 +283,7 @@ static int condition_test( const wcstring &condition, complete_type_t type )
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (type == COMPLETE_AUTOSUGGEST)
|
||||
if (this->type == COMPLETE_AUTOSUGGEST)
|
||||
{
|
||||
/* Autosuggestion can't support conditions */
|
||||
return 0;
|
||||
|
@ -288,11 +291,8 @@ static int condition_test( const wcstring &condition, complete_type_t type )
|
|||
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
|
||||
printf("condition_test %ls\n", condition.c_str());
|
||||
|
||||
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()) {
|
||||
/* Compute new value and reinsert it */
|
||||
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. */
|
||||
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);
|
||||
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); 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 NULL;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
completion_entry_t *c;
|
||||
|
||||
complete_init();
|
||||
|
||||
c = complete_find_exact_entry( cmd, cmd_type );
|
||||
c = complete_find_exact_entry( cmd, cmd_is_path );
|
||||
|
||||
if( c == NULL )
|
||||
{
|
||||
c = new completion_entry_t();
|
||||
c = new completion_entry_t(cmd, cmd_is_path, L"", true);
|
||||
completion_entries.push_front(c);
|
||||
c->cmd = cmd;
|
||||
c->cmd_type = cmd_type;
|
||||
c->short_opt_str = L"";
|
||||
c->authoritative = 1;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void complete_set_authoritative( const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
int authoritative )
|
||||
void complete_set_authoritative( const wchar_t *cmd, bool cmd_is_path, bool authoritative )
|
||||
{
|
||||
completion_entry_t *c;
|
||||
|
||||
CHECK( cmd, );
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void complete_add( const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt,
|
||||
int old_mode,
|
||||
|
@ -370,7 +362,7 @@ void complete_add( const wchar_t *cmd,
|
|||
|
||||
scoped_lock lock(completion_lock);
|
||||
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());
|
||||
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,
|
||||
int cmd_type,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_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(); ) {
|
||||
completion_entry_t *e = *iter;
|
||||
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 );
|
||||
}
|
||||
|
||||
|
@ -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++)
|
||||
{
|
||||
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;
|
||||
|
||||
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
|
||||
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();
|
||||
|
||||
|
@ -793,9 +785,7 @@ static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &co
|
|||
int cmd_len;
|
||||
int skip;
|
||||
|
||||
if( !cmd )
|
||||
return;
|
||||
|
||||
const wchar_t * const cmd = str.c_str();
|
||||
cmd_start=wcsrchr(cmd, L'/');
|
||||
|
||||
if( cmd_start )
|
||||
|
@ -820,9 +810,9 @@ static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &co
|
|||
|
||||
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'/' ))
|
||||
{
|
||||
|
@ -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
|
||||
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;
|
||||
if (el.empty())
|
||||
continue;
|
||||
|
@ -930,13 +920,9 @@ static const wchar_t *complete_function_desc( const wcstring &fn )
|
|||
|
||||
\param comp the list to add all completions to
|
||||
*/
|
||||
static void complete_cmd( const wchar_t *cmd,
|
||||
std::vector<completion_t> &comp,
|
||||
int use_function,
|
||||
int use_builtin,
|
||||
int use_command,
|
||||
complete_type_t type )
|
||||
void completer_t::complete_cmd( const wcstring &str, bool use_function, bool use_builtin, bool use_command)
|
||||
{
|
||||
const wchar_t * const cmd = str.c_str();
|
||||
wchar_t *path_cpy;
|
||||
wchar_t *nxt_path;
|
||||
wchar_t *state;
|
||||
|
@ -953,9 +939,9 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
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 )
|
||||
continue;
|
||||
|
||||
prev_count = comp.size() ;
|
||||
prev_count = this->completions.size() ;
|
||||
|
||||
if( expand_string( nxt_completion,
|
||||
comp,
|
||||
this->completions,
|
||||
ACCEPT_INCOMPLETE |
|
||||
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 )
|
||||
{
|
||||
c.completion += add_slash ;
|
||||
|
@ -1009,7 +995,7 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
}
|
||||
free( path_cpy );
|
||||
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)));
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -1033,7 +1019,7 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
if( use_builtin )
|
||||
{
|
||||
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 );
|
||||
|
||||
|
@ -1059,8 +1045,8 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
continue;
|
||||
}
|
||||
|
||||
if( expand_string( nxt_completion,
|
||||
comp,
|
||||
if( expand_string( nxt_completion,
|
||||
this->completions,
|
||||
ACCEPT_INCOMPLETE | DIRECTORIES_ONLY ) != EXPAND_ERROR )
|
||||
{
|
||||
}
|
||||
|
@ -1071,10 +1057,6 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
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
|
||||
|
@ -1088,12 +1070,10 @@ void completer_t::complete_cmd( const wcstring &str, bool use_function, bool use
|
|||
\param desc Description of the completion
|
||||
\param comp_out The list into which the results will be inserted
|
||||
*/
|
||||
static void complete_from_args( const wcstring &str,
|
||||
const wcstring &args,
|
||||
const wcstring &desc,
|
||||
std::vector<completion_t> &comp_out,
|
||||
complete_type_t type,
|
||||
complete_flags_t flags )
|
||||
void completer_t::complete_from_args( const wcstring &str,
|
||||
const wcstring &args,
|
||||
const wcstring &desc,
|
||||
complete_flags_t flags )
|
||||
{
|
||||
/* 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);
|
||||
|
||||
if (type != COMPLETE_AUTOSUGGEST)
|
||||
if (this->type != COMPLETE_AUTOSUGGEST)
|
||||
proc_push_interactive(0);
|
||||
|
||||
parser.eval_args( args.c_str(), possible_comp );
|
||||
|
||||
if (type != COMPLETE_AUTOSUGGEST)
|
||||
if (this->type != COMPLETE_AUTOSUGGEST)
|
||||
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
|
||||
completion should be disabled, 1 otherwise.
|
||||
*/
|
||||
static int complete_param( const wchar_t *cmd_orig,
|
||||
const wchar_t *popt,
|
||||
const wchar_t *str,
|
||||
int use_switches,
|
||||
complete_type_t type,
|
||||
std::vector<completion_t> &comp_out)
|
||||
bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spopt, const wcstring &sstr, bool use_switches)
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
wcstring cmd, path;
|
||||
|
@ -1228,10 +1205,11 @@ static int complete_param( const wchar_t *cmd_orig,
|
|||
if (type == COMPLETE_DEFAULT)
|
||||
complete_load( cmd, true );
|
||||
|
||||
scoped_lock lock(completion_lock);
|
||||
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); 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 ) ) ) )
|
||||
{
|
||||
|
@ -1250,11 +1228,11 @@ static int complete_param( const wchar_t *cmd_orig,
|
|||
{
|
||||
const complete_entry_opt_t *o = &*oiter;
|
||||
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_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;
|
||||
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;
|
||||
use_common &= ((o->result_mode & NO_COMMON )==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) )
|
||||
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_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
|
||||
*/
|
||||
|
||||
if( !condition_test( o->condition, type ))
|
||||
if( !this->condition_test( o->condition ))
|
||||
continue;
|
||||
|
||||
|
||||
if( (o->short_opt == L'\0' ) && (o->long_opt[0]==L'\0'))
|
||||
{
|
||||
use_files &= ((o->result_mode & NO_FILES )==0);
|
||||
complete_from_args( str, o->comp, o->localized_desc(), comp_out, type, o->flags );
|
||||
complete_from_args( str, o->comp, o->localized_desc(), o->flags );
|
||||
}
|
||||
|
||||
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[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.
|
||||
*/
|
||||
wcstring completion = format_string(L"%ls=", whole_opt.c_str()+offset);
|
||||
completion_allocate( comp_out,
|
||||
completion_allocate( this->completions,
|
||||
completion,
|
||||
C_(o->desc.c_str()),
|
||||
flags );
|
||||
|
||||
}
|
||||
|
||||
completion_allocate( comp_out,
|
||||
completion_allocate( this->completions,
|
||||
whole_opt.c_str() + offset,
|
||||
C_(o->desc.c_str()),
|
||||
flags );
|
||||
|
@ -1420,16 +1398,12 @@ static int complete_param( const wchar_t *cmd_orig,
|
|||
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
|
||||
*/
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
if( expand_string( comp_str,
|
||||
comp_out,
|
||||
this->completions,
|
||||
flags ) == EXPAND_ERROR )
|
||||
{
|
||||
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
|
||||
*/
|
||||
static int complete_variable( const wchar_t *whole_var,
|
||||
int start_offset,
|
||||
std::vector<completion_t> &comp_list,
|
||||
complete_type_t type)
|
||||
bool completer_t::complete_variable(const wcstring &str, int start_offset)
|
||||
{
|
||||
const wchar_t * const whole_var = str.c_str();
|
||||
const wchar_t *var = &whole_var[start_offset];
|
||||
int varlen = wcslen( var );
|
||||
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());
|
||||
}
|
||||
|
||||
completion_allocate( comp_list,
|
||||
comp.c_str(),
|
||||
desc.c_str(),
|
||||
flags );
|
||||
completion_allocate( this->completions, comp.c_str(), desc.c_str(), flags );
|
||||
res =1;
|
||||
|
||||
}
|
||||
|
@ -1535,40 +1499,29 @@ static int complete_variable( const wchar_t *whole_var,
|
|||
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
|
||||
complete as an environment variable.
|
||||
|
||||
\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 )
|
||||
{
|
||||
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
|
||||
*/
|
||||
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;
|
||||
int res=0;
|
||||
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 )
|
||||
{
|
||||
wcstring desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
completion_allocate( comp,
|
||||
completion_allocate( this->completions,
|
||||
&pw_name[name_len],
|
||||
desc,
|
||||
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 desc = format_string(COMPLETE_USER_DESC, pw_name);
|
||||
|
||||
completion_allocate( comp,
|
||||
completion_allocate( this->completions,
|
||||
name,
|
||||
desc,
|
||||
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;
|
||||
}
|
||||
|
||||
bool completer_t::try_complete_user( const wcstring &str )
|
||||
{
|
||||
return ::try_complete_user(str.c_str(), this->completions) > 0;
|
||||
}
|
||||
|
||||
void complete2( const wcstring &cmd, std::vector<completion_t> &comps, complete_type_t type )
|
||||
void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_type_t type )
|
||||
{
|
||||
/* Make our completer */
|
||||
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 had_ddash = 0;
|
||||
|
||||
// CHECK( comp, );
|
||||
|
||||
complete_init();
|
||||
|
||||
// debug( 1, L"Complete '%ls'", cmd );
|
||||
|
||||
cursor_pos = cmd.size();
|
||||
|
@ -1888,266 +1833,6 @@ void complete2( const wcstring &cmd, std::vector<completion_t> &comps, complete_
|
|||
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 )
|
||||
{
|
||||
scoped_lock locker(completion_lock);
|
||||
for (completion_entry_list_t::const_iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
|
||||
{
|
||||
const completion_entry_t *e = *iter;
|
||||
|
@ -2188,7 +1874,7 @@ void complete_print( wcstring &out )
|
|||
modestr[o->result_mode] );
|
||||
|
||||
append_switch( out,
|
||||
e->cmd_type?L"path":L"command",
|
||||
e->cmd_is_path ? L"path" : L"command",
|
||||
e->cmd );
|
||||
|
||||
|
||||
|
|
11
complete.h
11
complete.h
|
@ -198,7 +198,7 @@ enum complete_type_t {
|
|||
\param flags A set of completion flags
|
||||
*/
|
||||
void complete_add( const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt,
|
||||
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
|
||||
flagged as an error by syntax highlighting.
|
||||
*/
|
||||
void complete_set_authoritative( const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
int authoritative );
|
||||
void complete_set_authoritative( const wchar_t *cmd, bool cmd_type, bool authoritative );
|
||||
|
||||
/**
|
||||
Remove a previously defined completion
|
||||
*/
|
||||
void complete_remove( const wchar_t *cmd,
|
||||
int cmd_type,
|
||||
bool cmd_is_path,
|
||||
wchar_t short_opt,
|
||||
const wchar_t *long_opt );
|
||||
|
||||
|
||||
/** 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 complete2( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type );
|
||||
void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type );
|
||||
|
||||
/**
|
||||
Print a list of all current completions into the string_buffer_t.
|
||||
|
|
3
exec.cpp
3
exec.cpp
|
@ -1667,6 +1667,7 @@ void exec( parser_t &parser, job_t *j )
|
|||
|
||||
static int exec_subshell_internal( const wcstring &cmd, wcstring_list_t *lst )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
char *begin, *end;
|
||||
char z=0;
|
||||
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 )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return exec_subshell_internal(cmd, &outputs);
|
||||
}
|
||||
|
||||
__warn_unused int exec_subshell( const wcstring &cmd )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return exec_subshell_internal(cmd, NULL);
|
||||
}
|
||||
|
|
2
reader.h
2
reader.h
|
@ -133,7 +133,7 @@ void reader_pop();
|
|||
- The command to be completed as a null terminated array of wchar_t
|
||||
- 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 );
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 );
|
||||
/**
|
||||
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 wc The wildcard to test against
|
||||
|
|
Loading…
Reference in a new issue