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 "util.h"
#include "sanity.h"
#include "wutil.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 {
wcstring escaped = key;
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 */
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()));
return true;
}

View file

@ -14,6 +14,14 @@
#include <tr1/memory>
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 {
friend class history_t;
@ -31,8 +39,8 @@ class history_item_t {
const wcstring &str() const { return contents; }
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) */
bool matches_search(const wcstring &val) const { return contents.size() > val.size() && contents.find(val) != wcstring::npos; }
/* Whether our contents matches a search term. */
bool matches_search(const wcstring &term, enum history_search_type_t type) const;
time_t timestamp() const { return creation_timestamp; }
@ -106,6 +114,9 @@ class history_search_t {
/** The history in which we are searching */
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. */
typedef std::pair<size_t, wcstring> prev_match_t;
std::deque<prev_match_t> prev_matches;
@ -137,14 +148,16 @@ class history_search_t {
wcstring current_item(void) const;
/** 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),
search_type(type),
term(str)
{}
/* Default constructor */
history_search_t() :
history(),
search_type(HISTORY_SEARCH_TYPE_CONTAINS),
term()
{}

View file

@ -175,6 +175,9 @@ commence.
*/
#define SEARCH_FORWARD 1
/* A color is an int */
typedef int color_t;
/**
A struct describing the state of the interactive reader. These
states can be stacked, in case reader_readline() calls are
@ -184,10 +187,11 @@ class reader_data_t
{
public:
/**
Buffer containing the whole current commandline
*/
/** String containing the whole current commandline */
wcstring command_line;
/** String containing the autosuggestion */
wcstring autosuggestion;
/**
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
highlight.h) of buff[i].
*/
int *color;
std::vector<color_t> colors;
/**
An array defining the block level at each character.
*/
int *indent;
/** An array defining the block level at each character. */
std::vector<int> indents;
/**
Function for tab completion
@ -427,15 +429,34 @@ int reader_exit_forced()
static void reader_repaint()
{
//PCA INSTANCED_PARSER what is this call for?
parser_t::principal_parser().test( data->command_line.c_str(), data->indent, 0, 0 );
//Update the indentation
parser_t::principal_parser().test( data->command_line.c_str(), &data->indents[0], 0, 0 );
#if 0
s_write( &data->screen,
data->prompt_buff.c_str(),
data->command_line.c_str(),
data->color,
data->indent,
&data->colors[0],
&data->indents[0],
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;
}
@ -532,19 +553,9 @@ static int check_size()
{
data->buff_sz = maxi( 128, data->command_length()*2 );
data->command_line.reserve(data->buff_sz);
data->color = (int *)realloc( data->color,
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();
}
data->command_line.reserve(data->buff_sz);
data->colors.resize(data->buff_sz);
data->indents.resize(data->buff_sz);
}
return 1;
}
@ -777,8 +788,7 @@ static void remove_backward()
data->command_line.erase(data->buff_pos-1, 1);
data->buff_pos--;
reader_super_highlight_me_plenty( data->buff_pos,
0 );
reader_super_highlight_me_plenty( data->buff_pos, 0 );
reader_repaint();
@ -796,8 +806,7 @@ static int insert_string(const wcstring &str)
data->command_line.insert(data->buff_pos, str);
data->buff_pos += len;
/* Syntax highlight */
reader_super_highlight_me_plenty( data->buff_pos-1,
0 );
reader_super_highlight_me_plenty( data->buff_pos-1, 0 );
reader_repaint();
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
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);
}
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
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++ )
{
data->color[i] = HIGHLIGHT_SEARCH_MATCH<<16;
data->colors.at(i) = HIGHLIGHT_SEARCH_MATCH<<16;
}
reader_repaint();
@ -1293,6 +1313,7 @@ static void reader_flash()
nanosleep( &pollint, NULL );
reader_super_highlight_me_plenty( data->buff_pos, 0 );
reader_repaint();
@ -2247,9 +2268,6 @@ void reader_pop()
}
data=data->next;
free( n->color );
free( n->indent );
sb_destroy( &n->kill_item );
/* Invoke the destructor to balance our new */
@ -2353,7 +2371,7 @@ static void highlight_search(void) {
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;
if (ctx->string_to_highlight == data->command_line) {
/* The data hasn't changed, so swap in our colors */
free(data->color);
data->color = ctx->color;
size_t len = ctx->string_to_highlight.size();
data->colors.clear();
data->colors.insert(data->colors.begin(), ctx->color, ctx->color + len);
free(ctx->color);
ctx->color = NULL;
//data->repaint_needed = 1;
//s_reset( &data->screen, 1 );
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 );
#endif
highlight_search();
update_autosuggestion();
}
@ -3051,7 +3074,7 @@ const wchar_t *reader_readline()
}
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 )
@ -3193,8 +3216,8 @@ const wchar_t *reader_readline()
base_pos_old = parse_util_get_offset_from_line( data->command_line, line_old );
indent_old = data->indent[base_pos_old];
indent_new = data->indent[base_pos_new];
indent_old = data->indents.at(base_pos_old);
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 );
total_offset_new = parse_util_get_offset( data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old));