mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 05:28:49 +00:00
Initial work towards making autosuggestion smarter by recognizing paths
This commit is contained in:
parent
e2ff77b4ec
commit
a92d9d442b
10 changed files with 142 additions and 26 deletions
|
@ -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;
|
||||
|
|
5
common.h
5
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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
115
history.cpp
115
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
13
history.h
13
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();
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
12
reader.cpp
12
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();
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue