From 47019e315ac9c4b20880fdaa403ec4ca04399d14 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 5 May 2012 13:34:09 -0700 Subject: [PATCH] Some work towards refactoring how completions are inserted to support escaping in autosuggestions --- reader.cpp | 212 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 176 insertions(+), 36 deletions(-) diff --git a/reader.cpp b/reader.cpp index cc31808a8..18ecfdca3 100644 --- a/reader.cpp +++ b/reader.cpp @@ -771,7 +771,7 @@ static int insert_string(const wcstring &str) */ static int insert_char( wchar_t c ) { - return insert_string( wcstring(1, c) ); + return insert_string(wcstring(&c, 1)); } @@ -926,6 +926,135 @@ static void get_param( const wchar_t *cmd, } } +/** + Insert the string in the given command line at the given cursor + position. The function checks if the string is quoted or not and + correctly escapes the string. + \param val the string to insert + \param flags A union of all flags describing the completion to insert. See the completion_t struct for more information on possible values. + \param command_line The command line into which we will insert + \param inout_cursor_pos On input, the location of the cursor within the command line. On output, the new desired position. + \return The completed string +*/ +static wcstring completion_insert_helper(const wcstring &val_str, int flags, const wcstring &command_line, size_t *inout_cursor_pos) +{ + wchar_t *replaced; + const wchar_t *val = val_str.c_str(); + bool add_space = !(flags & COMPLETE_NO_SPACE); + bool do_replace = !!(flags & COMPLETE_NO_CASE); + bool do_escape = !(flags & COMPLETE_DONT_ESCAPE); + const size_t cursor_pos = *inout_cursor_pos; + + // debug( 0, L"Insert completion %ls with flags %d", val, flags); + + if( do_replace ) + { + + int move_cursor; + const wchar_t *begin, *end; + wchar_t *escaped; + + const wchar_t *buff = command_line.c_str(); + parse_util_token_extent( buff, cursor_pos, &begin, 0, 0, 0 ); + end = buff + cursor_pos; + + wcstring sb(buff, begin - buff); + + if( do_escape ) + { + escaped = escape( val, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + sb.append( escaped ); + move_cursor = wcslen(escaped); + free( escaped ); + } + else + { + sb.append( val ); + move_cursor = wcslen(val); + } + + + if( add_space ) + { + sb.append( L" " ); + move_cursor += 1; + } + sb.append( end ); + + size_t new_cursor_pos = (begin - buff) + move_cursor; + *inout_cursor_pos = new_cursor_pos; + return sb; + } + else + { + wchar_t quote = L'\0'; + if( do_escape ) + { + get_param(command_line.c_str(), cursor_pos, "e, 0, 0, 0); + if( quote == L'\0' ) + { + replaced = escape( val, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + } + else + { + bool unescapable = false; + + const wchar_t *pin; + wchar_t *pout; + + replaced = pout = (wchar_t *)malloc( sizeof(wchar_t)*(wcslen(val) + 1) ); + + for( pin=val; *pin; pin++ ) + { + switch( *pin ) + { + case L'\n': + case L'\t': + case L'\b': + case L'\r': + unescapable = true; + break; + default: + *pout++ = *pin; + break; + } + } + + if (unescapable) + { + free( replaced ); + wchar_t *tmp = escape( val, ESCAPE_ALL | ESCAPE_NO_QUOTED ); + replaced = wcsdupcat( L" ", tmp ); + free( tmp); + replaced[0]=quote; + } + else + *pout = 0; + } + } + else + { + replaced = wcsdup(val); + } + + wcstring result = command_line; + result.insert(cursor_pos, replaced); + size_t new_cursor_pos = cursor_pos + wcslen(replaced); + if (add_space) + { + if (quote && (command_line.c_str()[cursor_pos] != quote)) + { + /* This is a quoted parameter, first print a quote */ + result.insert(new_cursor_pos++, wcstring("e, 1)); + } + result.insert(new_cursor_pos++, L" "); + } + free(replaced); + *inout_cursor_pos = new_cursor_pos; + return result; + } +} + /** Insert the string at the current cursor position. The function checks if the string is quoted or not and correctly escapes the @@ -936,6 +1065,16 @@ static void get_param( const wchar_t *cmd, */ static void completion_insert( const wchar_t *val, int flags ) +{ + size_t cursor = data->buff_pos; + wcstring new_command_line = completion_insert_helper(val, flags, data->command_line, &cursor); + reader_set_buffer(new_command_line, cursor); + + /* Since we just inserted a completion, don't immediately do a new autosuggestion */ + data->suppress_autosuggestion = true; +} + +static void completion_insert_old( const wchar_t *val, int flags ) { assert(data != NULL); @@ -1070,8 +1209,7 @@ static void completion_insert( const wchar_t *val, int flags ) free(replaced); - } - + } } /** @@ -1224,6 +1362,7 @@ static void run_pager( wchar_t *prefix, int is_quoted, const std::vectorcursor_pos == search_string.size()); + if (line_ends_with_space && ! cursor_at_end) + return 0; + /* Try normal completions */ std::vector completions; complete(search_string, completions, COMPLETE_AUTOSUGGEST, &this->commands_to_load); @@ -1372,7 +1526,7 @@ static void update_autosuggestion(void) { #else data->autosuggestion.clear(); if (! data->suppress_autosuggestion && ! data->command_line.empty() && data->history_search.is_at_end()) { - autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, data->command_line); + autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, data->command_line, data->buff_pos); iothread_perform(threaded_autosuggest, autosuggest_completed, ctx); } #endif @@ -1456,11 +1610,11 @@ static int reader_can_replace( const wcstring &in, int flags ) */ -static int handle_completions( std::vector &comp ) +static int handle_completions( const std::vector &comp ) { - wchar_t *base = 0; + wchar_t *base = NULL; int len = 0; - int done = 0; + bool done = false; int count = 0; int flags=0; const wchar_t *begin, *end, *buff = data->command_line.c_str(); @@ -1473,21 +1627,17 @@ static int handle_completions( std::vector &comp ) /* Check trivial cases */ - switch( comp.size() ) + switch(comp.size()) { - /* - No suitable completions found, flash screen and retur - */ + /* No suitable completions found, flash screen and return */ case 0: { reader_flash(); - done = 1; + done = true; break; } - /* - Exactly one suitable completion found - insert it - */ + /* Exactly one suitable completion found - insert it */ case 1: { @@ -1503,8 +1653,8 @@ static int handle_completions( std::vector &comp ) { completion_insert( c.completion.c_str(), c.flags ); } - done = 1; - len = 1; + done = true; + len = 1; // I think this just means it's a true return break; } } @@ -1512,17 +1662,12 @@ static int handle_completions( std::vector &comp ) if( !done ) { - /* - Try to find something to insert whith the correct case - */ + /* Try to find something to insert whith the correct case */ for( size_t i=0; i< comp.size() ; i++ ) { const completion_t &c = comp.at( i ); - int new_len; - /* - Ignore case insensitive completions for now - */ + /* Ignore case insensitive completions for now */ if( c.flags & COMPLETE_NO_CASE ) continue; @@ -1530,8 +1675,8 @@ static int handle_completions( std::vector &comp ) if( base ) { - new_len = comp_len( base, c.completion.c_str() ); - len = new_len < len ? new_len: len; + int new_len = comp_len( base, c.completion.c_str() ); + len = mini(new_len, len); } else { @@ -1541,9 +1686,7 @@ static int handle_completions( std::vector &comp ) } } - /* - If we found something to insert, do it. - */ + /* If we found something to insert, do it. */ if( len > 0 ) { if( count > 1 ) @@ -1551,18 +1694,15 @@ static int handle_completions( std::vector &comp ) base[len]=L'\0'; completion_insert(base, flags); - done = 1; + done = true; } } - if( !done && base == 0 ) + if( !done && base == NULL ) { - /* - Try to find something to insert ignoring case - */ - + /* Try to find something to insert ignoring case */ if( begin ) {