Some more changes in preparation for turning on complete-based autosuggestions

This commit is contained in:
ridiculousfish 2012-02-26 20:11:34 -08:00
parent 3553e65089
commit f74a82776f
10 changed files with 107 additions and 65 deletions

View file

@ -143,6 +143,34 @@ void autoload_t::unload_all(void) {
this->evict_all_nodes();
}
/** Check whether the given command is loaded. */
bool autoload_t::has_tried_loading( const wcstring &cmd )
{
scoped_lock locker(lock);
autoload_function_t * func = this->get_node(cmd);
return func != NULL;
}
static bool is_stale(const autoload_function_t *func) {
/** Return whether this function is stale. Internalized functions can never be stale. */
return ! func->is_internalized && time(NULL) - func->access.last_checked > kAutoloadStalenessInterval;
}
autoload_function_t *autoload_t::get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction)
{
ASSERT_IS_LOCKED(lock);
autoload_function_t *func = this->get_node(cmd);
if (! func) {
func = new autoload_function_t(cmd);
if (allow_eviction) {
this->add_node(func);
} else {
this->add_node_without_eviction(func);
}
}
return func;
}
/**
This internal helper function does all the real work. By using two
functions, the internal function can return on various places in
@ -176,7 +204,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
if (! func) {
/* Can't use a function that doesn't exist */
use_cached = false;
} else if ( ! allow_stale_functions && time(NULL) - func->access.last_checked > kAutoloadStalenessInterval) {
} else if ( ! allow_stale_functions && is_stale(func)) {
/* Can't use a stale function */
use_cached = false;
} else if (really_load && ! func->is_placeholder && ! func->is_loaded) {
@ -189,7 +217,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
/* If we can use this function, return whether we were able to access it */
if (use_cached) {
return func->access.accessible;
return func->is_internalized || func->access.accessible;
}
}
/* The source of the script will end up here */
@ -215,6 +243,16 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
if (matching_builtin_script) {
has_script_source = true;
script_source = str2wcstring(matching_builtin_script->def);
/* Make a node representing this function */
scoped_lock locker(lock);
autoload_function_t *func = this->get_autoloaded_function_with_creation(cmd, really_load);
/* This function is internalized */
func->is_internalized = true;
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */
if (really_load) func->is_loaded = true;
}
if (! has_script_source)
@ -255,12 +293,7 @@ bool autoload_t::locate_file_and_maybe_load_it( const wcstring &cmd, bool really
/* Create the function if we haven't yet. This does not load it. Do not trigger eviction unless we are actually loading, because we don't want to evict off of the main thread. */
if (! func) {
func = new autoload_function_t(cmd);
if (really_load) {
this->add_node(func);
} else {
this->add_node_without_eviction(func);
}
func = get_autoloaded_function_with_creation(cmd, really_load);
}
/* It's a fiction to say the script is loaded at this point, but we're definitely going to load it down below. */

View file

@ -26,10 +26,11 @@ file_access_attempt_t access_file(const wcstring &path, int mode);
struct autoload_function_t : public lru_node_t
{
autoload_function_t(const wcstring &key) : lru_node_t(key), is_loaded(false), is_placeholder(false) { bzero(&access, sizeof access); }
autoload_function_t(const wcstring &key) : lru_node_t(key), access(), is_loaded(false), is_placeholder(false), is_internalized(false) { }
file_access_attempt_t access; /** The last access attempt */
bool is_loaded; /** Whether we have actually loaded this function */
bool is_placeholder; /** Whether we are a placeholder that stands in for "no such function". If this is true, then is_loaded must be false. */
bool is_internalized; /** Whether this function came from a builtin "internalized" script */
};
@ -75,7 +76,8 @@ private:
virtual void node_was_evicted(autoload_function_t *node);
autoload_function_t *get_autoloaded_function_with_creation(const wcstring &cmd, bool allow_eviction);
protected:
/** Overridable callback for when a command is removed */
virtual void command_removed(const wcstring &cmd) { }
@ -100,6 +102,9 @@ private:
\param reload wheter to recheck file timestamps on already loaded files
*/
int load( const wcstring &cmd, bool reload );
/** Check whether we have tried loading the given command. Does not do any I/O. */
bool has_tried_loading( const wcstring &cmd );
/**
Tell the autoloader that the specified file, in the specified path,

View file

@ -1870,11 +1870,17 @@ void tokenize_variable_array( const wcstring &val, std::vector<wcstring> &out)
out.push_back(val.substr(pos, end - pos));
}
bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) {
bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value)
{
size_t prefix_size = proposed_prefix.size();
return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0;
}
bool list_contains_string(const wcstring_list_t &list, const wcstring &str)
{
return std::find(list.begin(), list.end(), str) != list.end();
}
int create_directory( const wcstring &d )
{
int ok = 0;

View file

@ -250,6 +250,10 @@ std::string wcs2string(const wcstring &input);
/** Test if a string prefixes another. Returns true if a is a prefix of b */
bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value);
/** Test if a list contains a string using a linear search. */
bool list_contains_string(const wcstring_list_t &list, const wcstring &str);
void assert_is_main_thread(const char *who);
#define ASSERT_IS_MAIN_THREAD_TRAMPOLINE(x) assert_is_main_thread(x)
#define ASSERT_IS_MAIN_THREAD() ASSERT_IS_MAIN_THREAD_TRAMPOLINE(__FUNCTION__)

