From f74a82776f9a20f09149712d50cee557d8302ec0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 26 Feb 2012 20:11:34 -0800 Subject: [PATCH] Some more changes in preparation for turning on complete-based autosuggestions --- autoload.cpp | 49 +++++++++++++++++++++++++++++++++++++++++-------- autoload.h | 9 +++++++-- common.cpp | 8 +++++++- common.h | 4 ++++ complete.cpp | 28 +++++++++++++++++++++++----- complete.h | 4 ++-- highlight.cpp | 2 +- reader.cpp | 24 +++++++++++++++++++++--- reader.h | 2 +- wutil.cpp | 42 ------------------------------------------ 10 files changed, 107 insertions(+), 65 deletions(-) diff --git a/autoload.cpp b/autoload.cpp index e214acb0e..57de29cc4 100644 --- a/autoload.cpp +++ b/autoload.cpp @@ -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. */ diff --git a/autoload.h b/autoload.h index 2d060488d..b94876a71 100644 --- a/autoload.h +++ b/autoload.h @@ -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, diff --git a/common.cpp b/common.cpp index 96d846ca8..217a63c0c 100644 --- a/common.cpp +++ b/common.cpp @@ -1870,11 +1870,17 @@ void tokenize_variable_array( const wcstring &val, std::vector &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; diff --git a/common.h b/common.h index 8fb950fe9..51c2e1eab 100644 --- a/common.h +++ b/common.h @@ -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__) diff --git a/complete.cpp b/complete.cpp index 21f45ef11..0314a4ab1 100644 --- a/complete.cpp +++ b/complete.cpp @@ -227,6 +227,7 @@ class completer_t { const complete_type_t type; const wcstring initial_cmd; std::vector completions; + wcstring_list_t commands_to_load; /** Table of completions conditions that have already been tested and the corresponding test results */ typedef std::map 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 &comps, complete_type_t type ) +void complete( const wcstring &cmd, std::vector &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 &comps, complete_t free( (void *)prev_token ); comps = completer.get_completions(); + completer.get_commands_to_load(commands_to_load); } diff --git a/complete.h b/complete.h index 6412a5bad..0a78b77de 100644 --- a/complete.h +++ b/complete.h @@ -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 &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 &comp, complete_type_t type, wcstring_list_t *to_load = NULL ); /** Print a list of all current completions into the string_buffer_t. diff --git a/highlight.cpp b/highlight.cpp index e9ab057e9..e16619fa1 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -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 ); diff --git a/reader.cpp b/reader.cpp index 0f95672e1..657e6bc3b 100644 --- a/reader.cpp +++ b/reader.cpp @@ -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 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 ); diff --git a/reader.h b/reader.h index 581f177b7..af66e6310 100644 --- a/reader.h +++ b/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 wcstring &, std::vector &, complete_type_t ); +typedef void (*complete_function_t)( const wcstring &, std::vector &, complete_type_t, wcstring_list_t * lst ); void reader_set_complete_function( complete_function_t ); /** diff --git a/wutil.cpp b/wutil.cpp index f3642a531..8cbf7d432 100644 --- a/wutil.cpp +++ b/wutil.cpp @@ -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 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 )