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;
wchar_t *result = unescape(str.c_str(), escape_special);
if ( result) {
if (result) {
str.replace(str.begin(), str.end(), result);
free(result);
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 );
/**
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.
*/

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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();

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();
/* 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.
\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

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();
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();

View file

@ -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');

View file

@ -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