diff --git a/include/replxx.h b/include/replxx.h index 30152f3..5127ac2 100644 --- a/include/replxx.h +++ b/include/replxx.h @@ -141,6 +141,7 @@ enum { REPLXX_KEY_ENTER = REPLXX_KEY_CONTROL( 'M' ) }; */ typedef enum { REPLXX_ACTION_INSERT_CHARACTER, + REPLXX_ACTION_NEW_LINE, REPLXX_ACTION_DELETE_CHARACTER_UNDER_CURSOR, REPLXX_ACTION_DELETE_CHARACTER_LEFT_OF_CURSOR, REPLXX_ACTION_KILL_TO_END_OF_LINE, diff --git a/include/replxx.hxx b/include/replxx.hxx index 1a62d99..5362312 100644 --- a/include/replxx.hxx +++ b/include/replxx.hxx @@ -150,6 +150,7 @@ public: */ enum class ACTION { INSERT_CHARACTER, + NEW_LINE, DELETE_CHARACTER_UNDER_CURSOR, DELETE_CHARACTER_LEFT_OF_CURSOR, KILL_TO_END_OF_LINE, diff --git a/src/replxx_impl.cxx b/src/replxx_impl.cxx index 12ec683..0c60ccf 100644 --- a/src/replxx_impl.cxx +++ b/src/replxx_impl.cxx @@ -43,6 +43,7 @@ namespace { namespace action_names { char const INSERT_CHARACTER[] = "insert_character"; +char const NEW_LINE[] = "new_line"; char const MOVE_CURSOR_TO_BEGINING_OF_LINE[] = "move_cursor_to_begining_of_line"; char const MOVE_CURSOR_TO_END_OF_LINE[] = "move_cursor_to_end_of_line"; char const MOVE_CURSOR_LEFT[] = "move_cursor_left"; @@ -142,11 +143,11 @@ class IOModeGuard { Replxx::ReplxxImpl::ReplxxImpl( FILE*, FILE*, FILE* ) : _utf8Buffer() , _data() + , _pos( 0 ) , _charWidths() , _display() , _displayInputLength( 0 ) , _hint() - , _pos( 0 ) , _prefix( 0 ) , _hintSelection( -1 ) , _history() @@ -190,6 +191,7 @@ Replxx::ReplxxImpl::ReplxxImpl( FILE*, FILE*, FILE* ) , _mutex() { using namespace std::placeholders; _namedActions[action_names::INSERT_CHARACTER] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::INSERT_CHARACTER, _1 ); + _namedActions[action_names::NEW_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::NEW_LINE, _1 ); _namedActions[action_names::MOVE_CURSOR_TO_BEGINING_OF_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::MOVE_CURSOR_TO_BEGINING_OF_LINE, _1 ); _namedActions[action_names::MOVE_CURSOR_TO_END_OF_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::MOVE_CURSOR_TO_END_OF_LINE, _1 ); _namedActions[action_names::MOVE_CURSOR_LEFT] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::MOVE_CURSOR_LEFT, _1 ); @@ -280,7 +282,7 @@ Replxx::ReplxxImpl::ReplxxImpl( FILE*, FILE*, FILE* ) bind_key( 127, _namedActions.at( action_names::DELETE_CHARACTER_UNDER_CURSOR ) ); bind_key( Replxx::KEY::DELETE + 0, _namedActions.at( action_names::DELETE_CHARACTER_UNDER_CURSOR ) ); bind_key( Replxx::KEY::BACKSPACE + 0, _namedActions.at( action_names::DELETE_CHARACTER_LEFT_OF_CURSOR ) ); - bind_key( Replxx::KEY::control( 'J' ), _namedActions.at( action_names::COMMIT_LINE ) ); + bind_key( Replxx::KEY::control( 'J' ), _namedActions.at( action_names::NEW_LINE ) ); bind_key( Replxx::KEY::ENTER + 0, _namedActions.at( action_names::COMMIT_LINE ) ); bind_key( Replxx::KEY::control( 'L' ), _namedActions.at( action_names::CLEAR_SCREEN ) ); bind_key( Replxx::KEY::control( 'N' ), _namedActions.at( action_names::COMPLETE_NEXT ) ); @@ -314,14 +316,15 @@ Replxx::ReplxxImpl::~ReplxxImpl( void ) { Replxx::ACTION_RESULT Replxx::ReplxxImpl::invoke( Replxx::ACTION action_, char32_t code ) { switch ( action_ ) { case ( Replxx::ACTION::INSERT_CHARACTER ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::insert_character, code ) ); + case ( Replxx::ACTION::NEW_LINE ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::new_line, code ) ); case ( Replxx::ACTION::DELETE_CHARACTER_UNDER_CURSOR ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::delete_character, code ) ); case ( Replxx::ACTION::DELETE_CHARACTER_LEFT_OF_CURSOR ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::backspace_character, code ) ); case ( Replxx::ACTION::KILL_TO_END_OF_LINE ): return ( action( WANT_REFRESH | SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_to_end_of_line, code ) ); case ( Replxx::ACTION::KILL_TO_BEGINING_OF_LINE ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_to_begining_of_line, code ) ); case ( Replxx::ACTION::KILL_TO_END_OF_WORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right, code ) ); case ( Replxx::ACTION::KILL_TO_BEGINING_OF_WORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left, code ) ); - case ( Replxx::ACTION::KILL_TO_END_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right, code ) ); - case ( Replxx::ACTION::KILL_TO_BEGINING_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left, code ) ); + case ( Replxx::ACTION::KILL_TO_END_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right, code ) ); + case ( Replxx::ACTION::KILL_TO_BEGINING_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left, code ) ); case ( Replxx::ACTION::KILL_TO_WHITESPACE_ON_LEFT ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_to_whitespace_to_left, code ) ); case ( Replxx::ACTION::YANK ): return ( action( HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::yank, code ) ); case ( Replxx::ACTION::YANK_CYCLE ): return ( action( HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::yank_cycle, code ) ); @@ -330,8 +333,8 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::invoke( Replxx::ACTION action_, char32 case ( Replxx::ACTION::MOVE_CURSOR_TO_END_OF_LINE ): return ( action( WANT_REFRESH, &Replxx::ReplxxImpl::go_to_end_of_line, code ) ); case ( Replxx::ACTION::MOVE_CURSOR_ONE_WORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left, code ) ); case ( Replxx::ACTION::MOVE_CURSOR_ONE_WORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right, code ) ); - case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left, code ) ); - case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right, code ) ); + case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left, code ) ); + case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right, code ) ); case ( Replxx::ACTION::MOVE_CURSOR_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_char_left, code ) ); case ( Replxx::ACTION::MOVE_CURSOR_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_char_right, code ) ); case ( Replxx::ACTION::HISTORY_NEXT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::history_next, code ) ); @@ -345,9 +348,9 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::invoke( Replxx::ACTION action_, char32 case ( Replxx::ACTION::CAPITALIZE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word, code ) ); case ( Replxx::ACTION::LOWERCASE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word, code ) ); case ( Replxx::ACTION::UPPERCASE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word, code ) ); - case ( Replxx::ACTION::CAPITALIZE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word, code ) ); - case ( Replxx::ACTION::LOWERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word, code ) ); - case ( Replxx::ACTION::UPPERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word, code ) ); + case ( Replxx::ACTION::CAPITALIZE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word, code ) ); + case ( Replxx::ACTION::LOWERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word, code ) ); + case ( Replxx::ACTION::UPPERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word, code ) ); case ( Replxx::ACTION::TRANSPOSE_CHARACTERS ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::transpose_characters, code ) ); case ( Replxx::ACTION::TOGGLE_OVERWRITE_MODE ): return ( action( NOOP, &Replxx::ReplxxImpl::toggle_overwrite_mode, code ) ); #ifndef _WIN32 @@ -674,7 +677,7 @@ void Replxx::ReplxxImpl::render( char32_t ch ) { if ( ch == Replxx::KEY::ESCAPE ) { _display.push_back( '^' ); _display.push_back( '[' ); - } else if ( is_control_code( ch ) ) { + } else if ( is_control_code( ch ) && ( ch != '\n' ) ) { _display.push_back( '^' ); _display.push_back( control_to_human( ch ) ); } else { @@ -1306,7 +1309,7 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::insert_character( char32_t c ) { * beep on unknown Ctrl and/or Meta keys * don't insert control characters */ - if ( ( c >= static_cast( Replxx::KEY::BASE ) ) || is_control_code( c ) ) { + if ( ( c >= static_cast( Replxx::KEY::BASE ) ) || ( is_control_code( c ) && ( c != '\n' ) ) ) { beep(); return ( Replxx::ACTION_RESULT::CONTINUE ); } @@ -1346,6 +1349,11 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::insert_character( char32_t c ) { return ( Replxx::ACTION_RESULT::CONTINUE ); } +// ctrl-J/linefeed/newline +Replxx::ACTION_RESULT Replxx::ReplxxImpl::new_line( char32_t ) { + return ( insert_character( '\n' ) ); +} + // ctrl-A, HOME: move cursor to start of line Replxx::ACTION_RESULT Replxx::ReplxxImpl::go_to_begining_of_line( char32_t ) { _pos = 0; @@ -1654,8 +1662,7 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::backspace_character( char32_t ) { return ( Replxx::ACTION_RESULT::CONTINUE ); } -// ctrl-J/linefeed/newline, accept line -// ctrl-M/return/enter +// ctrl-M/return/enter, accept line Replxx::ACTION_RESULT Replxx::ReplxxImpl::commit_line( char32_t ) { // we need one last refresh with the cursor at the end of the line // so we don't display the next prompt over the previous input line diff --git a/src/replxx_impl.hxx b/src/replxx_impl.hxx index c243015..8c7f8af 100644 --- a/src/replxx_impl.hxx +++ b/src/replxx_impl.hxx @@ -74,6 +74,7 @@ public: } }; typedef std::vector completions_t; + typedef std::vector data_t; typedef std::vector hints_t; typedef std::unique_ptr utf8_buffer_t; typedef std::unique_ptr input_buffer_t; @@ -103,11 +104,11 @@ private: private: mutable Utf8String _utf8Buffer; UnicodeString _data; + int _pos; // character position in buffer ( 0 <= _pos <= _data[_line].length() ) char_widths_t _charWidths; // character widths from mk_wcwidth() display_t _display; int _displayInputLength; UnicodeString _hint; - int _pos; // character position in buffer ( 0 <= _pos <= _len ) int _prefix; // prefix length used in common prefix search int _hintSelection; // Currently selected hint. History _history; @@ -197,6 +198,7 @@ private: int get_input_line( void ); Replxx::ACTION_RESULT action( action_trait_t, key_press_handler_raw_t const&, char32_t ); Replxx::ACTION_RESULT insert_character( char32_t ); + Replxx::ACTION_RESULT new_line( char32_t ); Replxx::ACTION_RESULT go_to_begining_of_line( char32_t ); Replxx::ACTION_RESULT go_to_end_of_line( char32_t ); Replxx::ACTION_RESULT move_one_char_left( char32_t ); diff --git a/src/unicodestring.hxx b/src/unicodestring.hxx index bcc09a0..22f3e46 100644 --- a/src/unicodestring.hxx +++ b/src/unicodestring.hxx @@ -25,6 +25,15 @@ public: assign( src ); } + explicit UnicodeString( UnicodeString const& other, int offset, int len = -1 ) + : _data() { + _data.insert( + _data.end(), + other._data.begin() + offset, + len > 0 ? other._data.begin() + offset + len : other._data.end() + ); + } + explicit UnicodeString( char const* src ) : _data() { assign( src ); diff --git a/tests.py b/tests.py index a2d8ffc..f6ee032 100755 --- a/tests.py +++ b/tests.py @@ -1045,7 +1045,7 @@ def test_history_unique( self_ ): command = ReplxxTests._cSample_ + " u0 q1" ) self_.check_scenario( - rapid( "/history\n/unique\n/history\n" ), + rapid( "/history/unique/history" ), "//history\r\n" " 0: a\r\n" " 1: b\r\n"