Initial work towards making autosuggestion smarter by recognizing paths

This commit is contained in:
ridiculousfish 2012-02-15 11:33:41 -08:00
parent e2ff77b4ec
commit a92d9d442b
10 changed files with 142 additions and 26 deletions

View file

@ -1506,7 +1506,7 @@ bool unescape_string(wcstring &str, int escape_special)
{ {
bool success = false; bool success = false;
wchar_t *result = unescape(str.c_str(), escape_special); wchar_t *result = unescape(str.c_str(), escape_special);
if ( result) { if (result) {
str.replace(str.begin(), str.end(), result); str.replace(str.begin(), str.end(), result);
free(result); free(result);
success = true; success = true;

View file

@ -547,10 +547,7 @@ void common_handle_winch( int signal );
void write_screen( const wchar_t *msg, string_buffer_t *buff ); void write_screen( const wchar_t *msg, string_buffer_t *buff );
/** /**
Tokenize the specified string into the specified array_list_t. Tokenize the specified string into the specified wcstring_list_t.
Each new element is allocated using malloc and must be freed by the
caller.
\param val the input string. The contents of this string is not changed. \param val the input string. The contents of this string is not changed.
\param out the list in which to place the elements. \param out the list in which to place the elements.
*/ */

View file

@ -1868,9 +1868,9 @@ void complete_print( string_buffer_t *out )
{ {
CHECK( 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++) for (option_list_t::const_iterator oiter = e->options.begin(); oiter != e->options.end(); oiter++)
{ {
const complete_entry_opt_t *o = &*oiter; const complete_entry_opt_t *o = &*oiter;

View file

@ -20,6 +20,7 @@
#include "fallback.h" #include "fallback.h"
#include "util.h" #include "util.h"
#include "sanity.h" #include "sanity.h"
#include "tokenizer.h"
#include "wutil.h" #include "wutil.h"
#include "history.h" #include "history.h"
@ -28,6 +29,7 @@
#include "path.h" #include "path.h"
#include "signal.h" #include "signal.h"
#include "autoload.h" #include "autoload.h"
#include "iothread.h"
#include <map> #include <map>
#include <algorithm> #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); 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); 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 */ /* 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(); 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);
}
}

View file

@ -11,10 +11,13 @@
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <utility> #include <utility>
#include <list>
#include <tr1/memory> #include <tr1/memory>
#include <set> #include <set>
using std::tr1::shared_ptr; using std::tr1::shared_ptr;
typedef std::list<wcstring> path_list_t;
enum history_search_type_t { enum history_search_type_t {
/** The history searches for strings containing the given string */ /** The history searches for strings containing the given string */
HISTORY_SEARCH_TYPE_CONTAINS, HISTORY_SEARCH_TYPE_CONTAINS,
@ -28,7 +31,7 @@ class history_item_t {
private: private:
history_item_t(const wcstring &); 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 */ /** The actual contents of the entry */
wcstring contents; wcstring contents;
@ -36,6 +39,9 @@ class history_item_t {
/** Original creation time for the entry */ /** Original creation time for the entry */
time_t creation_timestamp; time_t creation_timestamp;
/** Paths that we require to be valid for this item to be autosuggested */
path_list_t required_paths;
public: public:
const wcstring &str() const { return contents; } const wcstring &str() const { return contents; }
bool empty() const { return contents.empty(); } bool empty() const { return contents.empty(); }
@ -101,7 +107,10 @@ public:
static history_t & history_with_name(const wcstring &name); static history_t & history_with_name(const wcstring &name);
/** Add a new history item to the end */ /** 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 */ /** Saves history */
void save(); void save();

View file

@ -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(); iothread_init();
/* Create and initialize a request. */ /* Create and initialize a request. */

View file

@ -13,7 +13,7 @@
\param context A arbitary context pointer to pass to the handler and completion callback. \param context A arbitary context pointer to pass to the handler and completion callback.
\return A sequence number, currently not very useful. \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. Gets the fd on which to listen for completion callbacks.
@ -27,4 +27,10 @@ int iothread_port(void);
*/ */
void iothread_service_completion(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 #endif

View file

@ -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(); ASSERT_IS_MAIN_THREAD();
background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr;
if (ctx->string_to_highlight == data->command_line) { if (ctx->string_to_highlight == data->command_line) {
/* The data hasn't changed, so swap in our colors */ /* The data hasn't changed, so swap in our colors */
size_t len = ctx->string_to_highlight.size(); size_t len = ctx->string_to_highlight.size();
@ -2400,8 +2399,7 @@ static void highlight_complete(void *ctx_ptr, int result) {
delete ctx; delete ctx;
} }
static int threaded_highlight(void *ctx_ptr) { static int threaded_highlight(background_highlight_context_t *ctx) {
background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr;
const wchar_t *delayer = ctx->vars.get(L"HIGHLIGHT_DELAY"); const wchar_t *delayer = ctx->vars.get(L"HIGHLIGHT_DELAY");
double secDelay = 0; double secDelay = 0;
if (delayer) { if (delayer) {
@ -3020,10 +3018,10 @@ const wchar_t *reader_readline()
*/ */
if( ! data->command_line.empty() ) if( ! data->command_line.empty() )
{ {
// wcscpy(data->search_buff,L""); if (data->history) {
//history_add( data->buff );
if (data->history)
data->history->add(data->command_line); data->history->add(data->command_line);
data->history->add_with_file_detection(data->command_line);
}
} }
finished=1; finished=1;
data->buff_pos=data->command_length(); data->buff_pos=data->command_length();

View file

@ -131,8 +131,8 @@ void tok_init( tokenizer *tok, const wchar_t *b, int flags )
CHECK( b, ); CHECK( b, );
tok->accept_unfinished = flags & TOK_ACCEPT_UNFINISHED; tok->accept_unfinished = !! (flags & TOK_ACCEPT_UNFINISHED);
tok->show_comments = flags & TOK_SHOW_COMMENTS; tok->show_comments = !! (flags & TOK_SHOW_COMMENTS);
tok->has_next=1; tok->has_next=1;
tok->has_next = (*b != L'\0'); tok->has_next = (*b != L'\0');

View file

@ -29,8 +29,7 @@ enum token_type
TOK_REDIRECT_NOCLOB, /**<? redirection token */ TOK_REDIRECT_NOCLOB, /**<? redirection token */
TOK_BACKGROUND,/**< send job to bg token */ TOK_BACKGROUND,/**< send job to bg token */
TOK_COMMENT/**< comment token */ TOK_COMMENT/**< comment token */
} };
;
/** /**
Tokenizer error types Tokenizer error types