mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 04:13:08 +00:00
Work on new history implementation
This commit is contained in:
parent
7fcf25a78f
commit
5ad6849d4e
16 changed files with 789 additions and 194 deletions
|
@ -2151,7 +2151,7 @@ static int builtin_read( parser_t &parser, wchar_t **argv )
|
|||
reader_set_prompt( prompt );
|
||||
if( shell )
|
||||
{
|
||||
reader_set_complete_function( &complete2 );
|
||||
reader_set_complete_function( &complete );
|
||||
reader_set_highlight_function( &highlight_shell );
|
||||
reader_set_test_function( &reader_shell_test );
|
||||
}
|
||||
|
|
|
@ -557,7 +557,7 @@ static int builtin_complete( parser_t &parser, wchar_t **argv )
|
|||
|
||||
// comp = al_halloc( 0 );
|
||||
|
||||
complete2( do_complete, comp );
|
||||
complete( do_complete, comp );
|
||||
|
||||
for( size_t i=0; i< comp.size() ; i++ )
|
||||
{
|
||||
|
|
10
complete.cpp
10
complete.cpp
|
@ -1074,7 +1074,7 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
if( use_command )
|
||||
{
|
||||
|
||||
if( expand_string2(cmd, comp, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) != EXPAND_ERROR )
|
||||
if( expand_string(cmd, comp, ACCEPT_INCOMPLETE | EXECUTABLES_ONLY ) != EXPAND_ERROR )
|
||||
{
|
||||
complete_cmd_desc( cmd, comp );
|
||||
}
|
||||
|
@ -1113,7 +1113,7 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
|
||||
prev_count = comp.size() ;
|
||||
|
||||
if( expand_string2(
|
||||
if( expand_string(
|
||||
nxt_completion,
|
||||
comp,
|
||||
ACCEPT_INCOMPLETE |
|
||||
|
@ -1180,7 +1180,7 @@ static void complete_cmd( const wchar_t *cmd,
|
|||
continue;
|
||||
}
|
||||
|
||||
if( expand_string2( nxt_completion,
|
||||
if( expand_string( nxt_completion,
|
||||
comp,
|
||||
ACCEPT_INCOMPLETE | DIRECTORIES_ONLY ) != EXPAND_ERROR )
|
||||
{
|
||||
|
@ -1563,7 +1563,7 @@ static void complete_param_expand( wchar_t *str,
|
|||
ACCEPT_INCOMPLETE |
|
||||
(do_file?0:EXPAND_SKIP_WILDCARDS);
|
||||
|
||||
if( expand_string2( wcsdup(comp_str),
|
||||
if( expand_string( wcsdup(comp_str),
|
||||
comp_out,
|
||||
flags ) == EXPAND_ERROR )
|
||||
{
|
||||
|
@ -1755,7 +1755,7 @@ static int try_complete_user( const wchar_t *cmd,
|
|||
return res;
|
||||
}
|
||||
|
||||
void complete2( const wchar_t *cmd,
|
||||
void complete( const wchar_t *cmd,
|
||||
std::vector<completion_t> &comp )
|
||||
{
|
||||
wchar_t *tok_begin, *tok_end, *cmdsubst_begin, *cmdsubst_end, *prev_begin, *prev_end;
|
||||
|
|
13
complete.h
13
complete.h
|
@ -219,18 +219,9 @@ void complete_remove( const wchar_t *cmd,
|
|||
const wchar_t *long_opt );
|
||||
|
||||
/**
|
||||
Find all completions of the command cmd, insert them into out. The
|
||||
caller must free the variables returned in out. The results are
|
||||
returned in the array_list_t 'out', in the format of wide character
|
||||
strings, with each element consisting of a suggested completion and
|
||||
a description of what kind of object this completion represents,
|
||||
separated by a separator of type COMPLETE_SEP.
|
||||
|
||||
Values returned by this function should be freed by the caller.
|
||||
Find all completions of the command cmd, insert them into out.
|
||||
*/
|
||||
//void complete( const wchar_t *cmd, array_list_t *out );
|
||||
|
||||
void complete2( const wchar_t* cmd, std::vector<completion_t> &out);
|
||||
void complete( const wchar_t* cmd, std::vector<completion_t> &out);
|
||||
|
||||
/**
|
||||
Print a list of all current completions into the string_buffer_t.
|
||||
|
|
77
env.cpp
77
env.cpp
|
@ -190,10 +190,10 @@ static buffer_t export_buffer;
|
|||
static int has_changed = 1;
|
||||
|
||||
/**
|
||||
This stringbuffer is used to store the value of dynamically
|
||||
This string is used to store the value of dynamically
|
||||
generated variables, such as history.
|
||||
*/
|
||||
static string_buffer_t dyn_var;
|
||||
static wcstring dyn_var;
|
||||
|
||||
/**
|
||||
Variable used by env_get_names to communicate auxiliary information
|
||||
|
@ -531,7 +531,6 @@ void env_init()
|
|||
wchar_t *uname;
|
||||
wchar_t *version;
|
||||
|
||||
sb_init( &dyn_var );
|
||||
b_init( &export_buffer );
|
||||
|
||||
/*
|
||||
|
@ -700,8 +699,6 @@ void env_destroy()
|
|||
{
|
||||
env_universal_destroy();
|
||||
|
||||
sb_destroy( &dyn_var );
|
||||
|
||||
b_destroy( &export_buffer );
|
||||
|
||||
while( &top->env != global )
|
||||
|
@ -1160,7 +1157,7 @@ env_var_t env_get_string( const wchar_t *key )
|
|||
for( i=add_current;; i++ )
|
||||
{
|
||||
// PCA This looks bad!
|
||||
wchar_t *next = history_get( i-add_current );
|
||||
wchar_t *next = NULL;//history_get( i-add_current );
|
||||
if( !next )
|
||||
{
|
||||
break;
|
||||
|
@ -1242,7 +1239,7 @@ env_var_t env_get_string( const wchar_t *key )
|
|||
}
|
||||
}
|
||||
|
||||
wchar_t *env_get( const wchar_t *key )
|
||||
const wchar_t *env_get( const wchar_t *key )
|
||||
{
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
|
@ -1255,58 +1252,52 @@ wchar_t *env_get( const wchar_t *key )
|
|||
if( wcscmp( key, L"history" ) == 0 )
|
||||
{
|
||||
wchar_t *current;
|
||||
int i;
|
||||
int add_current=0;
|
||||
sb_clear( &dyn_var );
|
||||
dyn_var.clear();
|
||||
|
||||
current = reader_get_buffer();
|
||||
if( current && wcslen( current ) )
|
||||
{
|
||||
add_current=1;
|
||||
sb_append( &dyn_var, current );
|
||||
}
|
||||
|
||||
for( i=add_current;; i++ )
|
||||
{
|
||||
wchar_t *next = history_get( i-add_current );
|
||||
if( !next )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if( i!=0)
|
||||
{
|
||||
sb_append( &dyn_var, ARRAY_SEP_STR );
|
||||
}
|
||||
|
||||
sb_append( &dyn_var, next );
|
||||
}
|
||||
|
||||
return (wchar_t *)dyn_var.buff;
|
||||
dyn_var.append(current);
|
||||
dyn_var.append(ARRAY_SEP_STR);
|
||||
}
|
||||
|
||||
history_t *history = reader_get_history();
|
||||
if (history) {
|
||||
for (size_t idx = 1; idx < (size_t)(-1); idx++) {
|
||||
history_item_t item = history->item_at_index(idx);
|
||||
if (item.empty()) break;
|
||||
|
||||
dyn_var.append(item.str());
|
||||
dyn_var.append(ARRAY_SEP_STR);
|
||||
}
|
||||
}
|
||||
|
||||
/* We always have a trailing ARRAY_SEP_STR; get rid of it */
|
||||
if (dyn_var.size() >= wcslen(ARRAY_SEP_STR)) {
|
||||
dyn_var.resize(dyn_var.size() - wcslen(ARRAY_SEP_STR));
|
||||
}
|
||||
|
||||
return dyn_var.c_str();
|
||||
}
|
||||
else if( wcscmp( key, L"COLUMNS" )==0 )
|
||||
{
|
||||
sb_clear( &dyn_var );
|
||||
sb_printf( &dyn_var, L"%d", common_get_width() );
|
||||
return (wchar_t *)dyn_var.buff;
|
||||
dyn_var = to_string<int>(common_get_width());
|
||||
return dyn_var.c_str();
|
||||
}
|
||||
else if( wcscmp( key, L"LINES" )==0 )
|
||||
{
|
||||
sb_clear( &dyn_var );
|
||||
sb_printf( &dyn_var, L"%d", common_get_height() );
|
||||
return (wchar_t *)dyn_var.buff;
|
||||
dyn_var = to_string<int>(common_get_height());
|
||||
return dyn_var.c_str();
|
||||
}
|
||||
else if( wcscmp( key, L"status" )==0 )
|
||||
{
|
||||
sb_clear( &dyn_var );
|
||||
sb_printf( &dyn_var, L"%d", proc_get_last_status() );
|
||||
return (wchar_t *)dyn_var.buff;
|
||||
dyn_var = to_string<int>(proc_get_last_status());
|
||||
return dyn_var.c_str();
|
||||
}
|
||||
else if( wcscmp( key, L"umask" )==0 )
|
||||
{
|
||||
sb_clear( &dyn_var );
|
||||
sb_printf( &dyn_var, L"0%0.3o", get_umask() );
|
||||
return (wchar_t *)dyn_var.buff;
|
||||
dyn_var = format_string(L"0%0.3o", get_umask());
|
||||
return dyn_var.c_str();
|
||||
}
|
||||
|
||||
while( env != 0 )
|
||||
|
|
2
env.h
2
env.h
|
@ -93,7 +93,7 @@ int env_set( const wchar_t *key,
|
|||
valid until the next call to env_get(), env_set(), env_push() or
|
||||
env_pop() takes place.
|
||||
*/
|
||||
wchar_t *env_get( const wchar_t *key );
|
||||
const wchar_t *env_get( const wchar_t *key );
|
||||
|
||||
class env_var_t : public wcstring {
|
||||
private:
|
||||
|
|
|
@ -1506,7 +1506,7 @@ static void remove_internal_separator2( wcstring &s, int conv )
|
|||
}
|
||||
|
||||
|
||||
int expand_string2( const wcstring &input, std::vector<completion_t> &output, int flags )
|
||||
int expand_string( const wcstring &input, std::vector<completion_t> &output, int flags )
|
||||
{
|
||||
parser_t parser(PARSER_TYPE_ERRORS_ONLY);
|
||||
std::vector<completion_t> list1, list2;
|
||||
|
@ -1725,7 +1725,7 @@ bool expand_one(wcstring &string, int flags) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (expand_string2(string, completions, flags)) {
|
||||
if (expand_string(string, completions, flags)) {
|
||||
if (completions.size() == 1) {
|
||||
string = completions.at(0).completion;
|
||||
result = true;
|
||||
|
|
12
expand.h
12
expand.h
|
@ -138,18 +138,12 @@ class parser_t;
|
|||
out. If expansion is performed, the original parameter is freed and
|
||||
newly allocated strings are inserted into the list out.
|
||||
|
||||
If \c context is non-null, all the strings contained in the
|
||||
array_list_t \c out will be registered to be free'd when context is
|
||||
free'd.
|
||||
|
||||
\param context the halloc context to use for automatic deallocation
|
||||
\param in The parameter to expand
|
||||
\param input The parameter to expand
|
||||
\param output The list to which the result will be appended.
|
||||
\param flag Specifies if any expansion pass should be skipped. Legal values are any combination of EXPAND_SKIP_CMDSUBST EXPAND_SKIP_VARIABLES and EXPAND_SKIP_WILDCARDS
|
||||
\param out The list to which the result will be appended.
|
||||
\return One of EXPAND_OK, EXPAND_ERROR, EXPAND_WILDCARD_MATCH and EXPAND_WILDCARD_NO_MATCH. EXPAND_WILDCARD_NO_MATCH and EXPAND_WILDCARD_MATCH are normal exit conditions used only on strings containing wildcards to tell if the wildcard produced any matches.
|
||||
*/
|
||||
__warn_unused int expand_string( void *context, wchar_t *in, array_list_t *out, int flag );
|
||||
__warn_unused int expand_string2( const wcstring &input, std::vector<completion_t> &output, int flag );
|
||||
__warn_unused int expand_string( const wcstring &input, std::vector<completion_t> &output, int flag );
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "path.h"
|
||||
#include "halloc.h"
|
||||
#include "halloc_util.h"
|
||||
#include "history.h"
|
||||
|
||||
/**
|
||||
The number of tests to run
|
||||
|
@ -679,14 +680,13 @@ static void test_lru(void) {
|
|||
|
||||
static int expand_test( const wchar_t *in, int flags, ... )
|
||||
{
|
||||
array_list_t out;
|
||||
std::vector<completion_t> output;
|
||||
va_list va;
|
||||
int i=0;
|
||||
size_t i=0;
|
||||
int res=1;
|
||||
wchar_t *arg;
|
||||
|
||||
al_init( &out );
|
||||
if( expand_string( 0, wcsdup(in), &out, flags) )
|
||||
if( expand_string( in, output, flags) )
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -696,13 +696,13 @@ static int expand_test( const wchar_t *in, int flags, ... )
|
|||
|
||||
while( (arg=va_arg(va, wchar_t *) )!= 0 )
|
||||
{
|
||||
if( al_get_count( &out ) == i )
|
||||
if( output.size() == i )
|
||||
{
|
||||
res=0;
|
||||
break;
|
||||
}
|
||||
|
||||
if( wcscmp( (wchar_t *)al_get( &out, i ),arg) != 0 )
|
||||
if (output.at(i).completion != arg)
|
||||
{
|
||||
res=0;
|
||||
break;
|
||||
|
@ -712,7 +712,6 @@ static int expand_test( const wchar_t *in, int flags, ... )
|
|||
}
|
||||
va_end( va );
|
||||
|
||||
al_foreach( &out, &free );
|
||||
return res;
|
||||
|
||||
}
|
||||
|
@ -770,7 +769,7 @@ static void test_path()
|
|||
void perf_complete()
|
||||
{
|
||||
wchar_t c;
|
||||
array_list_t out;
|
||||
std::vector<completion_t> out;
|
||||
long long t1, t2;
|
||||
int matches=0;
|
||||
double t;
|
||||
|
@ -783,7 +782,6 @@ void perf_complete()
|
|||
|
||||
|
||||
say( L"Testing completion performance" );
|
||||
al_init( &out );
|
||||
|
||||
reader_push(L"");
|
||||
say( L"Here we go" );
|
||||
|
@ -796,12 +794,10 @@ void perf_complete()
|
|||
str[0]=c;
|
||||
reader_set_buffer( str, 0 );
|
||||
|
||||
complete( str, &out );
|
||||
complete( str, out );
|
||||
|
||||
matches += al_get_count( &out );
|
||||
|
||||
al_foreach( &out, &free );
|
||||
al_truncate( &out, 0 );
|
||||
matches += out.size();
|
||||
out.clear();
|
||||
}
|
||||
t2=get_time();
|
||||
|
||||
|
@ -818,12 +814,10 @@ void perf_complete()
|
|||
|
||||
reader_set_buffer( str, 0 );
|
||||
|
||||
complete( str, &out );
|
||||
complete( str, out );
|
||||
|
||||
matches += al_get_count( &out );
|
||||
|
||||
al_foreach( &out, &free );
|
||||
al_truncate( &out, 0 );
|
||||
matches += out.size();
|
||||
out.clear();
|
||||
}
|
||||
t2=get_time();
|
||||
|
||||
|
@ -831,12 +825,42 @@ void perf_complete()
|
|||
|
||||
say( L"Two letter command completion took %f seconds per completion, %f microseconds/match", t, (double)(t2-t1)/matches );
|
||||
|
||||
al_destroy( &out );
|
||||
|
||||
reader_pop();
|
||||
|
||||
}
|
||||
|
||||
static void test_history_matches(history_search_t &search, size_t matches) {
|
||||
size_t i;
|
||||
for (i=0; i < matches; i++) {
|
||||
assert(search.go_backwards());
|
||||
}
|
||||
assert(! search.go_backwards());
|
||||
|
||||
for (i=1; i < matches; i++) {
|
||||
assert(search.go_forwards());
|
||||
}
|
||||
assert(! search.go_forwards());
|
||||
}
|
||||
|
||||
static void test_history(void) {
|
||||
say( L"Testing history");
|
||||
|
||||
history_t &history = history_t::history_with_name(L"test_history");
|
||||
history.add(L"Alpha");
|
||||
history.add(L"Beta");
|
||||
history.add(L"Gamma");
|
||||
|
||||
/* All three items match "a" */
|
||||
history_search_t search1(history, L"a");
|
||||
test_history_matches(search1, 3);
|
||||
assert(search1.current_item().str() == L"Alpha");
|
||||
|
||||
/* One item matches "et" */
|
||||
history_search_t search2(history, L"et");
|
||||
test_history_matches(search2, 1);
|
||||
assert(search2.current_item().str() == L"Beta");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -869,6 +893,7 @@ int main( int argc, char **argv )
|
|||
test_lru();
|
||||
test_expand();
|
||||
test_path();
|
||||
test_history();
|
||||
|
||||
say( L"Encountered %d errors in low-level tests", err_count );
|
||||
|
||||
|
|
496
history.cpp
496
history.cpp
|
@ -28,7 +28,327 @@
|
|||
#include "intern.h"
|
||||
#include "path.h"
|
||||
#include "signal.h"
|
||||
#include <map>
|
||||
|
||||
static pthread_mutex_t hist_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static std::map<wcstring, history_t *> histories;
|
||||
|
||||
static wcstring history_filename(const wcstring &name, const wcstring &suffix);
|
||||
|
||||
static hash_table_t *mode_table=0;
|
||||
|
||||
/* Unescapes newlines in-place */
|
||||
static void unescape_newlines(wcstring &str);
|
||||
|
||||
/* Custom deleter for our shared_ptr */
|
||||
class history_item_data_deleter_t {
|
||||
private:
|
||||
const bool free_it;
|
||||
public:
|
||||
history_item_data_deleter_t(bool flag) : free_it(flag) { }
|
||||
void operator()(const wchar_t *data) {
|
||||
if (free_it)
|
||||
free((void *)data);
|
||||
}
|
||||
};
|
||||
|
||||
history_item_t::history_item_t(const wcstring &str) : contents(str), timestamp(time(NULL))
|
||||
{
|
||||
}
|
||||
|
||||
history_item_t::history_item_t(const wcstring &str, time_t when) : contents(str), timestamp(when)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
history_t & history_t::history_with_name(const wcstring &name) {
|
||||
/* Note that histories are currently never deleted, so we can return a reference to them without using something like shared_ptr */
|
||||
scoped_lock locker(hist_lock);
|
||||
history_t *& current = histories[name];
|
||||
if (current == NULL)
|
||||
current = new history_t(name);
|
||||
return *current;
|
||||
}
|
||||
|
||||
history_t::history_t(const wcstring &pname) :
|
||||
name(pname),
|
||||
mmap_start(NULL),
|
||||
mmap_length(0),
|
||||
save_timestamp(0),
|
||||
loaded_old(false)
|
||||
{
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
}
|
||||
|
||||
history_t::~history_t()
|
||||
{
|
||||
pthread_mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
void history_t::add(const wcstring &str)
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
new_items.push_back(history_item_t(str.c_str(), true));
|
||||
}
|
||||
|
||||
history_item_t history_t::item_at_index(size_t idx) {
|
||||
scoped_lock locker(lock);
|
||||
|
||||
/* 0 is considered an invalid index */
|
||||
assert(idx > 0);
|
||||
idx--;
|
||||
|
||||
/* idx=0 corresponds to last item in new_items */
|
||||
size_t new_item_count = new_items.size();
|
||||
if (idx < new_item_count) {
|
||||
return new_items.at(new_item_count - idx - 1);
|
||||
}
|
||||
|
||||
/* Now look in our old items */
|
||||
idx -= new_item_count;
|
||||
load_old_if_needed();
|
||||
size_t old_item_count = old_item_offsets.size();
|
||||
if (idx < old_item_count) {
|
||||
/* idx=0 corresponds to last item in old_item_offsets */
|
||||
size_t offset = old_item_offsets.at(old_item_count - idx - 1);
|
||||
return history_t::decode_item(mmap_start + offset, mmap_length - offset);
|
||||
}
|
||||
|
||||
/* Index past the valid range, so return an empty history item */
|
||||
return history_item_t(wcstring(), 0);
|
||||
}
|
||||
|
||||
history_item_t history_t::decode_item(const char *begin, size_t len)
|
||||
{
|
||||
const char *pos = begin, *end = begin + len;
|
||||
|
||||
int was_backslash = 0;
|
||||
|
||||
wcstring output;
|
||||
time_t timestamp = 0;
|
||||
|
||||
int first_char = 1;
|
||||
bool timestamp_mode = false;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
wchar_t c;
|
||||
mbstate_t state;
|
||||
bzero(&state, sizeof state);
|
||||
|
||||
size_t res;
|
||||
|
||||
res = mbrtowc( &c, pos, end-pos, &state );
|
||||
|
||||
if( res == (size_t)-1 )
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
else if( res == (size_t)-2 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( res == (size_t)0 )
|
||||
{
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
pos += res;
|
||||
|
||||
if( c == L'\n' )
|
||||
{
|
||||
if( timestamp_mode )
|
||||
{
|
||||
const wchar_t *time_string = output.c_str();
|
||||
while( *time_string && !iswdigit(*time_string))
|
||||
time_string++;
|
||||
errno=0;
|
||||
|
||||
if( *time_string )
|
||||
{
|
||||
time_t tm;
|
||||
wchar_t *end;
|
||||
|
||||
errno = 0;
|
||||
tm = (time_t)wcstol( time_string, &end, 10 );
|
||||
|
||||
if( tm && !errno && !*end )
|
||||
{
|
||||
timestamp = tm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
output.clear();
|
||||
timestamp_mode = 0;
|
||||
continue;
|
||||
}
|
||||
if( !was_backslash )
|
||||
break;
|
||||
}
|
||||
|
||||
if( first_char )
|
||||
{
|
||||
if( c == L'#' )
|
||||
timestamp_mode = 1;
|
||||
}
|
||||
|
||||
first_char = 0;
|
||||
|
||||
output.push_back(c);
|
||||
|
||||
was_backslash = ( (c == L'\\') && !was_backslash);
|
||||
|
||||
}
|
||||
|
||||
unescape_newlines(output);
|
||||
return history_item_t(output, timestamp);
|
||||
}
|
||||
|
||||
void history_t::populate_from_mmap(void)
|
||||
{
|
||||
const char *begin = mmap_start;
|
||||
const char *end = begin + mmap_length;
|
||||
const char *pos;
|
||||
|
||||
int ignore_newline = 0;
|
||||
int do_push = 1;
|
||||
|
||||
for( pos = begin; pos <end; pos++ )
|
||||
{
|
||||
|
||||
if( do_push )
|
||||
{
|
||||
ignore_newline = *pos == '#';
|
||||
/* Need to unique-ize */
|
||||
old_item_offsets.push_back(pos - begin);
|
||||
do_push = 0;
|
||||
}
|
||||
|
||||
switch( *pos )
|
||||
{
|
||||
case '\\':
|
||||
{
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '\n':
|
||||
{
|
||||
if( ignore_newline )
|
||||
{
|
||||
ignore_newline = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_push = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void history_t::load_old_if_needed(void)
|
||||
{
|
||||
if (loaded_old) return;
|
||||
loaded_old = true;
|
||||
|
||||
int fd;
|
||||
int ok=0;
|
||||
|
||||
signal_block();
|
||||
wcstring filename = history_filename(name, L"");
|
||||
|
||||
if( ! filename.empty() )
|
||||
{
|
||||
if( ( fd = wopen( filename.c_str(), O_RDONLY ) ) > 0 )
|
||||
{
|
||||
off_t len = lseek( fd, 0, SEEK_END );
|
||||
if( len != (off_t)-1)
|
||||
{
|
||||
mmap_length = (size_t)len;
|
||||
if( lseek( fd, 0, SEEK_SET ) == 0 )
|
||||
{
|
||||
if( (mmap_start = (char *)mmap( 0, mmap_length, PROT_READ, MAP_PRIVATE, fd, 0 )) != MAP_FAILED )
|
||||
{
|
||||
ok = 1;
|
||||
this->populate_from_mmap();
|
||||
}
|
||||
}
|
||||
}
|
||||
close( fd );
|
||||
}
|
||||
}
|
||||
signal_unblock();
|
||||
}
|
||||
|
||||
bool history_search_t::go_forwards() {
|
||||
/* Forwards means reducing our index. */
|
||||
if (idx == 0)
|
||||
return false;
|
||||
|
||||
size_t i;
|
||||
for (i=idx-1; i > 0; i--) {
|
||||
const history_item_t item = history->item_at_index(i);
|
||||
/* Skip if it's empty. Empty items only occur at the end of the history. */
|
||||
if (item.empty())
|
||||
continue;
|
||||
|
||||
/* Look for term in item.data */
|
||||
if (item.matches_search(term)) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
bool history_search_t::go_backwards() {
|
||||
/* Backwards means increasing our index */
|
||||
const size_t max_idx = (size_t)(-1);
|
||||
if (idx == max_idx)
|
||||
return false;
|
||||
|
||||
size_t i;
|
||||
for (i=idx+1;; i++) {
|
||||
/* We're done if we reach the largest index */
|
||||
if (i == max_idx)
|
||||
return false;
|
||||
|
||||
const history_item_t item = history->item_at_index(i);
|
||||
/* We're done if it's empty */
|
||||
if (item.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Look for term in item.data */
|
||||
if (item.matches_search(term)) {
|
||||
idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Goes to the end (forwards) */
|
||||
void history_search_t::go_to_end(void) {
|
||||
idx = 0;
|
||||
}
|
||||
|
||||
/** Goes to the beginning (backwards) */
|
||||
void history_search_t::go_to_beginning(void) {
|
||||
idx = (size_t)(-1);
|
||||
}
|
||||
|
||||
|
||||
history_item_t history_search_t::current_item() const {
|
||||
assert(idx > 0 && idx < (size_t)(-1));
|
||||
return history->item_at_index(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
Interval in seconds between automatic history save
|
||||
|
@ -125,13 +445,8 @@ typedef struct
|
|||
Original creation time for the entry
|
||||
*/
|
||||
time_t timestamp;
|
||||
}
|
||||
item_t;
|
||||
} item_t;
|
||||
|
||||
/**
|
||||
Table of all history modes
|
||||
*/
|
||||
static hash_table_t *mode_table=0;
|
||||
|
||||
/**
|
||||
The surrent history mode
|
||||
|
@ -214,6 +529,19 @@ static wchar_t *history_escape_newlines( wchar_t *in )
|
|||
return (wchar_t *)out->buff;
|
||||
}
|
||||
|
||||
static void unescape_newlines(wcstring &str)
|
||||
{
|
||||
/* Replace instances of backslash + newline with just the newline */
|
||||
const wchar_t *needle = L"\\\n", *replacement = L"\n";
|
||||
size_t needle_len = wcslen(needle);
|
||||
size_t offset = 0;
|
||||
while((offset = str.find(needle, offset)) != wcstring::npos)
|
||||
{
|
||||
str.replace(offset, needle_len, replacement);
|
||||
offset += needle_len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Remove backslashes from all newlines. This makes a string from the
|
||||
history file better formated for on screen display. The memory for
|
||||
|
@ -479,6 +807,20 @@ static wchar_t *history_filename( void *context, const wchar_t *name, const wcha
|
|||
return res;
|
||||
}
|
||||
|
||||
static wcstring history_filename(const wcstring &name, const wcstring &suffix)
|
||||
{
|
||||
wcstring path;
|
||||
if (! path_get_config(path))
|
||||
return L"";
|
||||
|
||||
wcstring result = path;
|
||||
result.append(L"/");
|
||||
result.append(name);
|
||||
result.append(L"_history");
|
||||
result.append(suffix);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Go through the mmaped region and insert pointers to suitable loacations into the item list
|
||||
*/
|
||||
|
@ -607,6 +949,131 @@ static void history_load( history_mode_t *m )
|
|||
signal_unblock();
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
Save the specified mode to file
|
||||
*/
|
||||
void history_t::save( void *n, history_mode_t *m )
|
||||
{
|
||||
scoped_lock locker(lock);
|
||||
|
||||
/* Nothing to do if there's no new items */
|
||||
if (new_items.empty())
|
||||
return;
|
||||
|
||||
FILE *out;
|
||||
int i;
|
||||
int has_new=0;
|
||||
wchar_t *tmp_name;
|
||||
|
||||
int ok = 1;
|
||||
|
||||
signal_block();
|
||||
|
||||
wcstring tmp_name = history_filename(name, L".tmp");
|
||||
|
||||
if( ! tmp_name.empty() )
|
||||
{
|
||||
if( (out=wfopen( tmp_name, "w" ) ) )
|
||||
{
|
||||
hash_table_t mine;
|
||||
|
||||
hash_init( &mine, &hash_item_func, &hash_item_cmp );
|
||||
|
||||
for( i=0; i<al_get_count(&m->item); i++ )
|
||||
{
|
||||
void *ptr = al_get( &m->item, i );
|
||||
int is_new = item_is_new( m, ptr );
|
||||
if( is_new )
|
||||
{
|
||||
hash_put( &mine, item_get( m, ptr ), L"" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Re-save the old history
|
||||
*/
|
||||
for( i=0; ok && (i<al_get_count(&on_disk->item)); i++ )
|
||||
{
|
||||
void *ptr = al_get( &on_disk->item, i );
|
||||
item_t *i = item_get( on_disk, ptr );
|
||||
if( !hash_get( &mine, i ) )
|
||||
{
|
||||
if( item_write( out, on_disk, ptr ) == -1 )
|
||||
{
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hash_destroy( &mine );
|
||||
|
||||
/*
|
||||
Add our own items last
|
||||
*/
|
||||
for( i=0; ok && (i<al_get_count(&m->item)); i++ )
|
||||
{
|
||||
void *ptr = al_get( &m->item, i );
|
||||
int is_new = item_is_new( m, ptr );
|
||||
if( is_new )
|
||||
{
|
||||
if( item_write( out, m, ptr ) == -1 )
|
||||
{
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( fclose( out ) || !ok )
|
||||
{
|
||||
/*
|
||||
This message does not have high enough priority to
|
||||
be shown by default.
|
||||
*/
|
||||
debug( 2, L"Error when writing history file" );
|
||||
}
|
||||
else
|
||||
{
|
||||
wrename( tmp_name, history_filename( on_disk, m->name, 0 ) );
|
||||
}
|
||||
}
|
||||
free( tmp_name );
|
||||
}
|
||||
|
||||
halloc_free( on_disk);
|
||||
|
||||
if( ok )
|
||||
{
|
||||
|
||||
/*
|
||||
Reset the history. The item_t entries created in this session
|
||||
are not lost or dropped, they are stored in the session_item
|
||||
hash table. On reload, they will be automatically inserted at
|
||||
the end of the history list.
|
||||
*/
|
||||
|
||||
if( m->mmap_start && (m->mmap_start != MAP_FAILED ) )
|
||||
{
|
||||
munmap( m->mmap_start, m->mmap_length );
|
||||
}
|
||||
|
||||
al_truncate( &m->item, 0 );
|
||||
al_truncate( &m->used, 0 );
|
||||
m->pos = 0;
|
||||
m->has_loaded = 0;
|
||||
m->mmap_start=0;
|
||||
m->mmap_length=0;
|
||||
|
||||
m->save_timestamp=time(0);
|
||||
m->new_count = 0;
|
||||
}
|
||||
|
||||
signal_unblock();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Save the specified mode to file
|
||||
*/
|
||||
|
@ -951,23 +1418,6 @@ const wchar_t *history_next_match( const wchar_t *needle)
|
|||
}
|
||||
|
||||
|
||||
void history_set_mode( const wchar_t *name )
|
||||
{
|
||||
if( !mode_table )
|
||||
{
|
||||
mode_table = (hash_table_t *)malloc( sizeof(hash_table_t ));
|
||||
hash_init( mode_table, &hash_wcs_func, &hash_wcs_cmp );
|
||||
}
|
||||
|
||||
current_mode = (history_mode_t *)hash_get( mode_table, name );
|
||||
|
||||
if( !current_mode )
|
||||
{
|
||||
current_mode = history_create_mode( name );
|
||||
hash_put( mode_table, name, current_mode );
|
||||
}
|
||||
}
|
||||
|
||||
void history_init()
|
||||
{
|
||||
}
|
||||
|
|
179
history.h
179
history.h
|
@ -6,6 +6,137 @@
|
|||
#define FISH_HISTORY_H
|
||||
|
||||
#include <wchar.h>
|
||||
#include "common.h"
|
||||
#include "pthread.h"
|
||||
#include <vector>
|
||||
#include <tr1/memory>
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
class history_item_t {
|
||||
friend class history_t;
|
||||
|
||||
private:
|
||||
history_item_t(const wcstring &);
|
||||
history_item_t(const wcstring &, time_t);
|
||||
|
||||
/** The actual contents of the entry */
|
||||
wcstring contents;
|
||||
|
||||
/** Original creation time for the entry */
|
||||
time_t timestamp;
|
||||
|
||||
public:
|
||||
const wcstring &str() const { return contents; }
|
||||
bool empty() const { return contents.empty(); }
|
||||
bool matches_search(const wcstring &val) const { return contents.find(val) != wcstring::npos; }
|
||||
};
|
||||
|
||||
class history_t {
|
||||
private:
|
||||
/** No copying */
|
||||
history_t(const history_t&);
|
||||
history_t &operator=(const history_t&);
|
||||
|
||||
/** Private creator */
|
||||
history_t(const wcstring &pname);
|
||||
|
||||
/** Destructor */
|
||||
~history_t();
|
||||
|
||||
/** Lock for thread safety */
|
||||
pthread_mutex_t lock;
|
||||
|
||||
/** The name of this list. Used for picking a suitable filename and for switching modes. */
|
||||
const wcstring name;
|
||||
|
||||
/** New items. */
|
||||
std::vector<history_item_t> new_items;
|
||||
|
||||
/** The mmaped region for the history file */
|
||||
const char *mmap_start;
|
||||
|
||||
/** The size of the mmaped region */
|
||||
size_t mmap_length;
|
||||
|
||||
/**
|
||||
Timestamp of last save
|
||||
*/
|
||||
time_t save_timestamp;
|
||||
|
||||
static history_item_t decode_item(const char *ptr, size_t len);
|
||||
|
||||
void populate_from_mmap(void);
|
||||
|
||||
/** List of old items, as offsets into out mmap data */
|
||||
std::vector<size_t> old_item_offsets;
|
||||
|
||||
/** Whether we've loaded old items */
|
||||
bool loaded_old;
|
||||
|
||||
/** Loads old if necessary */
|
||||
void load_old_if_needed(void);
|
||||
|
||||
public:
|
||||
/** Returns history with the given name, creating it if necessary */
|
||||
static history_t & history_with_name(const wcstring &name);
|
||||
|
||||
/** Add a new history item to the end */
|
||||
void add(const wcstring &str);
|
||||
|
||||
/** Saves history */
|
||||
void save();
|
||||
|
||||
/** Return the specified history at the specified index. 0 is the index of the current commandline. */
|
||||
history_item_t item_at_index(size_t idx);
|
||||
};
|
||||
|
||||
class history_search_t {
|
||||
|
||||
/** The history in which we are searching */
|
||||
history_t * history;
|
||||
|
||||
/** The search term */
|
||||
wcstring term;
|
||||
|
||||
/** Current index into the history */
|
||||
size_t idx;
|
||||
|
||||
public:
|
||||
|
||||
/** Finds the next search term (forwards in time). Returns true if one was found. */
|
||||
bool go_forwards(void);
|
||||
|
||||
/** Finds the previous search result (backwards in time). Returns true if one was found. */
|
||||
bool go_backwards(void);
|
||||
|
||||
/** Goes to the end (forwards) */
|
||||
void go_to_end(void);
|
||||
|
||||
/** Goes to the beginning (backwards) */
|
||||
void go_to_beginning(void);
|
||||
|
||||
/** Returns the current search result. asserts if there is no current item. */
|
||||
history_item_t current_item(void) const;
|
||||
|
||||
/** Constructor */
|
||||
history_search_t(history_t &hist, const wcstring &str) :
|
||||
history(&hist),
|
||||
term(str),
|
||||
idx()
|
||||
{
|
||||
}
|
||||
|
||||
/* Default constructor */
|
||||
history_search_t() :
|
||||
history(),
|
||||
term(),
|
||||
idx()
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Init history library. The history file won't actually be loaded
|
||||
|
@ -14,56 +145,10 @@
|
|||
void history_init();
|
||||
|
||||
/**
|
||||
Saves the new history to disc and frees all memory used by the history.
|
||||
Saves the new history to disc.
|
||||
*/
|
||||
void history_destroy();
|
||||
|
||||
/**
|
||||
Add a new history item to the bottom of the history, containing a
|
||||
copy of str. Remove any duplicates. Moves the current item past the
|
||||
end of the history list.
|
||||
*/
|
||||
void history_add( const wchar_t *str );
|
||||
|
||||
/**
|
||||
Find previous history item starting with str. If this moves before
|
||||
the start of the history, str is returned.
|
||||
*/
|
||||
const wchar_t *history_prev_match( const wchar_t *str );
|
||||
|
||||
/**
|
||||
Return the specified history at the specified index, or 0 if out of bounds. 0 is the index of the current commandline.
|
||||
*/
|
||||
wchar_t *history_get( int idx );
|
||||
|
||||
|
||||
/**
|
||||
Move to first history item
|
||||
*/
|
||||
void history_first();
|
||||
|
||||
/**
|
||||
Make current point to last history item
|
||||
*/
|
||||
void history_reset();
|
||||
|
||||
|
||||
/**
|
||||
Find next history item starting with str. If this moves past
|
||||
the end of the history, str is returned.
|
||||
*/
|
||||
const wchar_t *history_next_match( const wchar_t *str);
|
||||
|
||||
|
||||
/**
|
||||
Set the current mode name for history. Each application that uses
|
||||
the history has it's own mode. This must be called prior to any use
|
||||
of the history.
|
||||
*/
|
||||
|
||||
void history_set_mode( const wchar_t *name );
|
||||
|
||||
|
||||
/**
|
||||
Perform sanity checks
|
||||
*/
|
||||
|
|
|
@ -806,7 +806,7 @@ int parser_t::eval_args( const wchar_t *line, std::vector<completion_t> &args )
|
|||
DIE_MEM();
|
||||
}
|
||||
|
||||
if( expand_string2( tmp, args, 0 ) == EXPAND_ERROR )
|
||||
if( expand_string( tmp, args, 0 ) == EXPAND_ERROR )
|
||||
{
|
||||
err_pos=tok_get_pos( &tok );
|
||||
do_loop=0;
|
||||
|
@ -1344,7 +1344,7 @@ void parser_t::parse_job_argument_list( process_t *p,
|
|||
p->count_help_magic = 1;
|
||||
}
|
||||
|
||||
switch( expand_string2( tok_last( tok ), args, 0 ) )
|
||||
switch( expand_string( tok_last( tok ), args, 0 ) )
|
||||
{
|
||||
case EXPAND_ERROR:
|
||||
{
|
||||
|
|
39
path.cpp
39
path.cpp
|
@ -471,8 +471,47 @@ wchar_t *path_get_config( void *context)
|
|||
{
|
||||
debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." ));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool path_get_config(wcstring &path)
|
||||
{
|
||||
int done = 0;
|
||||
wcstring res;
|
||||
|
||||
const env_var_t xdg_dir = env_get_string( L"XDG_CONFIG_HOME" );
|
||||
if( ! xdg_dir.missing() )
|
||||
{
|
||||
res = xdg_dir + L"/fish";
|
||||
if( !create_directory( res.c_str() ) )
|
||||
{
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const env_var_t home = env_get_string( L"HOME" );
|
||||
if( ! home.missing() )
|
||||
{
|
||||
res = home + L"/.config/fish";
|
||||
if( !create_directory( res.c_str() ) )
|
||||
{
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( done )
|
||||
{
|
||||
path = res;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug( 0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access." ));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
wchar_t *path_make_canonical( void *context, const wchar_t *path )
|
||||
|
|
1
path.h
1
path.h
|
@ -22,6 +22,7 @@
|
|||
\return 0 if the no configuration directory can be located or created, the directory path otherwise.
|
||||
*/
|
||||
wchar_t *path_get_config( void *context);
|
||||
bool path_get_config(wcstring &path);
|
||||
|
||||
/**
|
||||
Finds the full path of an executable in a newly allocated string.
|
||||
|
|
71
reader.cpp
71
reader.cpp
|
@ -193,12 +193,18 @@ class reader_data_t
|
|||
The representation of the current screen contents
|
||||
*/
|
||||
screen_t screen;
|
||||
|
||||
/** The history */
|
||||
history_t *history;
|
||||
|
||||
/**
|
||||
String containing the current search item
|
||||
*/
|
||||
wcstring search_buff;
|
||||
|
||||
/* History search */
|
||||
history_search_t history_search;
|
||||
|
||||
/**
|
||||
Saved position used by token history search
|
||||
*/
|
||||
|
@ -1867,8 +1873,6 @@ static void handle_token_history( int forward, int reset )
|
|||
{
|
||||
if( current_pos == -1 )
|
||||
{
|
||||
const wchar_t *item;
|
||||
|
||||
/*
|
||||
Move to previous line
|
||||
*/
|
||||
|
@ -1877,19 +1881,13 @@ static void handle_token_history( int forward, int reset )
|
|||
/*
|
||||
Search for previous item that contains this substring
|
||||
*/
|
||||
item = history_prev_match(data->search_buff.c_str());
|
||||
|
||||
/*
|
||||
If there is no match, the original string is returned
|
||||
|
||||
If so, we clear the match string to avoid infinite loop
|
||||
*/
|
||||
if( wcscmp( item, data->search_buff.c_str() ) == 0 )
|
||||
{
|
||||
item=L"";
|
||||
}
|
||||
|
||||
data->token_history_buff = wcsdup( item );
|
||||
if (! data->history_search.go_backwards()) {
|
||||
/* No luck */
|
||||
data->token_history_buff = wcsdup(L"");
|
||||
} else {
|
||||
history_item_t item = data->history_search.current_item();
|
||||
data->token_history_buff = wcsdup(item.str().c_str());
|
||||
}
|
||||
current_pos = wcslen(data->token_history_buff);
|
||||
|
||||
}
|
||||
|
@ -2120,6 +2118,11 @@ wchar_t *reader_get_buffer(void)
|
|||
return data?data->buff:NULL;
|
||||
}
|
||||
|
||||
history_t *reader_get_history(void) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
return data ? data->history : NULL;
|
||||
}
|
||||
|
||||
void reader_set_buffer( const wchar_t *b, int p )
|
||||
{
|
||||
int l = wcslen( b );
|
||||
|
@ -2144,7 +2147,7 @@ void reader_set_buffer( const wchar_t *b, int p )
|
|||
|
||||
data->search_mode = NO_SEARCH;
|
||||
data->search_buff.clear();
|
||||
history_reset();
|
||||
data->history_search.go_to_end();
|
||||
|
||||
reader_super_highlight_me_plenty( data->buff_pos,
|
||||
0 );
|
||||
|
@ -2264,6 +2267,7 @@ void reader_push( const wchar_t *name )
|
|||
reader_data_t zerod = {};
|
||||
reader_data_t *n = new reader_data_t(zerod);
|
||||
|
||||
n->history = & history_t::history_with_name(name);
|
||||
n->app_name = name;
|
||||
n->next = data;
|
||||
sb_init( &n->kill_item );
|
||||
|
@ -2282,7 +2286,7 @@ void reader_push( const wchar_t *name )
|
|||
reader_set_highlight_function( &highlight_universal );
|
||||
reader_set_test_function( &default_test );
|
||||
reader_set_prompt( L"" );
|
||||
history_set_mode( name );
|
||||
//history_set_mode( name );
|
||||
|
||||
data->token_history_buff=0;
|
||||
}
|
||||
|
@ -2320,7 +2324,7 @@ void reader_pop()
|
|||
else
|
||||
{
|
||||
end_loop = 0;
|
||||
history_set_mode( data->app_name.c_str() );
|
||||
//history_set_mode( data->app_name.c_str() );
|
||||
s_reset( &data->screen, 1 );
|
||||
}
|
||||
}
|
||||
|
@ -2417,6 +2421,7 @@ static void highlight_search(void) {
|
|||
}
|
||||
|
||||
static void highlight_complete(void *ctx_ptr, int result) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
background_highlight_context_t *ctx = (background_highlight_context_t *)ctx_ptr;
|
||||
if (ctx->buff == data->buff) {
|
||||
/* The data hasn't changed, so swap in our colors */
|
||||
|
@ -2554,7 +2559,7 @@ static int read_i()
|
|||
event_fire_generic(L"fish_prompt");
|
||||
|
||||
reader_push(L"fish");
|
||||
reader_set_complete_function( &complete2 );
|
||||
reader_set_complete_function( &complete );
|
||||
reader_set_highlight_function( &highlight_shell );
|
||||
reader_set_test_function( &reader_shell_test );
|
||||
parser_t &parser = parser_t::principal_parser();
|
||||
|
@ -2980,7 +2985,8 @@ wchar_t *reader_readline()
|
|||
|
||||
if( data->token_history_pos==-1 )
|
||||
{
|
||||
history_reset();
|
||||
//history_reset();
|
||||
data->history_search.go_to_end();
|
||||
reader_set_buffer( data->search_buff.c_str(), data->search_buff.size() );
|
||||
}
|
||||
else
|
||||
|
@ -3045,7 +3051,9 @@ wchar_t *reader_readline()
|
|||
if( wcslen( data->buff ) )
|
||||
{
|
||||
// wcscpy(data->search_buff,L"");
|
||||
history_add( data->buff );
|
||||
//history_add( data->buff );
|
||||
if (data->history)
|
||||
data->history->add(data->buff);
|
||||
}
|
||||
finished=1;
|
||||
data->buff_pos=data->buff_len;
|
||||
|
@ -3101,6 +3109,7 @@ wchar_t *reader_readline()
|
|||
}
|
||||
|
||||
data->search_buff.append(data->buff);
|
||||
data->history_search = history_search_t(*data->history, data->search_buff);
|
||||
}
|
||||
|
||||
switch( data->search_mode )
|
||||
|
@ -3108,19 +3117,25 @@ wchar_t *reader_readline()
|
|||
|
||||
case LINE_SEARCH:
|
||||
{
|
||||
const wchar_t *it = 0;
|
||||
bool success;
|
||||
|
||||
if( ( c == R_HISTORY_SEARCH_BACKWARD ) ||
|
||||
( c == R_HISTORY_TOKEN_SEARCH_BACKWARD ) )
|
||||
{
|
||||
it = history_prev_match(data->search_buff.c_str());
|
||||
success = data->history_search.go_backwards();
|
||||
}
|
||||
else
|
||||
{
|
||||
it = history_next_match(data->search_buff.c_str());
|
||||
success = data->history_search.go_forwards();
|
||||
}
|
||||
|
||||
handle_history( it );
|
||||
wcstring new_text;
|
||||
if (success) {
|
||||
new_text = data->history_search.current_item().str();
|
||||
} else {
|
||||
new_text = data->search_buff;
|
||||
}
|
||||
handle_history( new_text.c_str() );
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -3197,13 +3212,13 @@ wchar_t *reader_readline()
|
|||
|
||||
case R_BEGINNING_OF_HISTORY:
|
||||
{
|
||||
history_first();
|
||||
data->history_search.go_to_beginning();
|
||||
break;
|
||||
}
|
||||
|
||||
case R_END_OF_HISTORY:
|
||||
{
|
||||
history_reset();
|
||||
data->history_search.go_to_end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3283,7 +3298,7 @@ wchar_t *reader_readline()
|
|||
{
|
||||
data->search_mode = NO_SEARCH;
|
||||
data->search_buff.clear();
|
||||
history_reset();
|
||||
data->history_search.go_to_end();
|
||||
data->token_history_pos=-1;
|
||||
}
|
||||
|
||||
|
|
4
reader.h
4
reader.h
|
@ -17,6 +17,7 @@
|
|||
|
||||
class parser_t;
|
||||
class completion_t;
|
||||
class history_t;
|
||||
|
||||
/**
|
||||
Read commands from \c fd until encountering EOF
|
||||
|
@ -84,6 +85,9 @@ void reader_run_command( const wchar_t *buff );
|
|||
*/
|
||||
wchar_t *reader_get_buffer();
|
||||
|
||||
/** Returns the current reader's history */
|
||||
history_t *reader_get_history(void);
|
||||
|
||||
/**
|
||||
Set the string of characters in the command buffer, as well as the cursor position.
|
||||
|
||||
|
|
Loading…
Reference in a new issue