View file

@ -227,6 +227,7 @@ class completer_t {
const complete_type_t type;
const wcstring initial_cmd;
std::vector<completion_t> completions;
wcstring_list_t commands_to_load;
/** Table of completions conditions that have already been tested and the corresponding test results */
typedef std::map<wcstring, bool> condition_cache_t;
@ -235,8 +236,7 @@ class completer_t {
public:
completer_t(const wcstring &c, complete_type_t t) :
type(t),
initial_cmd(c),
completions()
initial_cmd(c)
{
}
@ -268,6 +268,11 @@ class completer_t {
bool complete_variable(const wcstring &str, int start_offset);
bool condition_test( const wcstring &condition );
void get_commands_to_load(wcstring_list_t *lst) {
if (lst)
lst->insert(lst->end(), commands_to_load.begin(), commands_to_load.end());
}
};
@ -609,7 +614,9 @@ 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 );
if (allow_autoload) {
complete_load( cmd, false );
}
scoped_lock lock(completion_lock);
scoped_lock lock2(completion_entry_lock);
@ -1245,8 +1252,18 @@ bool completer_t::complete_param( const wcstring &scmd_orig, const wcstring &spo
wcstring cmd, path;
parse_cmd_string(cmd_orig, path, cmd);
if (type == COMPLETE_DEFAULT)
if (this->type == COMPLETE_DEFAULT)
{
complete_load( cmd, true );
}
else if (this->type == COMPLETE_AUTOSUGGEST)
{
/* Maybe indicate we should try loading this on the main thread */
if (! list_contains_string(this->commands_to_load, cmd) && ! completion_autoloader.has_tried_loading(cmd))
{
this->commands_to_load.push_back(cmd);
}
}
scoped_lock lock(completion_lock);
scoped_lock lock2(completion_entry_lock);
@ -1638,7 +1655,7 @@ bool completer_t::try_complete_user( const wcstring &str )
return res;
}
void complete( 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, wcstring_list_t *commands_to_load )
{
/* Make our completer */
completer_t completer(cmd, type);
@ -1876,6 +1893,7 @@ void complete( const wcstring &cmd, std::vector<completion_t> &comps, complete_t
free( (void *)prev_token );
comps = completer.get_completions();
completer.get_commands_to_load(commands_to_load);
}

View file

@ -223,8 +223,8 @@ void complete_remove( const wchar_t *cmd,
const wchar_t *long_opt );
/** Find all completions of the command cmd, insert them into out. */
void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type );
/** Find all completions of the command cmd, insert them into out. If to_load is not NULL, append all commands that we would autoload, but did not (presumably because this is not the main thread) */
void complete( const wcstring &cmd, std::vector<completion_t> &comp, complete_type_t type, wcstring_list_t *to_load = NULL );
/**
Print a list of all current completions into the string_buffer_t.

View file

@ -550,7 +550,7 @@ bool autosuggest_suggest_special(const wcstring &str, const wcstring &working_di
bool had_cmd = false;
wcstring suggestion;
bool suggestionOK = true;
bool suggestionOK = false;
tokenizer tok;
for( tok_init( &tok, str.c_str(), TOK_SQUASH_ERRORS );

View file

@ -1225,6 +1225,7 @@ struct autosuggestion_context_t {
file_detection_context_t detector;
const wcstring working_directory;
const env_vars vars;
wcstring_list_t commands_to_load;
autosuggestion_context_t(history_t *history, const wcstring &term) :
search_string(term),
@ -1261,7 +1262,7 @@ struct autosuggestion_context_t {
/* Try normal completions */
std::vector<completion_t> completions;
//complete2(search_string, completions, COMPLETE_AUTOSUGGEST);
//complete(search_string, completions, COMPLETE_AUTOSUGGEST, &this->commands_to_load);
if (! completions.empty()) {
this->autosuggestion = this->search_string;
this->autosuggestion.append(completions.at(0).completion);
@ -1288,6 +1289,24 @@ static bool can_autosuggest(void) {
}
static void autosuggest_completed(autosuggestion_context_t *ctx, int result) {
/* Extract the commands to load */
wcstring_list_t commands_to_load;
ctx->commands_to_load.swap(commands_to_load);
/* If we have autosuggestions to load, load them and try again */
if (! result && ! commands_to_load.empty())
{
for (wcstring_list_t::const_iterator iter = commands_to_load.begin(); iter != commands_to_load.end(); iter++)
{
printf("loading %ls\n", iter->c_str());
complete_load(*iter, false);
}
iothread_perform(threaded_autosuggest, autosuggest_completed, ctx);
return;
}
if (result &&
can_autosuggest() &&
ctx->search_string == data->command_line &&
@ -1296,7 +1315,6 @@ static void autosuggest_completed(autosuggestion_context_t *ctx, int result) {
data->autosuggestion = ctx->autosuggestion;
sanity_check();
reader_repaint();
}
delete ctx;
}
@ -2838,7 +2856,7 @@ const wchar_t *reader_readline()
buffcpy = wcsndup( begin, len );
// comp = al_halloc( 0 );
data->complete_func( buffcpy, comp, COMPLETE_DEFAULT );
data->complete_func( buffcpy, comp, COMPLETE_DEFAULT, NULL);
sort_completion_list( comp );
remove_duplicates( comp );

View file

@ -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 wcstring &, std::vector<completion_t> &, complete_type_t );
typedef void (*complete_function_t)( const wcstring &, std::vector<completion_t> &, complete_type_t, wcstring_list_t * lst );
void reader_set_complete_function( complete_function_t );
/**

View file

@ -56,24 +56,6 @@ typedef std::string cstring;
*/
#define BUFF_COUNT 4
/**
For wgettext: The ring of string_buffer_t
*/
static string_buffer_t buff[BUFF_COUNT];
/**
For wgettext: Current position in the ring
*/
static int curr_buff=0;
/**
For wgettext: Buffer used by translate_wcs2str
*/
static char *wcs2str_buff=0;
/**
For wgettext: Size of buffer used by translate_wcs2str
*/
static size_t wcs2str_buff_count=0;
/* Lock to protect wgettext */
static pthread_mutex_t wgettext_lock;
@ -315,10 +297,6 @@ wcstring wbasename( const wcstring &path )
/* Really init wgettext */
static void wgettext_really_init() {
for( size_t i=0; i<BUFF_COUNT; i++ )
{
sb_init( &buff[i] );
}
pthread_mutex_init(&wgettext_lock, NULL);
bindtextdomain( PACKAGE_NAME, LOCALEDIR );
textdomain( PACKAGE_NAME );
@ -333,26 +311,6 @@ static void wgettext_init_if_necessary()
pthread_once(&once, wgettext_really_init);
}
/**
For wgettext: Wide to narrow character conversion. Internal implementation that
avoids exessive calls to malloc
*/
static char *wgettext_wcs2str( const wchar_t *in )
{
size_t len = MAX_UTF8_BYTES*wcslen(in)+1;
if( len > wcs2str_buff_count )
{
wcs2str_buff = (char *)realloc( wcs2str_buff, len );
if( !wcs2str_buff )
{
DIE_MEM();
}
}
return wcs2str_internal( in, wcs2str_buff);
}
const wchar_t *wgettext( const wchar_t *in )
{
if( !in )