Initial stab at autosuggestions

This commit is contained in:
ridiculousfish 2012-02-06 10:52:13 -08:00
parent 7d3151191d
commit 067dff8489
3 changed files with 100 additions and 46 deletions

View file

@ -19,6 +19,7 @@
#include "fallback.h" #include "fallback.h"
#include "util.h" #include "util.h"
#include "sanity.h"
#include "wutil.h" #include "wutil.h"
#include "history.h" #include "history.h"
@ -110,6 +111,23 @@ history_item_t::history_item_t(const wcstring &str, time_t when) : contents(str)
{ {
} }
bool history_item_t::matches_search(const wcstring &term, enum history_search_type_t type) const {
switch (type) {
case HISTORY_SEARCH_TYPE_CONTAINS:
/* We consider equal strings to NOT match a contains search (so that you don't have to see history equal to what you typed). The length check ensures that. */
return contents.size() > term.size() && contents.find(term) != wcstring::npos;
case HISTORY_SEARCH_TYPE_PREFIX:
/* We consider equal strings to match a prefix search, so that autosuggest will allow suggesting what you've typed */
return string_prefixes_string(term, contents);
default:
sanity_lose();
return false;
}
}
bool history_lru_node_t::write_to_file(FILE *f) const { bool history_lru_node_t::write_to_file(FILE *f) const {
wcstring escaped = key; wcstring escaped = key;
escape_newlines(escaped); escape_newlines(escaped);
@ -372,7 +390,7 @@ bool history_search_t::go_backwards() {
} }
/* Look for a term that matches and that we haven't seen before */ /* Look for a term that matches and that we haven't seen before */
if (item.matches_search(term) && ! match_already_made(item.str())) { if (item.matches_search(term, search_type) && ! match_already_made(item.str())) {
prev_matches.push_back(prev_match_t(idx, item.str())); prev_matches.push_back(prev_match_t(idx, item.str()));
return true; return true;
} }

View file

@ -14,6 +14,14 @@
#include <tr1/memory> #include <tr1/memory>
using std::tr1::shared_ptr; using std::tr1::shared_ptr;
enum history_search_type_t {
/** The history searches for strings containing the given string */
HISTORY_SEARCH_TYPE_CONTAINS,
/** The history searches for strings starting with the given string */
HISTORY_SEARCH_TYPE_PREFIX
};
class history_item_t { class history_item_t {
friend class history_t; friend class history_t;
@ -31,8 +39,8 @@ class history_item_t {
const wcstring &str() const { return contents; } const wcstring &str() const { return contents; }
bool empty() const { return contents.empty(); } bool empty() const { return contents.empty(); }
/* We consider equal strings to NOT match a search (so that you don't have to see history equal to what you typed) */ /* Whether our contents matches a search term. */
bool matches_search(const wcstring &val) const { return contents.size() > val.size() && contents.find(val) != wcstring::npos; } bool matches_search(const wcstring &term, enum history_search_type_t type) const;
time_t timestamp() const { return creation_timestamp; } time_t timestamp() const { return creation_timestamp; }
@ -106,6 +114,9 @@ class history_search_t {
/** The history in which we are searching */ /** The history in which we are searching */
history_t * history; history_t * history;
/** Our type */
enum history_search_type_t search_type;
/** Our list of previous matches as index, value. The end is the current match. */ /** Our list of previous matches as index, value. The end is the current match. */
typedef std::pair<size_t, wcstring> prev_match_t; typedef std::pair<size_t, wcstring> prev_match_t;
std::deque<prev_match_t> prev_matches; std::deque<prev_match_t> prev_matches;
@ -137,14 +148,16 @@ class history_search_t {
wcstring current_item(void) const; wcstring current_item(void) const;
/** Constructor */ /** Constructor */
history_search_t(history_t &hist, const wcstring &str) : history_search_t(history_t &hist, const wcstring &str, enum history_search_type_t type = HISTORY_SEARCH_TYPE_CONTAINS) :
history(&hist), history(&hist),
search_type(type),
term(str) term(str)
{} {}
/* Default constructor */ /* Default constructor */
history_search_t() : history_search_t() :
history(), history(),
search_type(HISTORY_SEARCH_TYPE_CONTAINS),
term() term()
{} {}

View file

@ -175,6 +175,9 @@ commence.
*/ */
#define SEARCH_FORWARD 1 #define SEARCH_FORWARD 1
/* A color is an int */
typedef int color_t;
/** /**
A struct describing the state of the interactive reader. These A struct describing the state of the interactive reader. These
states can be stacked, in case reader_readline() calls are states can be stacked, in case reader_readline() calls are
@ -184,10 +187,11 @@ class reader_data_t
{ {
public: public:
/** /** String containing the whole current commandline */
Buffer containing the whole current commandline
*/
wcstring command_line; wcstring command_line;
/** String containing the autosuggestion */
wcstring autosuggestion;
/** /**
The representation of the current screen contents The representation of the current screen contents
@ -255,12 +259,10 @@ class reader_data_t
color[i] is the classification (according to the enum in color[i] is the classification (according to the enum in
highlight.h) of buff[i]. highlight.h) of buff[i].
*/ */
int *color; std::vector<color_t> colors;
/** /** An array defining the block level at each character. */
An array defining the block level at each character. std::vector<int> indents;
*/
int *indent;
/** /**
Function for tab completion Function for tab completion
@ -427,15 +429,34 @@ int reader_exit_forced()
static void reader_repaint() static void reader_repaint()
{ {
//PCA INSTANCED_PARSER what is this call for? //Update the indentation
parser_t::principal_parser().test( data->command_line.c_str(), data->indent, 0, 0 ); parser_t::principal_parser().test( data->command_line.c_str(), &data->indents[0], 0, 0 );
#if 0
s_write( &data->screen, s_write( &data->screen,
data->prompt_buff.c_str(), data->prompt_buff.c_str(),
data->command_line.c_str(), data->command_line.c_str(),
data->color, &data->colors[0],
data->indent, &data->indents[0],
data->buff_pos ); data->buff_pos );
#else
wcstring full_line = (data->autosuggestion.empty() ? data->command_line : data->autosuggestion);
size_t len = std::max((size_t)1, full_line.size());
std::vector<color_t> colors = data->colors;
colors.resize(len, FISH_COLOR_NORMAL);
std::vector<int> indents = data->indents;
indents.resize(len);
s_write( &data->screen,
data->prompt_buff.c_str(),
full_line.c_str(),
&colors[0],
&indents[0],
data->buff_pos );
#endif
data->repaint_needed = 0; data->repaint_needed = 0;
} }
@ -532,19 +553,9 @@ static int check_size()
{ {
data->buff_sz = maxi( 128, data->command_length()*2 ); data->buff_sz = maxi( 128, data->command_length()*2 );
data->command_line.reserve(data->buff_sz); data->command_line.reserve(data->buff_sz);
data->colors.resize(data->buff_sz);
data->color = (int *)realloc( data->color, data->indents.resize(data->buff_sz);
sizeof(int)*data->buff_sz);
data->indent = (int *)realloc( data->indent,
sizeof(int)*data->buff_sz);
if( data->color==0 ||
data->indent == 0 )
{
DIE_MEM();
}
} }
return 1; return 1;
} }
@ -777,8 +788,7 @@ static void remove_backward()
data->command_line.erase(data->buff_pos-1, 1); data->command_line.erase(data->buff_pos-1, 1);
data->buff_pos--; data->buff_pos--;
reader_super_highlight_me_plenty( data->buff_pos, reader_super_highlight_me_plenty( data->buff_pos, 0 );
0 );
reader_repaint(); reader_repaint();
@ -796,8 +806,7 @@ static int insert_string(const wcstring &str)
data->command_line.insert(data->buff_pos, str); data->command_line.insert(data->buff_pos, str);
data->buff_pos += len; data->buff_pos += len;
/* Syntax highlight */ /* Syntax highlight */
reader_super_highlight_me_plenty( data->buff_pos-1, reader_super_highlight_me_plenty( data->buff_pos-1, 0 );
0 );
reader_repaint(); reader_repaint();
return 1; return 1;
@ -808,9 +817,9 @@ static int insert_string(const wcstring &str)
Insert the character into the command line buffer and print it to Insert the character into the command line buffer and print it to
the screen using syntax highlighting, etc. the screen using syntax highlighting, etc.
*/ */
static int insert_char( int c ) static int insert_char( wchar_t c )
{ {
return insert_string( wcstring(1, (wchar_t)c) ); return insert_string( wcstring(1, c) );
} }
@ -1272,6 +1281,17 @@ static void run_pager( wchar_t *prefix, int is_quoted, const std::vector<complet
io_buffer_destroy( in); io_buffer_destroy( in);
} }
static void update_autosuggestion(void) {
/* Updates autosuggestion. We look for an autosuggestion if the command line is non-empty and if we're not doing a history search. */
data->autosuggestion.clear();
if (! data->command_line.empty() && data->history_search.is_at_end()) {
history_search_t searcher = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX);
if (searcher.go_backwards()) {
data->autosuggestion = searcher.current_item();
}
}
}
/** /**
Flash the screen. This function only changed the color of the Flash the screen. This function only changed the color of the
current line, since the flash_screen sequnce is rather painful to current line, since the flash_screen sequnce is rather painful to
@ -1283,7 +1303,7 @@ static void reader_flash()
for( size_t i=0; i<data->buff_pos; i++ ) for( size_t i=0; i<data->buff_pos; i++ )
{ {
data->color[i] = HIGHLIGHT_SEARCH_MATCH<<16; data->colors.at(i) = HIGHLIGHT_SEARCH_MATCH<<16;
} }
reader_repaint(); reader_repaint();
@ -1293,6 +1313,7 @@ static void reader_flash()
nanosleep( &pollint, NULL ); nanosleep( &pollint, NULL );
reader_super_highlight_me_plenty( data->buff_pos, 0 ); reader_super_highlight_me_plenty( data->buff_pos, 0 );
reader_repaint(); reader_repaint();
@ -2247,9 +2268,6 @@ void reader_pop()
} }
data=data->next; data=data->next;
free( n->color );
free( n->indent );
sb_destroy( &n->kill_item ); sb_destroy( &n->kill_item );
/* Invoke the destructor to balance our new */ /* Invoke the destructor to balance our new */
@ -2353,7 +2371,7 @@ static void highlight_search(void) {
for( i=0; i<count; i++ ) for( i=0; i<count; i++ )
{ {
data->color[start+i] |= HIGHLIGHT_SEARCH_MATCH<<16; data->colors.at(start+i) |= HIGHLIGHT_SEARCH_MATCH<<16;
} }
} }
} }
@ -2364,9 +2382,13 @@ static void highlight_complete(void *ctx_ptr, int result) {
background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr; 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 */
free(data->color); size_t len = ctx->string_to_highlight.size();
data->color = ctx->color; data->colors.clear();
data->colors.insert(data->colors.begin(), ctx->color, ctx->color + len);
free(ctx->color);
ctx->color = NULL; ctx->color = NULL;
//data->repaint_needed = 1; //data->repaint_needed = 1;
//s_reset( &data->screen, 1 ); //s_reset( &data->screen, 1 );
highlight_search(); highlight_search();
@ -2413,6 +2435,7 @@ static void reader_super_highlight_me_plenty( int match_highlight_pos, array_lis
data->highlight_function( ctx->buff, ctx->color, match_highlight_pos, error, highlight_complete2, ctx ); data->highlight_function( ctx->buff, ctx->color, match_highlight_pos, error, highlight_complete2, ctx );
#endif #endif
highlight_search(); highlight_search();
update_autosuggestion();
} }
@ -3051,7 +3074,7 @@ const wchar_t *reader_readline()
} }
data->search_buff.append(data->command_line); data->search_buff.append(data->command_line);
data->history_search = history_search_t(*data->history, data->search_buff); data->history_search = history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS);
} }
switch( data->search_mode ) switch( data->search_mode )
@ -3193,8 +3216,8 @@ const wchar_t *reader_readline()
base_pos_old = parse_util_get_offset_from_line( data->command_line, line_old ); base_pos_old = parse_util_get_offset_from_line( data->command_line, line_old );
indent_old = data->indent[base_pos_old]; indent_old = data->indents.at(base_pos_old);
indent_new = data->indent[base_pos_new]; indent_new = data->indents.at(base_pos_new);
line_offset_old = data->buff_pos - parse_util_get_offset_from_line( data->command_line, line_old ); line_offset_old = data->buff_pos - parse_util_get_offset_from_line( data->command_line, line_old );
total_offset_new = parse_util_get_offset( data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old)); total_offset_new = parse_util_get_offset( data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old));