Skip to content

Commit

Permalink
Introduce modify_callback(std::string&, int&).
Browse files Browse the repository at this point in the history
  • Loading branch information
AmokHuginnsson committed Sep 16, 2019
1 parent 92a95f0 commit 223835f
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 2 deletions.
24 changes: 24 additions & 0 deletions examples/c-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@
#include "replxx.h"
#include "util.h"

void modify_callback(char** line, int* cursorPosition, void* ud) {
char* s = *line;
char* p = strchr( s, '*' );
if ( p ) {
int len = strlen( s );
char* n = *line = calloc( len * 2, 1 );
int i = p - s;
strncpy(n, s, i);
n += i;
strncpy(n, s, i);
n += i;
strncpy(n, p + 1, len - i - 1);
n += ( len - i - 1 );
strncpy(n, p + 1, len - i - 1);
*cursorPosition *= 2;
free( s );
}
}

void completionHook(char const* context, replxx_completions* lc, int* contextLen, void* ud) {
char** examples = (char**)( ud );
size_t i;
Expand Down Expand Up @@ -112,6 +131,7 @@ int main( int argc, char** argv ) {

int quiet = 0;
char const* prompt = "\x1b[1;32mreplxx\x1b[0m> ";
int installModifyCallback = 0;
int installCompletionCallback = 1;
int installHighlighterCallback = 1;
int installHintsCallback = 1;
Expand Down Expand Up @@ -139,6 +159,7 @@ int main( int argc, char** argv ) {
case 'm': replxx_set_no_color( replxx, (*argv)[1] - '0' ); break;
case 'p': prompt = recode( (*argv) + 1 ); break;
case 'q': quiet = atoi( (*argv) + 1 ); break;
case 'M': installModifyCallback = atoi( (*argv) + 1 ); break;
case 'C': installCompletionCallback = 0; break;
case 'S': installHighlighterCallback = 0; break;
case 'N': installHintsCallback = 0; break;
Expand All @@ -150,6 +171,9 @@ int main( int argc, char** argv ) {
const char* file = "./replxx_history.txt";

replxx_history_load( replxx, file );
if ( installModifyCallback ) {
replxx_set_modify_callback( replxx, modify_callback, 0 );
}
if ( installCompletionCallback ) {
replxx_set_completion_callback( replxx, completionHook, examples );
}
Expand Down
22 changes: 22 additions & 0 deletions include/replxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,28 @@ REPLXX_IMPEXP Replxx* replxx_init( void );
*/
REPLXX_IMPEXP void replxx_end( Replxx* replxx );

/*! \brief Line modification callback type definition.
*
* User can observe and modify line contents (and cursor position)
* in response to changes to both introduced by the user through
* normal interactions.
*
* When callback returns Replxx updates current line content
* and current cursor position to the ones updated by the callback.
*
* \param line[in,out] - a R/W reference to an UTF-8 encoded input entered by the user so far.
* \param cursorPosition[in,out] - a R/W reference to current cursor position.
* \param userData - pointer to opaque user data block.
*/
typedef void (replxx_modify_callback_t)(char** input, int* contextLen, void* userData);

/*! \brief Register modify callback.
*
* \param fn - user defined callback function.
* \param userData - pointer to opaque user data block to be passed into each invocation of the callback.
*/
REPLXX_IMPEXP void replxx_set_modify_callback( Replxx*, replxx_modify_callback_t* fn, void* userData );

/*! \brief Highlighter callback type definition.
*
* If user want to have colorful input she must simply install highlighter callback.
Expand Down
20 changes: 20 additions & 0 deletions include/replxx.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,20 @@ public:
typedef std::vector<Completion> completions_t;
typedef std::vector<std::string> hints_t;

/*! \brief Line modification callback type definition.
*
* User can observe and modify line contents (and cursor position)
* in response to changes to both introduced by the user through
* normal interactions.
*
* When callback returns Replxx updates current line content
* and current cursor position to the ones updated by the callback.
*
* \param line[in,out] - a R/W reference to an UTF-8 encoded input entered by the user so far.
* \param cursorPosition[in,out] - a R/W reference to current cursor position.
*/
typedef std::function<void ( std::string& line, int& cursorPosition )> modify_callback_t;

/*! \brief Completions callback type definition.
*
* \e contextLen is counted in Unicode code points (not in bytes!).
Expand Down Expand Up @@ -321,6 +335,12 @@ public:
Replxx( Replxx&& ) = default;
Replxx& operator = ( Replxx&& ) = default;

/*! \brief Register modify callback.
*
* \param fn - user defined callback function.
*/
void set_modify_callback( modify_callback_t const& fn );

/*! \brief Register completion callback.
*
* \param fn - user defined callback function.
Expand Down
17 changes: 17 additions & 0 deletions src/replxx.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ void Replxx::set_completion_callback( completion_callback_t const& fn ) {
_impl->set_completion_callback( fn );
}

void Replxx::set_modify_callback( modify_callback_t const& fn ) {
_impl->set_modify_callback( fn );
}

void Replxx::set_highlighter_callback( highlighter_callback_t const& fn ) {
_impl->set_highlighter_callback( fn );
}
Expand Down Expand Up @@ -345,6 +349,19 @@ struct replxx_hints {
replxx::Replxx::hints_t data;
};

void modify_fwd( replxx_modify_callback_t fn, std::string& line_, int& cursorPosition_, void* userData_ ) {
char* s( strdup( line_.c_str() ) );
fn( &s, &cursorPosition_, userData_ );
line_ = s;
free( s );
return;
}

void replxx_set_modify_callback(::Replxx* replxx_, replxx_modify_callback_t* fn, void* userData) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_modify_callback( std::bind( &modify_fwd, fn, _1, _2, userData ) );
}

replxx::Replxx::completions_t completions_fwd( replxx_completion_callback_t fn, std::string const& input_, int& contextLen_, void* userData ) {
replxx_completions completions;
fn( input_.c_str(), &completions, &contextLen_, userData );
Expand Down
23 changes: 23 additions & 0 deletions src/replxx_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,22 @@ void Replxx::ReplxxImpl::clear( void ) {
_displayInputLength = 0;
}

void Replxx::ReplxxImpl::call_modify_callback( void ) {
if ( ! _modifyCallback ) {
return;
}
_utf8Buffer.assign( _data );
std::string origLine( _utf8Buffer.get() );
int pos( _pos );
std::string line( origLine );
_modifyCallback( line, pos );
if ( ( pos != _pos ) || ( line != origLine ) ) {
_data.assign( line.c_str() );
_pos = min( pos, _data.length() );
_modifiedState = true;
}
}

Replxx::ReplxxImpl::completions_t Replxx::ReplxxImpl::call_completer( std::string const& input, int& contextLen_ ) const {
Replxx::completions_t completionsIntermediary(
!! _completionCallback
Expand Down Expand Up @@ -1087,6 +1103,7 @@ int Replxx::ReplxxImpl::get_input_line( void ) {

Replxx::ACTION_RESULT Replxx::ReplxxImpl::action( action_trait_t actionTrait_, key_press_handler_raw_t const& handler_, char32_t code_ ) {
Replxx::ACTION_RESULT res( ( this->*handler_ )( code_ ) );
call_modify_callback();
if ( actionTrait_ & RESET_KILL_ACTION ) {
_killRing.lastAction = KillRing::actionOther;
}
Expand Down Expand Up @@ -1123,9 +1140,11 @@ Replxx::ACTION_RESULT Replxx::ReplxxImpl::insert_character( char32_t c ) {
_data[_pos] = c;
}
++ _pos;
call_modify_callback();
int inputLen = calculate_displayed_length( _data.get(), _data.length() );
if (
( _pos == _data.length() )
&& ! _modifiedState
&& ( _noColor || ! ( !! _highlighterCallback || !! _hintCallback ) )
&& ( _prompt._indentation + inputLen < _prompt.screen_columns() )
) {
Expand Down Expand Up @@ -1875,6 +1894,10 @@ std::string Replxx::ReplxxImpl::history_line( int index ) {
return ( _utf8Buffer.get() );
}

void Replxx::ReplxxImpl::set_modify_callback( Replxx::modify_callback_t const& fn ) {
_modifyCallback = fn;
}

void Replxx::ReplxxImpl::set_completion_callback( Replxx::completion_callback_t const& fn ) {
_completionCallback = fn;
}
Expand Down
7 changes: 5 additions & 2 deletions src/replxx_impl.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ private:
Terminal _terminal;
std::thread::id _currentThread;
Prompt _prompt;
Replxx::modify_callback_t _modifyCallback;
Replxx::completion_callback_t _completionCallback;
Replxx::highlighter_callback_t _highlighterCallback;
Replxx::hint_callback_t _hintCallback;
Expand All @@ -137,6 +138,7 @@ private:
mutable std::mutex _mutex;
public:
ReplxxImpl( FILE*, FILE*, FILE* );
void set_modify_callback( Replxx::modify_callback_t const& fn );
void set_completion_callback( Replxx::completion_callback_t const& fn );
void set_highlighter_callback( Replxx::highlighter_callback_t const& fn );
void set_hint_callback( Replxx::hint_callback_t const& fn );
Expand All @@ -159,8 +161,6 @@ public:
void set_max_history_size( int len );
void set_completion_count_cutoff( int len );
int install_window_change_handler( void );
completions_t call_completer( std::string const& input, int& ) const;
hints_t call_hinter( std::string const& input, int&, Replxx::Color& color ) const;
void print( char const*, int );
Replxx::ACTION_RESULT clear_screen( char32_t );
void emulate_key_press( char32_t );
Expand Down Expand Up @@ -221,6 +221,9 @@ private:
char32_t read_char( HINT_ACTION = HINT_ACTION::SKIP );
char const* read_from_stdin( void );
char32_t do_complete_line( bool );
void call_modify_callback( void );
completions_t call_completer( std::string const& input, int& ) const;
hints_t call_hinter( std::string const& input, int&, Replxx::Color& color ) const;
void refresh_line( HINT_ACTION = HINT_ACTION::REGENERATE );
void render( char32_t );
void render( HINT_ACTION );
Expand Down
11 changes: 11 additions & 0 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,17 @@ def test_state_manipulation( self_ ):
"replxx\n",
command = [ ReplxxTests._cSample_, "q1" ]
)
def test_modify_callback( self_ ):
self_.check_scenario(
"<up><home><right><right>*<cr><c-d>",
"<c9><ceos>abcd<brightmagenta>12<rst><c15><c9><ceos>abcd<brightmagenta>12<rst><c9>"
"<c9><ceos>abcd<brightmagenta>12<rst><c10><c9><ceos>abcd<brightmagenta>12<rst><c11>"
"<c9><ceos>ababcd<brightmagenta>12<rst>cd<brightmagenta>12<rst><c15>"
"<c9><ceos>ababcd<brightmagenta>12<rst>cd<brightmagenta>12<rst><c21>\r\n"
"ababcd12cd12\r\n",
"abcd12\n",
command = [ ReplxxTests._cSample_, "q1", "M1" ]
)

def parseArgs( self, func, argv ):
global verbosity
Expand Down

0 comments on commit 223835f

Please sign in to comment.