aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--common.cpp2
-rw-r--r--common.h5
-rw-r--r--complete.cpp4
-rw-r--r--history.cpp115
-rw-r--r--history.h13
-rw-r--r--iothread.cpp2
-rw-r--r--iothread.h8
-rw-r--r--reader.cpp12
-rw-r--r--tokenizer.cpp4
-rw-r--r--tokenizer.h3
10 files changed, 142 insertions, 26 deletions
diff --git a/common.cpp b/common.cpp
index 47118cf0..33bbc8ac 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 970f8acd..cdefd554 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 54a0f2cd..7c833ec2 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 a895c68d..476a4c7b 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 <map>
#include <algorithm>
@@ -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 b4fbb326..952c9129 100644
--- a/history.h
+++ b/history.h
@@ -11,10 +11,13 @@
#include <vector>
#include <deque>
#include <utility>
+#include <list>
#include <tr1/memory>
#include <set>
using std::tr1::shared_ptr;
+typedef std::list<wcstring> 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 703fda5e..b302c673 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 a4246fa4..7619501a 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<typename T>
+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<void *>(context));
+}
+
#endif
diff --git a/reader.cpp b/reader.cpp
index 1de988fe..f77ccc70 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 e311d22e..0d2c9015 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 687fd77c..c0c4034d 100644
--- a/tokenizer.h
+++ b/tokenizer.h
@@ -29,8 +29,7 @@ enum token_type
TOK_REDIRECT_NOCLOB, /**<? redirection token */
TOK_BACKGROUND,/**< send job to bg token */
TOK_COMMENT/**< comment token */
-}
- ;
+};
/**
Tokenizer error types