diff --git a/common.cpp b/common.cpp index 47118cf03..33bbc8ac2 100644 --- a/common.cpp +++ b/common.cpp @@ -1506,7 +1506,7 @@ bool unescape_string(wcstring &str, int escape_special) { bool success = false; wchar_t *result = unescape(str.c_str(), escape_special); - if ( result) { + if (result) { str.replace(str.begin(), str.end(), result); free(result); success = true; diff --git a/common.h b/common.h index 970f8acd7..cdefd5543 100644 --- a/common.h +++ b/common.h @@ -547,10 +547,7 @@ void common_handle_winch( int signal ); void write_screen( const wchar_t *msg, string_buffer_t *buff ); /** - Tokenize the specified string into the specified array_list_t. - Each new element is allocated using malloc and must be freed by the - caller. - + Tokenize the specified string into the specified wcstring_list_t. \param val the input string. The contents of this string is not changed. \param out the list in which to place the elements. */ diff --git a/complete.cpp b/complete.cpp index 54a0f2cd8..7c833ec2f 100644 --- a/complete.cpp +++ b/complete.cpp @@ -1868,9 +1868,9 @@ void complete_print( string_buffer_t *out ) { CHECK( out, ); - for (completion_entry_list_t::iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) + for (completion_entry_list_t::const_iterator iter = completion_entries.begin(); iter != completion_entries.end(); iter++) { - completion_entry_t *e = *iter; + const completion_entry_t *e = *iter; for (option_list_t::const_iterator oiter = e->options.begin(); oiter != e->options.end(); oiter++) { const complete_entry_opt_t *o = &*oiter; diff --git a/history.cpp b/history.cpp index a895c68db..476a4c7b6 100644 --- a/history.cpp +++ b/history.cpp @@ -20,6 +20,7 @@ #include "fallback.h" #include "util.h" #include "sanity.h" +#include "tokenizer.h" #include "wutil.h" #include "history.h" @@ -28,6 +29,7 @@ #include "path.h" #include "signal.h" #include "autoload.h" +#include "iothread.h" #include #include @@ -105,7 +107,7 @@ history_item_t::history_item_t(const wcstring &str) : contents(str), creation_ti { } -history_item_t::history_item_t(const wcstring &str, time_t when) : contents(str), creation_timestamp(when) +history_item_t::history_item_t(const wcstring &str, time_t when, const path_list_t &paths) : contents(str), creation_timestamp(when), required_paths(paths) { } @@ -156,14 +158,16 @@ history_t::~history_t() pthread_mutex_destroy(&lock); } -void history_t::add(const wcstring &str) +void history_t::add(const wcstring &str, const path_list_t &valid_paths) { scoped_lock locker(lock); - new_items.push_back(history_item_t(str.c_str(), true)); + /* Add the history items */ + time_t now = time(NULL); + new_items.push_back(history_item_t(str, now, valid_paths)); /* This might be a good candidate for moving to a background thread */ - if((time(0) > save_timestamp + SAVE_INTERVAL) || (new_items.size() >= SAVE_COUNT)) + if((now > save_timestamp + SAVE_INTERVAL) || (new_items.size() >= SAVE_COUNT)) this->save_internal(); } @@ -586,3 +590,106 @@ void history_sanity_check() */ } +struct file_detection_context_t { + /* The history associated with this context */ + history_t *history; + + /* The command */ + wcstring command; + + /* The working directory at the time the command was issued */ + wcstring working_directory; + + /* Paths to test */ + path_list_t potential_paths; + + /* Paths that were found to be valid */ + path_list_t valid_paths; + + int perform_file_detection() { + ASSERT_IS_BACKGROUND_THREAD(); + for (path_list_t::const_iterator iter = potential_paths.begin(); iter != potential_paths.end(); iter++) { + wcstring path = *iter; + + /* Maybe append the working directory. Note that we know path is not empty here. */ + if (path.at(0) != '/') { + path.insert(0, working_directory); + } + + if (0 == waccess(path.c_str(), F_OK)) { + /* Push the original (possibly relative) path */ + valid_paths.push_front(*iter); + } + } + valid_paths.reverse(); + return 0; + } +}; + +static int threaded_perform_file_detection(file_detection_context_t *ctx) { + ASSERT_IS_BACKGROUND_THREAD(); + assert(ctx != NULL); + return ctx->perform_file_detection(); +} + +static void perform_file_detection_done(file_detection_context_t *ctx, int success) { + /* Now that file detection is done, create the history item */ + ctx->history->add(ctx->command, ctx->valid_paths); + + /* Done with the context. */ + delete ctx; +} + +static bool string_could_be_path(const wcstring &potential_path) { + // Assume that things with leading dashes aren't paths + if (potential_path.empty() || potential_path.at(0) == L'-') + return false; + return true; +} + +void history_t::add_with_file_detection(const wcstring &str) +{ + ASSERT_IS_MAIN_THREAD(); + path_list_t potential_paths; + + tokenizer tokenizer; + for( tok_init( &tokenizer, str.c_str(), 0 ); + tok_has_next( &tokenizer ); + tok_next( &tokenizer ) ) + { + int type = tok_last_type( &tokenizer ); + if (type == TOK_STRING) { + const wchar_t *token_cstr = tok_last(&tokenizer); + if (token_cstr) { + wcstring potential_path = token_cstr; + if (unescape_string(potential_path, false) && string_could_be_path(potential_path)) { + potential_paths.push_front(potential_path); + } + } + } + } + + if (! potential_paths.empty()) { + /* We have some paths. Make a context. */ + file_detection_context_t *context = new file_detection_context_t(); + context->command = str; + context->history = this; + + /* Stash the working directory. */ + wchar_t dir_path[4096]; + const wchar_t *cwd = wgetcwd( dir_path, 4096 ); + if (cwd) { + wcstring wd = cwd; + /* Make sure the working directory ends with a slash */ + if (! wd.empty() && wd.at(wd.size() - 1) != L'/') + wd.push_back(L'/'); + context->working_directory.swap(wd); + } + + /* Store the potential paths. Reverse them to put them in the same order as in the command. */ + potential_paths.reverse(); + context->potential_paths.swap(potential_paths); + iothread_perform(threaded_perform_file_detection, perform_file_detection_done, context); + } +} + diff --git a/history.h b/history.h index b4fbb326c..952c91298 100644 --- a/history.h +++ b/history.h @@ -11,10 +11,13 @@ #include #include #include +#include #include #include using std::tr1::shared_ptr; +typedef std::list path_list_t; + enum history_search_type_t { /** The history searches for strings containing the given string */ HISTORY_SEARCH_TYPE_CONTAINS, @@ -28,7 +31,7 @@ class history_item_t { private: history_item_t(const wcstring &); - history_item_t(const wcstring &, time_t); + history_item_t(const wcstring &, time_t, const path_list_t &paths = path_list_t()); /** The actual contents of the entry */ wcstring contents; @@ -36,6 +39,9 @@ class history_item_t { /** Original creation time for the entry */ time_t creation_timestamp; + /** Paths that we require to be valid for this item to be autosuggested */ + path_list_t required_paths; + public: const wcstring &str() const { return contents; } bool empty() const { return contents.empty(); } @@ -101,7 +107,10 @@ public: static history_t & history_with_name(const wcstring &name); /** Add a new history item to the end */ - void add(const wcstring &str); + void add(const wcstring &str, const path_list_t &valid_paths = path_list_t()); + + /** Add a new history item to the end */ + void add_with_file_detection(const wcstring &str); /** Saves history */ void save(); diff --git a/iothread.cpp b/iothread.cpp index 703fda5e7..b302c673b 100644 --- a/iothread.cpp +++ b/iothread.cpp @@ -138,7 +138,7 @@ static void iothread_spawn_if_needed(void) { } } -int iothread_perform(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) { +int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context) { iothread_init(); /* Create and initialize a request. */ diff --git a/iothread.h b/iothread.h index a4246fa43..7619501ab 100644 --- a/iothread.h +++ b/iothread.h @@ -13,7 +13,7 @@ \param context A arbitary context pointer to pass to the handler and completion callback. \return A sequence number, currently not very useful. */ -int iothread_perform(int (*handler)(void *), void (*completionCallback)(void *, int), void *context); +int iothread_perform_base(int (*handler)(void *), void (*completionCallback)(void *, int), void *context); /** Gets the fd on which to listen for completion callbacks. @@ -27,4 +27,10 @@ int iothread_port(void); */ void iothread_service_completion(void); +/** Helper template */ +template +int iothread_perform(int (*handler)(T *), void (*completionCallback)(T *, int), T *context) { + return iothread_perform_base((int (*)(void *))handler, (void (*)(void *, int))completionCallback, static_cast(context)); +} + #endif diff --git a/reader.cpp b/reader.cpp index 1de988fe7..f77ccc702 100644 --- a/reader.cpp +++ b/reader.cpp @@ -2375,9 +2375,8 @@ static void highlight_search(void) { } } -static void highlight_complete(void *ctx_ptr, int result) { +static void highlight_complete(background_highlight_context_t *ctx, int result) { ASSERT_IS_MAIN_THREAD(); - background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr; if (ctx->string_to_highlight == data->command_line) { /* The data hasn't changed, so swap in our colors */ size_t len = ctx->string_to_highlight.size(); @@ -2400,8 +2399,7 @@ static void highlight_complete(void *ctx_ptr, int result) { delete ctx; } -static int threaded_highlight(void *ctx_ptr) { - background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr; +static int threaded_highlight(background_highlight_context_t *ctx) { const wchar_t *delayer = ctx->vars.get(L"HIGHLIGHT_DELAY"); double secDelay = 0; if (delayer) { @@ -3020,10 +3018,10 @@ const wchar_t *reader_readline() */ if( ! data->command_line.empty() ) { -// wcscpy(data->search_buff,L""); - //history_add( data->buff ); - if (data->history) + if (data->history) { data->history->add(data->command_line); + data->history->add_with_file_detection(data->command_line); + } } finished=1; data->buff_pos=data->command_length(); diff --git a/tokenizer.cpp b/tokenizer.cpp index e311d22e1..0d2c90155 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -131,8 +131,8 @@ void tok_init( tokenizer *tok, const wchar_t *b, int flags ) CHECK( b, ); - tok->accept_unfinished = flags & TOK_ACCEPT_UNFINISHED; - tok->show_comments = flags & TOK_SHOW_COMMENTS; + tok->accept_unfinished = !! (flags & TOK_ACCEPT_UNFINISHED); + tok->show_comments = !! (flags & TOK_SHOW_COMMENTS); tok->has_next=1; tok->has_next = (*b != L'\0'); diff --git a/tokenizer.h b/tokenizer.h index 687fd77c4..c0c4034d4 100644 --- a/tokenizer.h +++ b/tokenizer.h @@ -29,8 +29,7 @@ enum token_type TOK_REDIRECT_NOCLOB, /**