mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Some more work on threaded completions
This commit is contained in:
parent
a515db4aea
commit
5ea78f55f2
2 changed files with 322 additions and 12 deletions
333
complete.cpp
333
complete.cpp
|
@ -184,6 +184,40 @@ static pthread_mutex_t completion_lock = PTHREAD_MUTEX_INITIALIZER;
|
|||
*/
|
||||
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;
|
||||
std::vector<completion_t> completions;
|
||||
|
||||
public:
|
||||
completer_t(const wcstring &c, complete_type_t t) :
|
||||
type(t),
|
||||
cmd(c),
|
||||
completions()
|
||||
{
|
||||
}
|
||||
|
||||
bool try_complete_variable( const wcstring &str );
|
||||
bool try_complete_user( const wcstring &str );
|
||||
|
||||
bool complete_param( const wcstring &cmd_orig,
|
||||
const wcstring &popt,
|
||||
const wcstring &str,
|
||||
bool use_switches);
|
||||
|
||||
void complete_param_expand( const wcstring &str, bool do_file);
|
||||
|
||||
void complete_cmd( const wcstring &str,
|
||||
bool use_function,
|
||||
bool use_builtin,
|
||||
bool use_command);
|
||||
|
||||
bool empty() const { return completions.empty(); }
|
||||
const std::vector<completion_t> &get_completions(void) { return completions; }
|
||||
|
||||
};
|
||||
|
||||
/* Autoloader for completions */
|
||||
class completion_autoload_t : public autoload_t {
|
||||
public:
|
||||
|
@ -236,15 +270,24 @@ static void condition_cache_clear()
|
|||
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 )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
static int condition_test( const wcstring &condition, complete_type_t type )
|
||||
{
|
||||
if( condition.empty() )
|
||||
{
|
||||
// fwprintf( stderr, L"No condition specified\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (type == COMPLETE_AUTOSUGGEST)
|
||||
{
|
||||
/* Autosuggestion can't support conditions */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1026,6 +1069,11 @@ 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
|
||||
any return matching completions. Matching is done using \c
|
||||
|
@ -1197,7 +1245,7 @@ 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 ))
|
||||
if( (arg=param_match2( o, str ))!=0 && condition_test( o->condition, type ))
|
||||
{
|
||||
use_common &= ((o->result_mode & NO_COMMON )==0);
|
||||
use_files &= ((o->result_mode & NO_FILES )==0);
|
||||
|
@ -1220,7 +1268,7 @@ 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 ))
|
||||
if( param_match_old( o, popt ) && condition_test( o->condition, type ))
|
||||
{
|
||||
old_style_match = 1;
|
||||
use_common &= ((o->result_mode & NO_COMMON )==0);
|
||||
|
@ -1248,7 +1296,7 @@ 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 ))
|
||||
if( param_match( o, popt ) && condition_test( o->condition, type ))
|
||||
{
|
||||
use_common &= ((o->result_mode & NO_COMMON )==0);
|
||||
use_files &= ((o->result_mode & NO_FILES )==0);
|
||||
|
@ -1271,7 +1319,7 @@ static int complete_param( const wchar_t *cmd_orig,
|
|||
check if any of the arguments match
|
||||
*/
|
||||
|
||||
if( !condition_test( o->condition ))
|
||||
if( !condition_test( o->condition, type ))
|
||||
continue;
|
||||
|
||||
|
||||
|
@ -1367,6 +1415,11 @@ 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
|
||||
*/
|
||||
|
@ -1401,6 +1454,10 @@ 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
|
||||
|
@ -1473,8 +1530,7 @@ static int complete_variable( const wchar_t *whole_var,
|
|||
|
||||
\return 0 if unable to complete, 1 otherwise
|
||||
*/
|
||||
static int try_complete_variable( const wchar_t *cmd,
|
||||
std::vector<completion_t> &comp )
|
||||
static int try_complete_variable( const wchar_t *cmd, std::vector<completion_t> &comp )
|
||||
{
|
||||
int len = wcslen( cmd );
|
||||
int i;
|
||||
|
@ -1494,14 +1550,18 @@ static int try_complete_variable( const wchar_t *cmd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool completer_t::try_complete_variable( const wcstring &str )
|
||||
{
|
||||
return ::try_complete_variable(str.c_str(), this->completions) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Try to complete the specified string as a username. This is used by
|
||||
~USER type expansion.
|
||||
|
||||
\return 0 if unable to complete, 1 otherwise
|
||||
*/
|
||||
static int try_complete_user( const wchar_t *cmd,
|
||||
std::vector<completion_t> &comp )
|
||||
static int try_complete_user( const wchar_t *cmd, std::vector<completion_t> &comp )
|
||||
{
|
||||
const wchar_t *first_char=cmd;
|
||||
int res=0;
|
||||
|
@ -1563,6 +1623,255 @@ static int try_complete_user( const wchar_t *cmd,
|
|||
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 )
|
||||
{
|
||||
/* Make our completer */
|
||||
completer_t completer(cmd, 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, *prev_token=0;
|
||||
wcstring current_command;
|
||||
int on_command=0;
|
||||
int pos;
|
||||
bool done=false;
|
||||
int cursor_pos;
|
||||
int use_command = 1;
|
||||
int use_function = 1;
|
||||
int use_builtin = 1;
|
||||
int had_ddash = 0;
|
||||
|
||||
// CHECK( comp, );
|
||||
|
||||
complete_init();
|
||||
|
||||
// debug( 1, L"Complete '%ls'", cmd );
|
||||
|
||||
cursor_pos = cmd.size();
|
||||
|
||||
const wchar_t *cmd_cstr = cmd.c_str();
|
||||
parse_util_cmdsubst_extent( cmd_cstr, cursor_pos, &cmdsubst_begin, &cmdsubst_end );
|
||||
parse_util_token_extent( cmd_cstr, cursor_pos, &tok_begin, &tok_end, &prev_begin, &prev_end );
|
||||
|
||||
if( !cmdsubst_begin )
|
||||
done=1;
|
||||
|
||||
|
||||
/**
|
||||
If we are completing a variable name or a tilde expansion user
|
||||
name, we do that and return. No need for any other competions.
|
||||
*/
|
||||
|
||||
if( !done )
|
||||
{
|
||||
wcstring tmp = tok_begin;
|
||||
done = completer.try_complete_variable( tmp ) || completer.try_complete_user( tmp );
|
||||
}
|
||||
|
||||
if( !done )
|
||||
{
|
||||
pos = cursor_pos-(cmdsubst_begin-cmd_cstr);
|
||||
|
||||
buff = wcstring( cmdsubst_begin, cmdsubst_end-cmdsubst_begin );
|
||||
|
||||
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;
|
||||
|
||||
current_command = ncmd;
|
||||
|
||||
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_cstr) );
|
||||
|
||||
prev_token = prev_begin ? wcsndup( prev_begin, prev_end - prev_begin ): wcsdup(L"");
|
||||
|
||||
// debug( 0, L"on_command: %d, %ls %ls\n", on_command, current_command, current_token );
|
||||
|
||||
/*
|
||||
Check if we are using the 'command' or 'builtin' builtins
|
||||
_and_ we are writing a switch instead of a command. In that
|
||||
case, complete using the builtins completions, not using a
|
||||
subcommand.
|
||||
*/
|
||||
|
||||
if( (on_command || (wcscmp( current_token, L"--" ) == 0 ) ) &&
|
||||
(current_token[0] == L'-') &&
|
||||
!(use_command && use_function && use_builtin ) )
|
||||
{
|
||||
if( use_command == 0 )
|
||||
current_command = L"builtin";
|
||||
else
|
||||
current_command = L"command";
|
||||
|
||||
had_cmd = 1;
|
||||
on_command = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Use command completions if in between commands
|
||||
*/
|
||||
if( !had_cmd )
|
||||
{
|
||||
on_command=1;
|
||||
}
|
||||
|
||||
/*
|
||||
We don't want these to be null
|
||||
*/
|
||||
|
||||
if( !current_token )
|
||||
{
|
||||
current_token = wcsdup(L"");
|
||||
}
|
||||
|
||||
if( !prev_token )
|
||||
{
|
||||
prev_token = wcsdup(L"");
|
||||
}
|
||||
|
||||
if( current_token && prev_token )
|
||||
{
|
||||
if( on_command )
|
||||
{
|
||||
/* Complete command filename */
|
||||
completer.complete_cmd( current_token, use_function, use_builtin, use_command );
|
||||
}
|
||||
else
|
||||
{
|
||||
int do_file=0;
|
||||
|
||||
wcstring current_command_unescape = current_command;
|
||||
wcstring prev_token_unescape = prev_token;
|
||||
wcstring current_token_unescape = current_token;
|
||||
|
||||
if( unescape_string( current_command_unescape, 0 ) &&
|
||||
unescape_string( prev_token_unescape, 0 ) &&
|
||||
unescape_string( current_token_unescape, UNESCAPE_INCOMPLETE))
|
||||
{
|
||||
do_file = completer.complete_param( current_command_unescape,
|
||||
prev_token_unescape,
|
||||
current_token_unescape,
|
||||
!had_ddash );
|
||||
}
|
||||
|
||||
/*
|
||||
If we have found no command specific completions at
|
||||
all, fall back to using file completions.
|
||||
*/
|
||||
if( completer.empty() )
|
||||
do_file = 1;
|
||||
|
||||
/*
|
||||
This function wants the unescaped string
|
||||
*/
|
||||
completer.complete_param_expand( current_token, do_file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free( (void *)current_token );
|
||||
free( (void *)prev_token );
|
||||
|
||||
comps = completer.get_completions();
|
||||
}
|
||||
|
||||
void complete( const wchar_t *cmd, std::vector<completion_t> &comp, complete_type_t type )
|
||||
{
|
||||
|
||||
|
|
|
@ -225,6 +225,7 @@ void complete_remove( const wchar_t *cmd,
|
|||
|
||||
/** 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 );
|
||||
|
||||
/**
|
||||
Print a list of all current completions into the string_buffer_t.
|
||||
|
|
Loading…
Reference in a new issue