Some work to allow completions to be evaluated off of the main thread

This commit is contained in:
ridiculousfish 2012-02-24 12:13:35 -08:00
parent 90e979d0d9
commit a515db4aea
12 changed files with 144 additions and 129 deletions

View file

@ -547,7 +547,7 @@ static int builtin_complete( parser_t &parser, wchar_t **argv )
{
recursion_level++;
complete( do_complete, comp );
complete( do_complete, comp, COMPLETE_DEFAULT );
for( size_t i=0; i< comp.size() ; i++ )
{

View file

@ -2070,5 +2070,15 @@ void assert_is_background_thread(const char *who)
if (is_main_thread()) {
fprintf(stderr, "Warning: %s called on the main thread (may block!). Break on debug_thread_error to debug.\n", who);
debug_thread_error();
}
}
}
void assert_is_locked(void *vmutex, const char *who)
{
pthread_mutex_t *mutex = static_cast<pthread_mutex_t*>(vmutex);
if (0 == pthread_mutex_trylock(mutex)) {
fprintf(stderr, "Warning: %s is not locked when it should be. Break on debug_thread_error to debug.\n", who);
debug_thread_error();
pthread_mutex_unlock(mutex);
}
}

View file

@ -258,6 +258,9 @@ void assert_is_background_thread(const char *who);
#define ASSERT_IS_BACKGROUND_THREAD_TRAMPOLINE(x) assert_is_background_thread(x)
#define ASSERT_IS_BACKGROUND_THREAD() ASSERT_IS_BACKGROUND_THREAD_TRAMPOLINE(__FUNCTION__)
/* Useful macro for asserting that a lock is locked. This doesn't check whether this thread locked it, which it would be nice if it did, but here it is anyways. */
void assert_is_locked(void *mutex, const char *who);
#define ASSERT_IS_LOCKED(x) assert_is_locked((void *)(&x), #x)
/**
Converts the wide character string \c in into it's narrow

View file

@ -20,7 +20,7 @@
#include <pwd.h>
#include <signal.h>
#include <wchar.h>
#include <pthread.h>
#include "fallback.h"
#include "util.h"
@ -176,6 +176,7 @@ struct completion_entry_t
/** Linked list of all completion entries */
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
@ -259,12 +260,10 @@ static int condition_test( const wcstring &condition )
}
/**
Search for an exactly matching completion entry
*/
static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd,
const int cmd_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 )
{
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;
@ -274,16 +273,14 @@ static completion_entry_t *complete_find_exact_entry( const wchar_t *cmd,
return NULL;
}
/**
Locate the specified entry. Create it if it doesn't exist.
*/
static completion_entry_t *complete_get_exact_entry( const wchar_t *cmd,
int cmd_type )
/** 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 )
{
ASSERT_IS_LOCKED(completion_lock);
completion_entry_t *c;
complete_init();
c = complete_find_exact_entry( cmd, cmd_type );
if( c == NULL )
@ -307,6 +304,7 @@ void complete_set_authoritative( const wchar_t *cmd,
completion_entry_t *c;
CHECK( cmd, );
scoped_lock lock(completion_lock);
c = complete_get_exact_entry( cmd, cmd_type );
c->authoritative = authoritative;
}
@ -324,7 +322,8 @@ void complete_add( const wchar_t *cmd,
int flags )
{
CHECK( cmd, );
scoped_lock lock(completion_lock);
completion_entry_t *c;
c = complete_get_exact_entry( cmd, cmd_type );
@ -356,11 +355,11 @@ void complete_add( const wchar_t *cmd,
/**
Remove all completion options in the specified entry that match the
specified short / long option strings. Returns true if it is now
empty and should be deleted, false if it's not empty.
empty and should be deleted, false if it's not empty. Must be called while locked.
*/
static bool complete_remove_entry( completion_entry_t *e, wchar_t short_opt, const wchar_t *long_opt )
{
ASSERT_IS_LOCKED(completion_lock);
if(( short_opt == 0 ) && (long_opt == 0 ) )
{
e->options.clear();
@ -410,6 +409,7 @@ void complete_remove( const wchar_t *cmd,
const wchar_t *long_opt )
{
CHECK( cmd, );
scoped_lock lock(completion_lock);
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); ) {
completion_entry_t *e = *iter;
bool delete_it = false;
@ -529,7 +529,8 @@ int complete_is_valid_option( const wchar_t *str,
Make sure completions are loaded for the specified command
*/
if (allow_autoload) complete_load( cmd, false );
scoped_lock lock(completion_lock);
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{
const completion_entry_t *i = *iter;
@ -741,6 +742,8 @@ static void complete_strings( std::vector<completion_t> &comp_out,
*/
static void complete_cmd_desc( const wchar_t *cmd, std::vector<completion_t> &comp )
{
ASSERT_IS_MAIN_THREAD();
const wchar_t *cmd_start;
int cmd_len;
int skip;
@ -886,7 +889,8 @@ static void complete_cmd( const wchar_t *cmd,
std::vector<completion_t> &comp,
int use_function,
int use_builtin,
int use_command )
int use_command,
complete_type_t type )
{
wchar_t *path_cpy;
wchar_t *nxt_path;
@ -896,10 +900,12 @@ static void complete_cmd( const wchar_t *cmd,
wchar_t *cdpath_cpy = wcsdup(L".");
const bool wants_description = (type == COMPLETE_DEFAULT);
if( (wcschr( cmd, L'/') != 0) || (cmd[0] == L'~' ) )
{
if( use_command )
if( use_command && wants_description )
{
if( expand_string(cmd, comp, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) != EXPAND_ERROR )
@ -941,8 +947,7 @@ static void complete_cmd( const wchar_t *cmd,
prev_count = comp.size() ;
if( expand_string(
nxt_completion,
if( expand_string( nxt_completion,
comp,
ACCEPT_INCOMPLETE |
EXECUTABLES_ONLY ) != EXPAND_ERROR )
@ -958,7 +963,8 @@ static void complete_cmd( const wchar_t *cmd,
}
}
free( path_cpy );
complete_cmd_desc( cmd, comp );
if (wants_description)
complete_cmd_desc( cmd, comp );
}
}
@ -1157,7 +1163,8 @@ static int complete_param( const wchar_t *cmd_orig,
const wchar_t *popt,
const wchar_t *str,
int use_switches,
std::vector<completion_t> &comp_out )
complete_type_t type,
std::vector<completion_t> &comp_out)
{
int use_common=1, use_files=1;
@ -1165,7 +1172,8 @@ static int complete_param( const wchar_t *cmd_orig,
wcstring cmd, path;
parse_cmd_string(cmd_orig, path, cmd);
complete_load( cmd, true );
if (type == COMPLETE_DEFAULT)
complete_load( cmd, true );
for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++)
{
@ -1362,9 +1370,7 @@ static int complete_param( const wchar_t *cmd_orig,
/**
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 )
static void complete_param_expand( const wchar_t *str, std::vector<completion_t> &comp_out, int do_file, complete_type_t type )
{
const wchar_t *comp_str;
int flags;
@ -1377,10 +1383,14 @@ static void complete_param_expand( const wchar_t *str,
{
comp_str = str;
}
flags = EXPAND_SKIP_CMDSUBST |
ACCEPT_INCOMPLETE |
(do_file?0:EXPAND_SKIP_WILDCARDS);
flags = EXPAND_SKIP_CMDSUBST | ACCEPT_INCOMPLETE;
if (! do_file)
flags |= EXPAND_SKIP_WILDCARDS;
if (type == COMPLETE_AUTOSUGGEST)
flags |= EXPAND_NO_DESCRIPTIONS;
if( expand_string( comp_str,
comp_out,
@ -1553,8 +1563,7 @@ static int try_complete_user( const wchar_t *cmd,
return res;
}
void complete( const wchar_t *cmd,
std::vector<completion_t> &comp )
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;
@ -1610,7 +1619,7 @@ void complete( const wchar_t *cmd,
int had_cmd=0;
int end_loop=0;
tok_init( &tok, buff.c_str(), TOK_ACCEPT_UNFINISHED );
tok_init( &tok, buff.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS );
while( tok_has_next( &tok) && !end_loop )
{
@ -1765,8 +1774,7 @@ void complete( const wchar_t *cmd,
if( on_command )
{
/* Complete command filename */
complete_cmd( current_token,
comp, use_function, use_builtin, use_command );
complete_cmd( current_token, comp, use_function, use_builtin, use_command, type );
}
else
{
@ -1781,7 +1789,8 @@ void complete( const wchar_t *cmd,
do_file = complete_param( current_command_unescape,
prev_token_unescape,
current_token_unescape,
!had_ddash,
!had_ddash,
type,
comp );
}
@ -1799,7 +1808,7 @@ void complete( const wchar_t *cmd,
/*
This function wants the unescaped string
*/
complete_param_expand( current_token, comp, do_file );
complete_param_expand( current_token, comp, do_file, type );
}
}
}

View file

@ -148,6 +148,10 @@ public:
bool operator != (const completion_t& rhs) const { return ! (*this == rhs); }
};
enum complete_type_t {
COMPLETE_DEFAULT,
COMPLETE_AUTOSUGGEST
};
/**
@ -218,10 +222,9 @@ void complete_remove( const wchar_t *cmd,
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);
/** 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);
/**
Print a list of all current completions into the string_buffer_t.

View file

@ -1429,7 +1429,7 @@ static void remove_internal_separator2( wcstring &s, int conv )
}
int expand_string( const wcstring &input, std::vector<completion_t> &output, int flags )
int expand_string( const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags )
{
parser_t parser(PARSER_TYPE_ERRORS_ONLY);
std::vector<completion_t> list1, list2;
@ -1639,7 +1639,7 @@ int expand_string( const wcstring &input, std::vector<completion_t> &output, int
return res;
}
bool expand_one(wcstring &string, int flags) {
bool expand_one(wcstring &string, expand_flags_t flags) {
std::vector<completion_t> completions;
bool result = false;

View file

@ -21,40 +21,34 @@
#include "common.h"
#include <list>
/**
Flag specifying that cmdsubst expansion should be skipped
*/
#define EXPAND_SKIP_CMDSUBST 1
enum {
/** Flag specifying that cmdsubst expansion should be skipped */
EXPAND_SKIP_CMDSUBST = 1 << 0,
/** Flag specifying that variable expansion should be skipped */
EXPAND_SKIP_VARIABLES = 1 << 1,
/** Flag specifying that wildcard expansion should be skipped */
EXPAND_SKIP_WILDCARDS = 1 << 2,
/**
Flag specifying that variable expansion should be skipped
*/
#define EXPAND_SKIP_VARIABLES 2
/**
Incomplete matches in the last segment are ok (for tab
completion). An incomplete match is a wildcard that matches a
prefix of the filename. If accept_incomplete is true, only the
remainder of the string is returned.
*/
ACCEPT_INCOMPLETE = 1 << 3,
/**
Flag specifying that wildcard expansion should be skipped
*/
#define EXPAND_SKIP_WILDCARDS 4
/**
Incomplete matches in the last segment are ok (for tab
completion). An incomplete match is a wildcard that matches a
prefix of the filename. If accept_incomplete is true, only the
remainder of the string is returned.
*/
#define ACCEPT_INCOMPLETE 8
/**
Only match files that are executable by the current user. Only applicable together with ACCEPT_INCOMPLETE.
*/
#define EXECUTABLES_ONLY 16
/**
Only match directories. Only applicable together with ACCEPT_INCOMPLETE.
*/
#define DIRECTORIES_ONLY 32
/** Only match files that are executable by the current user. Only applicable together with ACCEPT_INCOMPLETE. */
EXECUTABLES_ONLY = 1 << 4,
/** Only match directories. Only applicable together with ACCEPT_INCOMPLETE. */
DIRECTORIES_ONLY = 1 << 5,
/** Don't generate descriptions */
EXPAND_NO_DESCRIPTIONS = 1 << 6
};
typedef int expand_flags_t;
/**
Use unencoded private-use keycodes for internal characters
@ -143,7 +137,7 @@ class parser_t;
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
\return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on strings containing wildcards to tell if the wildcard produced any matches.
*/
__warn_unused int expand_string( const wcstring &input, std::vector<completion_t> &output, int flag );
__warn_unused int expand_string( const wcstring &input, std::vector<completion_t> &output, expand_flags_t flags );
/**
@ -155,7 +149,7 @@ __warn_unused int expand_string( const wcstring &input, std::vector<completion_t
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
\return Whether expansion succeded
*/
bool expand_one( wcstring &inout_str, int flag );
bool expand_one( wcstring &inout_str, expand_flags_t flags );
/**
Convert the variable value to a human readable form, i.e. escape things, handle arrays, etc. Suitable for pretty-printing. The result must be free'd!

View file

@ -258,8 +258,7 @@ class reader_data_t
/**
Function for tab completion
*/
void (*complete_func)( const wchar_t *,
std::vector<completion_t>& );
complete_function_t complete_func;
/**
Function for syntax highlighting
@ -1235,6 +1234,10 @@ struct autosuggestion_context_t {
int threaded_autosuggest(void) {
ASSERT_IS_BACKGROUND_THREAD();
std::vector<completion_t> completions;
complete(search_string.c_str(), completions, COMPLETE_AUTOSUGGEST);
while (searcher.go_backwards()) {
history_item_t item = searcher.current_item();
bool item_ok = false;
@ -2299,8 +2302,7 @@ void reader_set_prompt( const wchar_t *new_prompt )
data->prompt = new_prompt;
}
void reader_set_complete_function( void (*f)( const wchar_t *,
std::vector<completion_t>& ) )
void reader_set_complete_function( complete_function_t f )
{
data->complete_func = f;
}
@ -2828,7 +2830,7 @@ const wchar_t *reader_readline()
buffcpy = wcsndup( begin, len );
// comp = al_halloc( 0 );
data->complete_func( buffcpy, comp );
data->complete_func( buffcpy, comp, COMPLETE_DEFAULT );
sort_completion_list( comp );
remove_duplicates( comp );

View file

@ -15,6 +15,7 @@
#include "util.h"
#include "io.h"
#include "common.h"
#include "complete.h"
class parser_t;
class completion_t;
@ -132,7 +133,8 @@ 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.
*/
void reader_set_complete_function( void (*f)( const wchar_t *, std::vector<completion_t> & ) );
typedef void (*complete_function_t)( const wchar_t *, std::vector<completion_t> &, complete_type_t );
void reader_set_complete_function( complete_function_t );
/**
The type of a highlight function.

View file

@ -573,7 +573,7 @@ static void wildcard_completion_allocate( std::vector<completion_t> &list,
const wcstring &fullname,
const wchar_t *completion,
const wchar_t *wc,
int is_cmd )
expand_flags_t expand_flags)
{
struct stat buf, lbuf;
wcstring sb;
@ -625,8 +625,10 @@ static void wildcard_completion_allocate( std::vector<completion_t> &list,
}
}
wcstring desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno );
wcstring desc;
if (! (expand_flags & EXPAND_NO_DESCRIPTIONS))
desc = file_get_desc( fullname.c_str(), lstat_res, lbuf, stat_res, buf, stat_errno );
if( sz >= 0 && S_ISDIR(buf.st_mode) )
{
free_completion = 1;
@ -688,7 +690,7 @@ static int test_flags( const wchar_t *filename,
*/
static int wildcard_expand_internal( const wchar_t *wc,
const wchar_t *base_dir,
int flags,
expand_flags_t flags,
std::vector<completion_t> &out )
{
@ -797,7 +799,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
long_name,
next.c_str(),
L"",
flags & EXECUTABLES_ONLY );
flags);
}
}
}
@ -842,7 +844,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
long_name,
name,
wc,
flags & EXECUTABLES_ONLY );
flags);
}
}
@ -1058,7 +1060,7 @@ static int wildcard_expand_internal( const wchar_t *wc,
int wildcard_expand( const wchar_t *wc,
const wchar_t *base_dir,
int flags,
expand_flags_t flags,
std::vector<completion_t> &out )
{
size_t c = out.size();
@ -1066,17 +1068,12 @@ int wildcard_expand( const wchar_t *wc,
if( flags & ACCEPT_INCOMPLETE )
{
const wchar_t *wc_base=L"";
wcstring wc_base;
const wchar_t *wc_base_ptr = wcsrchr( wc, L'/' );
string_buffer_t sb;
if( wc_base_ptr )
{
wc_base = wcsndup( wc, (wc_base_ptr-wc)+1 );
wc_base = wcstring(wc, (wc_base_ptr-wc)+1);
}
sb_init( &sb );
for( size_t i=c; i<out.size(); i++ )
{
@ -1084,25 +1081,14 @@ int wildcard_expand( const wchar_t *wc,
if( c.flags & COMPLETE_NO_CASE )
{
sb_clear( &sb );
sb_printf( &sb, L"%ls%ls%ls", base_dir, wc_base, c.completion.c_str() );
c.completion = (wchar_t *)sb.buff;
c.completion = format_string(L"%ls%ls%ls", base_dir, wc_base.c_str(), c.completion.c_str());
}
}
sb_destroy( &sb );
if( wc_base_ptr )
{
free( (void *)wc_base );
}
}
}
return res;
}
int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, int flags, std::vector<completion_t> &outputs )
int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, expand_flags_t flags, std::vector<completion_t> &outputs )
{
std::vector<completion_t> lst;

View file

@ -17,6 +17,7 @@
#include "util.h"
#include "common.h"
#include "expand.h"
/*
Use unencoded private-use keycodes for internal characters
@ -66,7 +67,7 @@ enum
\return 1 if matches where found, 0 otherwise. Return -1 on abort (I.e. ^C was pressed).
*/
int wildcard_expand_string(const wcstring &wc, const wcstring &base_dir, int 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
@ -90,6 +91,6 @@ int wildcard_complete( const wchar_t *str,
const wchar_t *desc,
const wchar_t *(*desc_func)(const wcstring &),
std::vector<completion_t> &out,
int flags );
expand_flags_t flags );
#endif

View file

@ -20,6 +20,7 @@
#include <libgen.h>
#include <pthread.h>
#include <string>
#include <map>
#if HAVE_LIBINTL_H
@ -73,6 +74,12 @@ static char *wcs2str_buff=0;
*/
static size_t wcs2str_buff_count=0;
/* Lock to protect wgettext */
static pthread_mutex_t wgettext_lock;
/* Maps string keys to (immortal) pointers to string values */
typedef std::map<wcstring, wcstring *> wgettext_map_t;
static std::map<wcstring, wcstring *> wgettext_map;
void wutil_init()
{
@ -312,6 +319,7 @@ static void wgettext_really_init() {
{
sb_init( &buff[i] );
}
pthread_mutex_init(&wgettext_lock, NULL);
bindtextdomain( PACKAGE_NAME, LOCALEDIR );
textdomain( PACKAGE_NAME );
}
@ -347,24 +355,21 @@ static char *wgettext_wcs2str( const wchar_t *in )
const wchar_t *wgettext( const wchar_t *in )
{
ASSERT_IS_MAIN_THREAD();
if( !in )
return in;
wgettext_init_if_necessary();
char *mbs_in = wgettext_wcs2str( in );
char *out = gettext( mbs_in );
wchar_t *wres=0;
sb_clear( &buff[curr_buff] );
sb_printf( &buff[curr_buff], L"%s", out );
wres = (wchar_t *)buff[curr_buff].buff;
curr_buff = (curr_buff+1)%BUFF_COUNT;
return wres;
wcstring key = in;
scoped_lock lock(wgettext_lock);
wcstring *& val = wgettext_map[key];
if (val == NULL) {
cstring mbs_in = wcs2string(key);
char *out = gettext(mbs_in.c_str());
val = new wcstring(format_string(L"%s", out));
}
return val->c_str();
}
wcstring wgettext2(const wcstring &in) {