Skip to content

Commit 9aa077e

Browse files
Expand model to support multiline mode.
1 parent 2c87a94 commit 9aa077e

6 files changed

Lines changed: 35 additions & 15 deletions

File tree

include/replxx.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ enum { REPLXX_KEY_ENTER = REPLXX_KEY_CONTROL( 'M' ) };
141141
*/
142142
typedef enum {
143143
REPLXX_ACTION_INSERT_CHARACTER,
144+
REPLXX_ACTION_NEW_LINE,
144145
REPLXX_ACTION_DELETE_CHARACTER_UNDER_CURSOR,
145146
REPLXX_ACTION_DELETE_CHARACTER_LEFT_OF_CURSOR,
146147
REPLXX_ACTION_KILL_TO_END_OF_LINE,

include/replxx.hxx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public:
150150
*/
151151
enum class ACTION {
152152
INSERT_CHARACTER,
153+
NEW_LINE,
153154
DELETE_CHARACTER_UNDER_CURSOR,
154155
DELETE_CHARACTER_LEFT_OF_CURSOR,
155156
KILL_TO_END_OF_LINE,

src/replxx_impl.cxx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace {
4343
namespace action_names {
4444

4545
char const INSERT_CHARACTER[] = "insert_character";
46+
char const NEW_LINE[] = "new_line";
4647
char const MOVE_CURSOR_TO_BEGINING_OF_LINE[] = "move_cursor_to_begining_of_line";
4748
char const MOVE_CURSOR_TO_END_OF_LINE[] = "move_cursor_to_end_of_line";
4849
char const MOVE_CURSOR_LEFT[] = "move_cursor_left";
@@ -142,11 +143,11 @@ class IOModeGuard {
142143
Replxx::ReplxxImpl::ReplxxImpl( FILE*, FILE*, FILE* )
143144
: _utf8Buffer()
144145
, _data()
146+
, _pos( 0 )
145147
, _charWidths()
146148
, _display()
147149
, _displayInputLength( 0 )
148150
, _hint()
149-
, _pos( 0 )
150151
, _prefix( 0 )
151152
, _hintSelection( -1 )
152153
, _history()
@@ -190,6 +191,7 @@ Replxx::ReplxxImpl::ReplxxImpl( FILE*, FILE*, FILE* )
190191
, _mutex() {
191192
using namespace std::placeholders;
192193
_namedActions[action_names::INSERT_CHARACTER] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::INSERT_CHARACTER, _1 );
194+
_namedActions[action_names::NEW_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::NEW_LINE, _1 );
193195
_namedActions[action_names::MOVE_CURSOR_TO_BEGINING_OF_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::MOVE_CURSOR_TO_BEGINING_OF_LINE, _1 );
194196
_namedActions[action_names::MOVE_CURSOR_TO_END_OF_LINE] = std::bind( &ReplxxImpl::invoke, this, Replxx::ACTION::MOVE_CURSOR_TO_END_OF_LINE, _1 );
195197
_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* )
280282
bind_key( 127, _namedActions.at( action_names::DELETE_CHARACTER_UNDER_CURSOR ) );
281283
bind_key( Replxx::KEY::DELETE + 0, _namedActions.at( action_names::DELETE_CHARACTER_UNDER_CURSOR ) );
282284
bind_key( Replxx::KEY::BACKSPACE + 0, _namedActions.at( action_names::DELETE_CHARACTER_LEFT_OF_CURSOR ) );
283-
bind_key( Replxx::KEY::control( 'J' ), _namedActions.at( action_names::COMMIT_LINE ) );
285+
bind_key( Replxx::KEY::control( 'J' ), _namedActions.at( action_names::NEW_LINE ) );
284286
bind_key( Replxx::KEY::ENTER + 0, _namedActions.at( action_names::COMMIT_LINE ) );
285287
bind_key( Replxx::KEY::control( 'L' ), _namedActions.at( action_names::CLEAR_SCREEN ) );
286288
bind_key( Replxx::KEY::control( 'N' ), _namedActions.at( action_names::COMPLETE_NEXT ) );
@@ -314,14 +316,15 @@ Replxx::ReplxxImpl::~ReplxxImpl( void ) {
314316
Replxx::ACTION_RESULT Replxx::ReplxxImpl::invoke( Replxx::ACTION action_, char32_t code ) {
315317
switch ( action_ ) {
316318
case ( Replxx::ACTION::INSERT_CHARACTER ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::insert_character, code ) );
319+
case ( Replxx::ACTION::NEW_LINE ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::new_line, code ) );
317320
case ( Replxx::ACTION::DELETE_CHARACTER_UNDER_CURSOR ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::delete_character, code ) );
318321
case ( Replxx::ACTION::DELETE_CHARACTER_LEFT_OF_CURSOR ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::backspace_character, code ) );
319322
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 ) );
320323
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 ) );
321324
case ( Replxx::ACTION::KILL_TO_END_OF_WORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right<false>, code ) );
322325
case ( Replxx::ACTION::KILL_TO_BEGINING_OF_WORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left<false>, code ) );
323-
case ( Replxx::ACTION::KILL_TO_END_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right<true>, code ) );
324-
case ( Replxx::ACTION::KILL_TO_BEGINING_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left<true>, code ) );
326+
case ( Replxx::ACTION::KILL_TO_END_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_right<true>, code ) );
327+
case ( Replxx::ACTION::KILL_TO_BEGINING_OF_SUBWORD ): return ( action( SET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::kill_word_to_left<true>, code ) );
325328
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 ) );
326329
case ( Replxx::ACTION::YANK ): return ( action( HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::yank, code ) );
327330
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
330333
case ( Replxx::ACTION::MOVE_CURSOR_TO_END_OF_LINE ): return ( action( WANT_REFRESH, &Replxx::ReplxxImpl::go_to_end_of_line, code ) );
331334
case ( Replxx::ACTION::MOVE_CURSOR_ONE_WORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left<false>, code ) );
332335
case ( Replxx::ACTION::MOVE_CURSOR_ONE_WORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right<false>, code ) );
333-
case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left<true>, code ) );
334-
case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right<true>, code ) );
336+
case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_left<true>, code ) );
337+
case ( Replxx::ACTION::MOVE_CURSOR_ONE_SUBWORD_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_word_right<true>, code ) );
335338
case ( Replxx::ACTION::MOVE_CURSOR_LEFT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_char_left, code ) );
336339
case ( Replxx::ACTION::MOVE_CURSOR_RIGHT ): return ( action( RESET_KILL_ACTION, &Replxx::ReplxxImpl::move_one_char_right, code ) );
337340
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
345348
case ( Replxx::ACTION::CAPITALIZE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word<false>, code ) );
346349
case ( Replxx::ACTION::LOWERCASE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word<false>, code ) );
347350
case ( Replxx::ACTION::UPPERCASE_WORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word<false>, code ) );
348-
case ( Replxx::ACTION::CAPITALIZE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word<true>, code ) );
349-
case ( Replxx::ACTION::LOWERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word<true>, code ) );
350-
case ( Replxx::ACTION::UPPERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word<true>, code ) );
351+
case ( Replxx::ACTION::CAPITALIZE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::capitalize_word<true>, code ) );
352+
case ( Replxx::ACTION::LOWERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::lowercase_word<true>, code ) );
353+
case ( Replxx::ACTION::UPPERCASE_SUBWORD ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::uppercase_word<true>, code ) );
351354
case ( Replxx::ACTION::TRANSPOSE_CHARACTERS ): return ( action( RESET_KILL_ACTION | HISTORY_RECALL_MOST_RECENT, &Replxx::ReplxxImpl::transpose_characters, code ) );
352355
case ( Replxx::ACTION::TOGGLE_OVERWRITE_MODE ): return ( action( NOOP, &Replxx::ReplxxImpl::toggle_overwrite_mode, code ) );
353356
#ifndef _WIN32
@@ -674,7 +677,7 @@ void Replxx::ReplxxImpl::render( char32_t ch ) {
674677
if ( ch == Replxx::KEY::ESCAPE ) {
675678
_display.push_back( '^' );
676679
_display.push_back( '[' );
677-
} else if ( is_control_code( ch ) ) {
680+
} else if ( is_control_code( ch ) && ( ch != '\n' ) ) {
678681
_display.push_back( '^' );
679682
_display.push_back( control_to_human( ch ) );
680683
} else {
@@ -1306,7 +1309,7 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::insert_character( char32_t c ) {
13061309
* beep on unknown Ctrl and/or Meta keys
13071310
* don't insert control characters
13081311
*/
1309-
if ( ( c >= static_cast<int>( Replxx::KEY::BASE ) ) || is_control_code( c ) ) {
1312+
if ( ( c >= static_cast<int>( Replxx::KEY::BASE ) ) || ( is_control_code( c ) && ( c != '\n' ) ) ) {
13101313
beep();
13111314
return ( Replxx::ACTION_RESULT::CONTINUE );
13121315
}
@@ -1346,6 +1349,11 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::insert_character( char32_t c ) {
13461349
return ( Replxx::ACTION_RESULT::CONTINUE );
13471350
}
13481351

1352+
// ctrl-J/linefeed/newline
1353+
Replxx::ACTION_RESULT Replxx::ReplxxImpl::new_line( char32_t ) {
1354+
return ( insert_character( '\n' ) );
1355+
}
1356+
13491357
// ctrl-A, HOME: move cursor to start of line
13501358
Replxx::ACTION_RESULT Replxx::ReplxxImpl::go_to_begining_of_line( char32_t ) {
13511359
_pos = 0;
@@ -1654,8 +1662,7 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::backspace_character( char32_t ) {
16541662
return ( Replxx::ACTION_RESULT::CONTINUE );
16551663
}
16561664

1657-
// ctrl-J/linefeed/newline, accept line
1658-
// ctrl-M/return/enter
1665+
// ctrl-M/return/enter, accept line
16591666
Replxx::ACTION_RESULT Replxx::ReplxxImpl::commit_line( char32_t ) {
16601667
// we need one last refresh with the cursor at the end of the line
16611668
// so we don't display the next prompt over the previous input line

src/replxx_impl.hxx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public:
7474
}
7575
};
7676
typedef std::vector<Completion> completions_t;
77+
typedef std::vector<UnicodeString> data_t;
7778
typedef std::vector<UnicodeString> hints_t;
7879
typedef std::unique_ptr<char[]> utf8_buffer_t;
7980
typedef std::unique_ptr<char32_t[]> input_buffer_t;
@@ -103,11 +104,11 @@ private:
103104
private:
104105
mutable Utf8String _utf8Buffer;
105106
UnicodeString _data;
107+
int _pos; // character position in buffer ( 0 <= _pos <= _data[_line].length() )
106108
char_widths_t _charWidths; // character widths from mk_wcwidth()
107109
display_t _display;
108110
int _displayInputLength;
109111
UnicodeString _hint;
110-
int _pos; // character position in buffer ( 0 <= _pos <= _len )
111112
int _prefix; // prefix length used in common prefix search
112113
int _hintSelection; // Currently selected hint.
113114
History _history;
@@ -197,6 +198,7 @@ private:
197198
int get_input_line( void );
198199
Replxx::ACTION_RESULT action( action_trait_t, key_press_handler_raw_t const&, char32_t );
199200
Replxx::ACTION_RESULT insert_character( char32_t );
201+
Replxx::ACTION_RESULT new_line( char32_t );
200202
Replxx::ACTION_RESULT go_to_begining_of_line( char32_t );
201203
Replxx::ACTION_RESULT go_to_end_of_line( char32_t );
202204
Replxx::ACTION_RESULT move_one_char_left( char32_t );

src/unicodestring.hxx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ public:
2525
assign( src );
2626
}
2727

28+
explicit UnicodeString( UnicodeString const& other, int offset, int len = -1 )
29+
: _data() {
30+
_data.insert(
31+
_data.end(),
32+
other._data.begin() + offset,
33+
len > 0 ? other._data.begin() + offset + len : other._data.end()
34+
);
35+
}
36+
2837
explicit UnicodeString( char const* src )
2938
: _data() {
3039
assign( src );

tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ def test_history_unique( self_ ):
10451045
command = ReplxxTests._cSample_ + " u0 q1"
10461046
)
10471047
self_.check_scenario(
1048-
rapid( "/history\n/unique\n/history\n<c-d>" ),
1048+
rapid( "/history<cr>/unique<cr>/history<cr><c-d>" ),
10491049
"<c9>/<rst><ceos><c10><c9>/history<rst><ceos><c17>\r\n"
10501050
" 0: a\r\n"
10511051
" 1: b\r\n"

0 commit comments

Comments
 (0)