mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 05:28:49 +00:00
Initial stab at autosuggestions
This commit is contained in:
parent
7d3151191d
commit
067dff8489
3 changed files with 100 additions and 46 deletions
20
history.cpp
20
history.cpp
|
@ -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;
|
||||
}
|
||||
|
|
19
history.h
19
history.h
|
@ -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()
|
||||
{}
|
||||
|
||||
|
|
107
reader.cpp
107
reader.cpp
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue