Browse Source

[Minor] Update replxx library

tags/3.1
Vsevolod Stakhov 2 years ago
parent
commit
4893fc8dc5

+ 130
- 12
contrib/replxx/include/replxx.h View File

@@ -126,6 +126,8 @@ enum { REPLXX_KEY_F22 = REPLXX_KEY_F21 + 1 };
enum { REPLXX_KEY_F23 = REPLXX_KEY_F22 + 1 };
enum { REPLXX_KEY_F24 = REPLXX_KEY_F23 + 1 };
enum { REPLXX_KEY_MOUSE = REPLXX_KEY_F24 + 1 };
enum { REPLXX_KEY_PASTE_START = REPLXX_KEY_MOUSE + 1 };
enum { REPLXX_KEY_PASTE_FINISH = REPLXX_KEY_PASTE_START + 1 };

#define REPLXX_KEY_SHIFT( key ) ( ( key ) | REPLXX_KEY_BASE_SHIFT )
#define REPLXX_KEY_CONTROL( key ) ( ( key ) | REPLXX_KEY_BASE_CONTROL )
@@ -139,19 +141,25 @@ 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,
REPLXX_ACTION_KILL_TO_BEGINING_OF_LINE,
REPLXX_ACTION_KILL_TO_END_OF_WORD,
REPLXX_ACTION_KILL_TO_BEGINING_OF_WORD,
REPLXX_ACTION_KILL_TO_END_OF_SUBWORD,
REPLXX_ACTION_KILL_TO_BEGINING_OF_SUBWORD,
REPLXX_ACTION_KILL_TO_WHITESPACE_ON_LEFT,
REPLXX_ACTION_YANK,
REPLXX_ACTION_YANK_CYCLE,
REPLXX_ACTION_YANK_LAST_ARG,
REPLXX_ACTION_MOVE_CURSOR_TO_BEGINING_OF_LINE,
REPLXX_ACTION_MOVE_CURSOR_TO_END_OF_LINE,
REPLXX_ACTION_MOVE_CURSOR_ONE_WORD_LEFT,
REPLXX_ACTION_MOVE_CURSOR_ONE_WORD_RIGHT,
REPLXX_ACTION_MOVE_CURSOR_ONE_SUBWORD_LEFT,
REPLXX_ACTION_MOVE_CURSOR_ONE_SUBWORD_RIGHT,
REPLXX_ACTION_MOVE_CURSOR_LEFT,
REPLXX_ACTION_MOVE_CURSOR_RIGHT,
REPLXX_ACTION_HISTORY_NEXT,
@@ -165,12 +173,16 @@ typedef enum {
REPLXX_ACTION_CAPITALIZE_WORD,
REPLXX_ACTION_LOWERCASE_WORD,
REPLXX_ACTION_UPPERCASE_WORD,
REPLXX_ACTION_CAPITALIZE_SUBWORD,
REPLXX_ACTION_LOWERCASE_SUBWORD,
REPLXX_ACTION_UPPERCASE_SUBWORD,
REPLXX_ACTION_TRANSPOSE_CHARACTERS,
REPLXX_ACTION_TOGGLE_OVERWRITE_MODE,
#ifndef _WIN32
REPLXX_ACTION_VERBATIM_INSERT,
REPLXX_ACTION_SUSPEND,
#endif
REPLXX_ACTION_BRACKETED_PASTE,
REPLXX_ACTION_CLEAR_SCREEN,
REPLXX_ACTION_CLEAR_SELF,
REPLXX_ACTION_REPAINT,
@@ -196,12 +208,17 @@ typedef struct ReplxxStateTag {
} ReplxxState;

typedef struct Replxx Replxx;
typedef struct ReplxxHistoryScan ReplxxHistoryScan;
typedef struct ReplxxHistoryEntryTag {
char const* timestamp;
char const* text;
} ReplxxHistoryEntry;

/*! \brief Create Replxx library resouce holder.
/*! \brief Create Replxx library resource holder.
*
* Use replxx_end() to free resoiurce acquired with this function.
* Use replxx_end() to free resources acquired with this function.
*
* \return Replxx library resouce holder.
* \return Replxx library resource holder.
*/
REPLXX_IMPEXP Replxx* replxx_init( void );

@@ -211,6 +228,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.
@@ -247,8 +286,8 @@ typedef struct replxx_completions replxx_completions;
* input == "if ( obj.me"
* contextLen == 2 (depending on \e replxx_set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* Client application is free to update \e contextLen to be 6 (or any other non-negative
* number not greater than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
@@ -292,8 +331,8 @@ typedef struct replxx_hints replxx_hints;
* input == "if ( obj.me"
* contextLen == 2 (depending on \e replxx_set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* Client application is free to update \e contextLen to be 6 (or any other non-negative
* number not greater than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
@@ -314,7 +353,7 @@ REPLXX_IMPEXP void replxx_set_hint_callback( Replxx*, replxx_hint_callback_t* fn
/*! \brief Key press handler type definition.
*
* \param code - the key code replxx got from terminal.
* \return Decition on how should input() behave after this key handler returns.
* \return Decision on how should input() behave after this key handler returns.
*/
typedef ReplxxActionResult (key_press_handler_t)( int code, void* userData );

@@ -326,6 +365,8 @@ typedef ReplxxActionResult (key_press_handler_t)( int code, void* userData );
REPLXX_IMPEXP void replxx_add_hint( replxx_hints* hints, const char* str );

/*! \brief Read line of user input.
*
* Returned pointer is managed by the library and is not to be freed in the client.
*
* \param prompt - prompt to be displayed before getting user input.
* \return An UTF-8 encoded input given by the user (or nullptr on EOF).
@@ -356,11 +397,17 @@ REPLXX_IMPEXP void replxx_set_state( Replxx*, ReplxxState* state );
*
* \param fmt - printf style format.
*/
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
REPLXX_IMPEXP int replxx_print( Replxx*, char const* fmt, ... );

/*! \brief Prints a char array with the given length to standard output.
*
* \copydetails print
*
* \param str - The char array to print.
* \param length - The length of the array.
*/
REPLXX_IMPEXP int replxx_write( Replxx*, char const* str, int length );

/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
@@ -385,6 +432,19 @@ REPLXX_IMPEXP ReplxxActionResult replxx_invoke( Replxx*, ReplxxAction action, in
*/
REPLXX_IMPEXP void replxx_bind_key( Replxx*, int code, key_press_handler_t handler, void* userData );

/*! \brief Bind internal `replxx` action (by name) to handle given key-press event.
*
* Action names are the same as unique part of names of ReplxxAction enumerations
* but in lower case, e.g.: an action for recalling previous history line
* is \e REPLXX_ACTION_HISTORY_PREVIOUS so action name to be used in this
* interface for the same effect is "history_previous".
*
* \param code - handle this key-press event with following handler.
* \param actionName - name of internal action to be invoked on key press.
* \return -1 if invalid action name was used, 0 otherwise.
*/
int replxx_bind_key_internal( Replxx*, int code, char const* actionName );

REPLXX_IMPEXP void replxx_set_preload_buffer( Replxx*, const char* preloadText );

REPLXX_IMPEXP void replxx_history_add( Replxx*, const char* line );
@@ -430,6 +490,23 @@ REPLXX_IMPEXP void replxx_set_complete_on_empty( Replxx*, int val );
*/
REPLXX_IMPEXP void replxx_set_beep_on_ambiguous_completion( Replxx*, int val );

/*! \brief Set complete next/complete previous behavior.
*
* COMPLETE_NEXT/COMPLETE_PREVIOUS actions have two modes of operations,
* in case when a partial completion is possible complete only partial part (`false` setting)
* or complete first proposed completion fully (`true` setting).
* The default is to complete fully (a `true` setting - complete immediately).
*
* \param val - complete immediately.
*/
REPLXX_IMPEXP void replxx_set_immediate_completion( Replxx*, int val );

/*! \brief Set history duplicate entries behaviour.
*
* \param val - should history contain only unique entries?
*/
REPLXX_IMPEXP void replxx_set_unique_history( Replxx*, int val );

/*! \brief Disable output coloring.
*
* \param val - if set to non-zero disable output colors.
@@ -439,15 +516,56 @@ REPLXX_IMPEXP void replxx_set_no_color( Replxx*, int val );
/*! \brief Set maximum number of entries in history list.
*/
REPLXX_IMPEXP void replxx_set_max_history_size( Replxx*, int len );
REPLXX_IMPEXP char const* replxx_history_line( Replxx*, int index );
REPLXX_IMPEXP ReplxxHistoryScan* replxx_history_scan_start( Replxx* );
REPLXX_IMPEXP void replxx_history_scan_stop( Replxx*, ReplxxHistoryScan* );
REPLXX_IMPEXP int replxx_history_scan_next( Replxx*, ReplxxHistoryScan*, ReplxxHistoryEntry* );

/*! \brief Synchronize REPL's history with given file.
*
* Synchronizing means loading existing history from given file,
* merging it with current history sorted by timestamps,
* saving merged version to given file,
* keeping merged version as current REPL's history.
*
* This call is an equivalent of calling:
* replxx_history_save( rx, "some-file" );
* replxx_history_load( rx, "some-file" );
*
* \param filename - a path to the file with which REPL's current history should be synchronized.
* \return 0 iff history file was successfully created, -1 otherwise.
*/
REPLXX_IMPEXP int replxx_history_sync( Replxx*, const char* filename );

/*! \brief Save REPL's history into given file.
*
* Saving means loading existing history from given file,
* merging it with current history sorted by timestamps,
* saving merged version to given file,
* keeping original (NOT merged) version as current REPL's history.
*
* \param filename - a path to the file where REPL's history should be saved.
* \return 0 iff history file was successfully created, -1 otherwise.
*/
REPLXX_IMPEXP int replxx_history_save( Replxx*, const char* filename );

/*! \brief Load REPL's history from given file.
*
* \param filename - a path to the file which contains REPL's history that should be loaded.
* \return 0 iff history file was successfully opened, -1 otherwise.
*/
REPLXX_IMPEXP int replxx_history_load( Replxx*, const char* filename );

/*! \brief Clear REPL's in-memory history.
*/
REPLXX_IMPEXP void replxx_history_clear( Replxx* );
REPLXX_IMPEXP void replxx_clear_screen( Replxx* );
#ifdef __REPLXX_DEBUG__
void replxx_debug_dump_print_codes(void);
#endif
/* the following is extension to the original linenoise API */
REPLXX_IMPEXP int replxx_install_window_change_handler( Replxx* );
REPLXX_IMPEXP void replxx_enable_bracketed_paste( Replxx* );
REPLXX_IMPEXP void replxx_disable_bracketed_paste( Replxx* );

#ifdef __cplusplus
}

+ 160
- 14
contrib/replxx/include/replxx.hxx View File

@@ -131,6 +131,8 @@ public:
static char32_t const F23 = F22 + 1;
static char32_t const F24 = F23 + 1;
static char32_t const MOUSE = F24 + 1;
static char32_t const PASTE_START = MOUSE + 1;
static char32_t const PASTE_FINISH = PASTE_START + 1;
static constexpr char32_t shift( char32_t key_ ) {
return ( key_ | BASE_SHIFT );
}
@@ -148,19 +150,25 @@ public:
*/
enum class ACTION {
INSERT_CHARACTER,
NEW_LINE,
DELETE_CHARACTER_UNDER_CURSOR,
DELETE_CHARACTER_LEFT_OF_CURSOR,
KILL_TO_END_OF_LINE,
KILL_TO_BEGINING_OF_LINE,
KILL_TO_END_OF_WORD,
KILL_TO_BEGINING_OF_WORD,
KILL_TO_END_OF_SUBWORD,
KILL_TO_BEGINING_OF_SUBWORD,
KILL_TO_WHITESPACE_ON_LEFT,
YANK,
YANK_CYCLE,
YANK_LAST_ARG,
MOVE_CURSOR_TO_BEGINING_OF_LINE,
MOVE_CURSOR_TO_END_OF_LINE,
MOVE_CURSOR_ONE_WORD_LEFT,
MOVE_CURSOR_ONE_WORD_RIGHT,
MOVE_CURSOR_ONE_SUBWORD_LEFT,
MOVE_CURSOR_ONE_SUBWORD_RIGHT,
MOVE_CURSOR_LEFT,
MOVE_CURSOR_RIGHT,
HISTORY_NEXT,
@@ -174,12 +182,16 @@ public:
CAPITALIZE_WORD,
LOWERCASE_WORD,
UPPERCASE_WORD,
CAPITALIZE_SUBWORD,
LOWERCASE_SUBWORD,
UPPERCASE_SUBWORD,
TRANSPOSE_CHARACTERS,
TOGGLE_OVERWRITE_MODE,
#ifndef _WIN32
VERBATIM_INSERT,
SUSPEND,
#endif
BRACKETED_PASTE,
CLEAR_SCREEN,
CLEAR_SELF,
REPAINT,
@@ -222,8 +234,60 @@ public:
}
};
typedef std::vector<Completion> completions_t;
class HistoryEntry {
std::string _timestamp;
std::string _text;
public:
HistoryEntry( std::string const& timestamp_, std::string const& text_ )
: _timestamp( timestamp_ )
, _text( text_ ) {
}
std::string const& timestamp( void ) const {
return ( _timestamp );
}
std::string const& text( void ) const {
return ( _text );
}
};
class HistoryScanImpl;
class HistoryScan {
public:
typedef std::unique_ptr<HistoryScanImpl, void (*)( HistoryScanImpl* )> impl_t;
private:
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif
impl_t _impl;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
public:
HistoryScan( impl_t );
HistoryScan( HistoryScan&& ) = default;
HistoryScan& operator = ( HistoryScan&& ) = default;
bool next( void );
HistoryEntry const& get( void ) const;
private:
HistoryScan( HistoryScan const& ) = delete;
HistoryScan& operator = ( HistoryScan const& ) = delete;
};
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!).
@@ -234,8 +298,8 @@ public:
* input == "if ( obj.me"
* contextLen == 2 (depending on \e set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* Client application is free to update \e contextLen to be 6 (or any other non-negative
* number not greater than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
@@ -252,7 +316,7 @@ public:
* displayed user input.
*
* Size of \e colors buffer is equal to number of code points in user \e input
* which will be different from simple `input.lenght()`!
* which will be different from simple `input.length()`!
*
* \param input - an UTF-8 encoded input entered by the user so far.
* \param colors - output buffer for color information.
@@ -269,8 +333,8 @@ public:
* input == "if ( obj.me"
* contextLen == 2 (depending on \e set_word_break_characters())
*
* Client application is free to update \e contextLen to be 6 (or any orther non-negative
* number not greated than the number of code points in input) if it makes better sense
* Client application is free to update \e contextLen to be 6 (or any other non-negative
* number not greater than the number of code points in input) if it makes better sense
* for given client application semantics.
*
* \param input - UTF-8 encoded input entered by the user until current cursor position.
@@ -283,7 +347,7 @@ public:
/*! \brief Key press handler type definition.
*
* \param code - the key code replxx got from terminal.
* \return Decition on how should input() behave after this key handler returns.
* \return Decision on how should input() behave after this key handler returns.
*/
typedef std::function<ACTION_RESULT ( char32_t code )> key_press_handler_t;

@@ -307,12 +371,12 @@ public:
class ReplxxImpl;
private:
typedef std::unique_ptr<ReplxxImpl, void (*)( ReplxxImpl* )> impl_t;
#ifdef _WIN32
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4251)
#endif
impl_t _impl;
#ifdef _WIN32
#ifdef _MSC_VER
#pragma warning(pop)
#endif

@@ -321,6 +385,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.
@@ -340,6 +410,8 @@ public:
void set_hint_callback( hint_callback_t const& fn );

/*! \brief Read line of user input.
*
* Returned pointer is managed by the library and is not to be freed in the client.
*
* \param prompt - prompt to be displayed before getting user input.
* \return An UTF-8 encoded input given by the user (or nullptr on EOF).
@@ -370,11 +442,17 @@ public:
*
* \param fmt - printf style format.
*/
#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
void print( char const* fmt, ... );

/*! \brief Prints a char array with the given length to standard output.
*
* \copydetails print
*
* \param str - The char array to print.
* \param length - The length of the array.
*/
void write( char const* str, int length );

/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
@@ -398,11 +476,60 @@ public:
*/
void bind_key( char32_t code, key_press_handler_t handler );

/*! \brief Bind internal `replxx` action (by name) to handle given key-press event.
*
* Action names are the same as names of Replxx::ACTION enumerations
* but in lower case, e.g.: an action for recalling previous history line
* is \e Replxx::ACTION::HISTORY_PREVIOUS so action name to be used in this
* interface for the same effect is "history_previous".
*
* \param code - handle this key-press event with following handler.
* \param actionName - name of internal action to be invoked on key press.
*/
void bind_key_internal( char32_t code, char const* actionName );

void history_add( std::string const& line );
int history_save( std::string const& filename );
int history_load( std::string const& filename );

/*! \brief Synchronize REPL's history with given file.
*
* Synchronizing means loading existing history from given file,
* merging it with current history sorted by timestamps,
* saving merged version to given file,
* keeping merged version as current REPL's history.
*
* This call is an equivalent of calling:
* history_save( "some-file" );
* history_load( "some-file" );
*
* \param filename - a path to the file with which REPL's current history should be synchronized.
* \return True iff history file was successfully created.
*/
bool history_sync( std::string const& filename );

/*! \brief Save REPL's history into given file.
*
* Saving means loading existing history from given file,
* merging it with current history sorted by timestamps,
* saving merged version to given file,
* keeping original (NOT merged) version as current REPL's history.
*
* \param filename - a path to the file where REPL's history should be saved.
* \return True iff history file was successfully created.
*/
bool history_save( std::string const& filename );

/*! \brief Load REPL's history from given file.
*
* \param filename - a path to the file which contains REPL's history that should be loaded.
* \return True iff history file was successfully opened.
*/
bool history_load( std::string const& filename );

/*! \brief Clear REPL's in-memory history.
*/
void history_clear( void );
int history_size( void ) const;
std::string history_line( int index );
HistoryScan history_scan( void ) const;

void set_preload_buffer( std::string const& preloadText );

@@ -446,6 +573,23 @@ public:
*/
void set_beep_on_ambiguous_completion( bool val );

/*! \brief Set complete next/complete previous behavior.
*
* COMPLETE_NEXT/COMPLETE_PREVIOUS actions have two modes of operations,
* in case when a partial completion is possible complete only partial part (`false` setting)
* or complete first proposed completion fully (`true` setting).
* The default is to complete fully (a `true` setting - complete immediately).
*
* \param val - complete immediately.
*/
void set_immediate_completion( bool val );

/*! \brief Set history duplicate entries behaviour.
*
* \param val - should history contain only unique entries?
*/
void set_unique_history( bool val );

/*! \brief Disable output coloring.
*
* \param val - if set to non-zero disable output colors.
@@ -457,6 +601,8 @@ public:
void set_max_history_size( int len );
void clear_screen( void );
int install_window_change_handler( void );
void enable_bracketed_paste( void );
void disable_bracketed_paste( void );

private:
Replxx( Replxx const& ) = delete;

+ 30
- 58
contrib/replxx/src/conversion.cxx View File

@@ -2,10 +2,7 @@
#include <string>
#include <cstring>
#include <cctype>
#include <clocale>

#include "unicode/utf8.h"

#include <locale.h>

#include "conversion.hxx"

@@ -47,38 +44,20 @@ bool is8BitEncoding( is_8bit_encoding() );
ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, const char* src) {
ConversionResult res = ConversionResult::conversionOK;
if ( ! locale::is8BitEncoding ) {
auto sourceStart = reinterpret_cast<const unsigned char*>(src);
auto slen = strlen(src);
auto targetStart = reinterpret_cast<UChar32*>(dst);
int i = 0, j = 0;

while (i < slen && j < dstSize) {
UChar32 uc;
auto prev_i = i;
U8_NEXT (sourceStart, i, slen, uc);

if (uc <= 0) {
if (U8_IS_LEAD (sourceStart[prev_i])) {
auto lead_byte = sourceStart[prev_i];
auto trailing_bytes = (((uint8_t)(lead_byte)>=0xc2)+
((uint8_t)(lead_byte)>=0xe0)+
((uint8_t)(lead_byte)>=0xf0));

if (trailing_bytes + i > slen) {
return ConversionResult::sourceExhausted;
}
}

/* Replace with 0xFFFD */
uc = 0x0000FFFD;
}
targetStart[j++] = uc;
}
const UTF8* sourceStart = reinterpret_cast<const UTF8*>(src);
const UTF8* sourceEnd = sourceStart + strlen(src);
UTF32* targetStart = reinterpret_cast<UTF32*>(dst);
UTF32* targetEnd = targetStart + dstSize;

dstCount = j;
res = ConvertUTF8toUTF32(
&sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion);

if (j < dstSize) {
targetStart[j] = 0;
if (res == conversionOK) {
dstCount = static_cast<int>( targetStart - reinterpret_cast<UTF32*>( dst ) );

if (dstCount < dstSize) {
*targetStart = 0;
}
}
} else {
for ( dstCount = 0; ( dstCount < dstSize ) && src[dstCount]; ++ dstCount ) {
@@ -94,28 +73,22 @@ ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, cons
);
}

void copyString32to8(
char* dst, int dstSize, const char32_t* src, int srcSize, int* dstCount
) {
int copyString32to8( char* dst, int dstSize, const char32_t* src, int srcSize ) {
int resCount( 0 );
if ( ! locale::is8BitEncoding ) {
int j = 0;
UBool is_error = 0;

for (auto i = 0; i < srcSize; i ++) {
U8_APPEND ((uint8_t *)dst, j, dstSize, src[i], is_error);

if (is_error) {
break;
}
}

if (!is_error) {
if (dstCount) {
*dstCount = j;
}

if (j < dstSize) {
dst[j] = '\0';
const UTF32* sourceStart = reinterpret_cast<const UTF32*>(src);
const UTF32* sourceEnd = sourceStart + srcSize;
UTF8* targetStart = reinterpret_cast<UTF8*>(dst);
UTF8* targetEnd = targetStart + dstSize;

ConversionResult res = ConvertUTF32toUTF8(
&sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion
);

if ( res == conversionOK ) {
resCount = static_cast<int>( targetStart - reinterpret_cast<UTF8*>( dst ) );
if ( resCount < dstSize ) {
*targetStart = 0;
}
}
} else {
@@ -123,13 +96,12 @@ void copyString32to8(
for ( i = 0; ( i < dstSize ) && ( i < srcSize ) && src[i]; ++ i ) {
dst[i] = static_cast<char>( src[i] );
}
if ( dstCount ) {
*dstCount = i;
}
resCount = i;
if ( i < dstSize ) {
dst[i] = 0;
}
}
return ( resCount );
}

}

+ 13
- 8
contrib/replxx/src/conversion.hxx View File

@@ -1,20 +1,25 @@
#ifndef REPLXX_CONVERSION_HXX_INCLUDED
#define REPLXX_CONVERSION_HXX_INCLUDED 1

namespace replxx {
#include "ConvertUTF.h"

#ifdef __has_include
#if __has_include( <version> )
#include <version>
#endif
#endif

#if ! ( defined( __cpp_lib_char8_t ) || ( defined( __clang_major__ ) && ( __clang_major__ >= 8 ) && ( __cplusplus > 201703L ) ) )
namespace replxx {
typedef unsigned char char8_t;
}
#endif

typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;
namespace replxx {

ConversionResult copyString8to32( char32_t* dst, int dstSize, int& dstCount, char const* src );
ConversionResult copyString8to32( char32_t* dst, int dstSize, int& dstCount, char8_t const* src );
void copyString32to8( char* dst, int dstSize, char32_t const* src, int srcSize, int* dstCount = nullptr );
int copyString32to8( char* dst, int dstSize, char32_t const* src, int srcSize );

namespace locale {
extern bool is8BitEncoding;

+ 33
- 3
contrib/replxx/src/escape.cxx View File

@@ -1,5 +1,5 @@
#include "escape.hxx"
#include "io.hxx"
#include "terminal.hxx"
#include "replxx.hxx"

#ifndef _WIN32
@@ -115,6 +115,12 @@ static char32_t ctrlRightArrowKeyRoutine(char32_t) {
static char32_t ctrlLeftArrowKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::BASE_CONTROL | Replxx::KEY::LEFT;
}
static char32_t bracketPasteStartKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::PASTE_START;
}
static char32_t bracketPasteFinishKeyRoutine(char32_t) {
return thisKeyMetaCtrl | Replxx::KEY::PASTE_FINISH;
}
static char32_t escFailureRoutine(char32_t) {
beep();
return -1;
@@ -442,11 +448,35 @@ static char32_t escLeftBracket20SemicolonRoutine(char32_t c) {
return doDispatch(c, escLeftBracket20SemicolonDispatch);
}

static CharacterDispatchRoutine escLeftBracket200Routines[] = {
bracketPasteStartKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket200Dispatch = {
1, "~", escLeftBracket200Routines
};
static char32_t escLeftBracket200Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket200Dispatch);
}

static CharacterDispatchRoutine escLeftBracket201Routines[] = {
bracketPasteFinishKeyRoutine, escFailureRoutine
};
static CharacterDispatch escLeftBracket201Dispatch = {
1, "~", escLeftBracket201Routines
};
static char32_t escLeftBracket201Routine(char32_t c) {
c = read_unicode_character();
if (c == 0) return 0;
return doDispatch(c, escLeftBracket201Dispatch);
}

static CharacterDispatchRoutine escLeftBracket20Routines[] = {
f9KeyRoutine, escLeftBracket20SemicolonRoutine, escFailureRoutine
f9KeyRoutine, escLeftBracket20SemicolonRoutine, escLeftBracket200Routine, escLeftBracket201Routine, escFailureRoutine
};
static CharacterDispatch escLeftBracket20Dispatch = {
2, "~;", escLeftBracket20Routines
4, "~;01", escLeftBracket20Routines
};
static char32_t escLeftBracket20Routine(char32_t c) {
c = read_unicode_character();

+ 322
- 68
contrib/replxx/src/history.cxx View File

@@ -1,147 +1,401 @@
#include <algorithm>
#include <memory>
#include <fstream>
#include <cstring>

#ifndef _WIN32

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#endif /* _WIN32 */

#include "replxx.hxx"
#include "history.hxx"
#include "utf8string.hxx"

using namespace std;

namespace replxx {

namespace {
void delete_ReplxxHistoryScanImpl( Replxx::HistoryScanImpl* impl_ ) {
delete impl_;
}
}

static int const REPLXX_DEFAULT_HISTORY_MAX_LEN( 1000 );

Replxx::HistoryScan::HistoryScan( impl_t impl_ )
: _impl( std::move( impl_ ) ) {
}

bool Replxx::HistoryScan::next( void ) {
return ( _impl->next() );
}

Replxx::HistoryScanImpl::HistoryScanImpl( History::entries_t const& entries_ )
: _entries( entries_ )
, _it( _entries.end() )
, _utf8Cache()
, _entryCache( std::string(), std::string() )
, _cacheValid( false ) {
}

Replxx::HistoryEntry const& Replxx::HistoryScan::get( void ) const {
return ( _impl->get() );
}

bool Replxx::HistoryScanImpl::next( void ) {
if ( _it == _entries.end() ) {
_it = _entries.begin();
} else {
++ _it;
}
_cacheValid = false;
return ( _it != _entries.end() );
}

Replxx::HistoryEntry const& Replxx::HistoryScanImpl::get( void ) const {
if ( _cacheValid ) {
return ( _entryCache );
}
_utf8Cache.assign( _it->text() );
_entryCache = Replxx::HistoryEntry( _it->timestamp(), _utf8Cache.get() );
_cacheValid = true;
return ( _entryCache );
}

Replxx::HistoryScan::impl_t History::scan( void ) const {
return ( Replxx::HistoryScan::impl_t( new Replxx::HistoryScanImpl( _entries ), delete_ReplxxHistoryScanImpl ) );
}

History::History( void )
: _data()
: _entries()
, _maxSize( REPLXX_DEFAULT_HISTORY_MAX_LEN )
, _maxLineLength( 0 )
, _index( 0 )
, _previousIndex( -2 )
, _recallMostRecent( false ) {
}

void History::add( UnicodeString const& line ) {
if ( ( _maxSize > 0 ) && ( _data.empty() || ( line != _data.back() ) ) ) {
if ( size() > _maxSize ) {
_data.erase( _data.begin() );
if ( -- _previousIndex < -1 ) {
_previousIndex = -2;
}
}
if ( static_cast<int>( line.length() ) > _maxLineLength ) {
_maxLineLength = static_cast<int>( line.length() );
}
_data.push_back( line );
, _current( _entries.begin() )
, _yankPos( _entries.end() )
, _previous( _entries.begin() )
, _recallMostRecent( false )
, _unique( true ) {
}

void History::add( UnicodeString const& line, std::string const& when ) {
if ( _maxSize <= 0 ) {
return;
}
if ( ! _entries.empty() && ( line == _entries.back().text() ) ) {
_entries.back() = Entry( now_ms_str(), line );
return;
}
remove_duplicate( line );
trim_to_max_size();
_entries.emplace_back( when, line );
_locations.insert( make_pair( line, last() ) );
if ( _current == _entries.end() ) {
_current = last();
}
_yankPos = _entries.end();
}

int History::save( std::string const& filename ) {
#ifndef _WIN32
mode_t old_umask = umask( S_IXUSR | S_IRWXG| S_IRWXO );
class FileLock {
std::string _path;
int _lockFd;
public:
FileLock( std::string const& name_ )
: _path( name_ + ".lock" )
, _lockFd( ::open( _path.c_str(), O_CREAT | O_RDWR, 0600 ) ) {
static_cast<void>( ::lockf( _lockFd, F_LOCK, 0 ) == 0 );
}
~FileLock( void ) {
static_cast<void>( ::lockf( _lockFd, F_ULOCK, 0 ) == 0 );
::close( _lockFd );
::unlink( _path.c_str() );
return;
}
};
#endif

bool History::save( std::string const& filename, bool sync_ ) {
#ifndef _WIN32
mode_t old_umask = umask( S_IXUSR | S_IRWXG | S_IRWXO );
FileLock fileLock( filename );
#endif
entries_t entries;
locations_t locations;
if ( ! sync_ ) {
entries.swap( _entries );
locations.swap( _locations );
_entries = entries;
reset_iters();
}
do_load( filename );
sort();
remove_duplicates();
trim_to_max_size();
ofstream histFile( filename );
if ( ! histFile ) {
return ( -1 );
return ( false );
}
#ifndef _WIN32
umask( old_umask );
chmod( filename.c_str(), S_IRUSR | S_IWUSR );
#endif
Utf8String utf8;
for ( UnicodeString const& h : _data ) {
if ( ! h.is_empty() ) {
utf8.assign( h );
histFile << utf8.get() << endl;
for ( Entry const& h : _entries ) {
if ( ! h.text().is_empty() ) {
utf8.assign( h.text() );
histFile << "### " << h.timestamp() << "\n" << utf8.get() << endl;
}
}
if ( ! sync_ ) {
_entries = std::move( entries );
_locations = std::move( locations );
}
reset_iters();
return ( true );
}

namespace {

bool is_timestamp( std::string const& s ) {
static char const TIMESTAMP_PATTERN[] = "### dddd-dd-dd dd:dd:dd.ddd";
static int const TIMESTAMP_LENGTH( sizeof ( TIMESTAMP_PATTERN ) - 1 );
if ( s.length() != TIMESTAMP_LENGTH ) {
return ( false );
}
for ( int i( 0 ); i < TIMESTAMP_LENGTH; ++ i ) {
if ( TIMESTAMP_PATTERN[i] == 'd' ) {
if ( ! isdigit( s[i] ) ) {
return ( false );
}
} else if ( s[i] != TIMESTAMP_PATTERN[i] ) {
return ( false );
}
}
return ( 0 );
return ( true );
}

}

int History::load( std::string const& filename ) {
bool History::do_load( std::string const& filename ) {
ifstream histFile( filename );
if ( ! histFile ) {
return ( -1 );
return ( false );
}
string line;
string when( "0000-00-00 00:00:00.000" );
while ( getline( histFile, line ).good() ) {
string::size_type eol( line.find_first_of( "\r\n" ) );
if ( eol != string::npos ) {
line.erase( eol );
}
if ( is_timestamp( line ) ) {
when.assign( line, 4, std::string::npos );
continue;
}
if ( ! line.empty() ) {
add( UnicodeString( line ) );
_entries.emplace_back( when, UnicodeString( line ) );
}
}
return 0;
return ( true );
}

bool History::load( std::string const& filename ) {
clear();
bool success( do_load( filename ) );
sort();
remove_duplicates();
trim_to_max_size();
_previous = _current = last();
_yankPos = _entries.end();
return ( success );
}

void History::sort( void ) {
typedef std::vector<Entry> sortable_entries_t;
_locations.clear();
sortable_entries_t sortableEntries( _entries.begin(), _entries.end() );
std::stable_sort( sortableEntries.begin(), sortableEntries.end() );
_entries.clear();
_entries.insert( _entries.begin(), sortableEntries.begin(), sortableEntries.end() );
}

void History::clear( void ) {
_locations.clear();
_entries.clear();
_current = _entries.begin();
_recallMostRecent = false;
}

void History::set_max_size( int size_ ) {
if ( size_ >= 0 ) {
_maxSize = size_;
int curSize( size() );
if ( _maxSize < curSize ) {
_data.erase( _data.begin(), _data.begin() + ( curSize - _maxSize ) );
}
trim_to_max_size();
}
}

void History::reset_pos( int pos_ ) {
if ( pos_ == -1 ) {
_index = size() - 1;
_recallMostRecent = false;
void History::reset_yank_iterator( void ) {
_yankPos = _entries.end();
}

bool History::next_yank_position( void ) {
bool resetYankSize( false );
if ( _yankPos == _entries.end() ) {
resetYankSize = true;
}
if ( ( _yankPos != _entries.begin() ) && ( _yankPos != _entries.end() ) ) {
-- _yankPos;
} else {
_index = pos_;
_yankPos = moved( _entries.end(), -2 );
}
return ( resetYankSize );
}

bool History::move( bool up_ ) {
if (_previousIndex != -2 && ! up_ ) {
_index = 1 + _previousIndex; // emulate Windows down-arrow
bool doRecall( _recallMostRecent && ! up_ );
if ( doRecall ) {
_current = _previous; // emulate Windows down-arrow
}
_recallMostRecent = false;
return ( doRecall || move( _current, up_ ? -1 : 1 ) );
}

void History::jump( bool start_, bool reset_ ) {
if ( start_ ) {
_current = _entries.begin();
} else {
_index += up_ ? -1 : 1;
_current = last();
}
_previousIndex = -2;
if (_index < 0) {
_index = 0;
return ( false );
} else if ( _index >= size() ) {
_index = size() - 1;
return ( false );
if ( reset_ ) {
_recallMostRecent = false;
}
_recallMostRecent = true;
return ( true );
}

void History::jump( bool start_ ) {
_index = start_ ? 0 : size() - 1;
_previousIndex = -2;
_recallMostRecent = true;
void History::save_pos( void ) {
_previous = _current;
}

void History::restore_pos( void ) {
_current = _previous;
}

bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_ ) {
int direct( size() + ( back_ ? -1 : 1 ) );
int i( ( _index + direct ) % _data.size() );
while ( i != _index ) {
if ( _data[i].starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
_index = i;
_previousIndex = -2;
_recallMostRecent = true;
int step( back_ ? -1 : 1 );
entries_t::const_iterator it( moved( _current, step, true ) );
while ( it != _current ) {
if ( it->text().starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
_current = it;
commit_index();
return ( true );
}
i += direct;
i %= _data.size();
move( it, step, true );
}
return ( false );
}

UnicodeString const& History::operator[] ( int idx_ ) const {
return ( _data[ idx_ ] );
bool History::move( entries_t::const_iterator& it_, int by_, bool wrapped_ ) const {
if ( by_ > 0 ) {
for ( int i( 0 ); i < by_; ++ i ) {
++ it_;
if ( it_ != _entries.end() ) {
} else if ( wrapped_ ) {
it_ = _entries.begin();
} else {
-- it_;
return ( false );
}
}
} else {
for ( int i( 0 ); i > by_; -- i ) {
if ( it_ != _entries.begin() ) {
-- it_;
} else if ( wrapped_ ) {
it_ = last();
} else {
return ( false );
}
}
}
return ( true );
}

History::entries_t::const_iterator History::moved( entries_t::const_iterator it_, int by_, bool wrapped_ ) const {
move( it_, by_, wrapped_ );
return ( it_ );
}

void History::erase( entries_t::const_iterator it_ ) {
bool invalidated( it_ == _current );
_locations.erase( it_->text() );
it_ = _entries.erase( it_ );
if ( invalidated ) {
_current = it_;
}
if ( ( _current == _entries.end() ) && ! _entries.empty() ) {
-- _current;
}
_yankPos = _entries.end();
_previous = _current;
}

void History::trim_to_max_size( void ) {
while ( size() > _maxSize ) {
erase( _entries.begin() );
}
}

void History::remove_duplicate( UnicodeString const& line_ ) {
if ( ! _unique ) {
return;
}
locations_t::iterator it( _locations.find( line_ ) );
if ( it == _locations.end() ) {
return;
}
erase( it->second );
}

void History::remove_duplicates( void ) {
if ( ! _unique ) {
return;
}
_locations.clear();
typedef std::pair<locations_t::iterator, bool> locations_insertion_result_t;
for ( entries_t::iterator it( _entries.begin() ), end( _entries.end() ); it != end; ++ it ) {
locations_insertion_result_t locationsInsertionResult( _locations.insert( make_pair( it->text(), it ) ) );
if ( ! locationsInsertionResult.second ) {
_entries.erase( locationsInsertionResult.first->second );
locationsInsertionResult.first->second = it;
}
}
}

void History::update_last( UnicodeString const& line_ ) {
if ( _unique ) {
_locations.erase( _entries.back().text() );
remove_duplicate( line_ );
_locations.insert( make_pair( line_, last() ) );
}
_entries.back() = Entry( now_ms_str(), line_ );
}

void History::drop_last( void ) {
erase( last() );
}

bool History::is_last( void ) const {
return ( _current == last() );
}

History::entries_t::const_iterator History::last( void ) const {
return ( moved( _entries.end(), -1 ) );
}

void History::reset_iters( void ) {
_previous = _current = last();
_yankPos = _entries.end();
}

}

+ 101
- 33
contrib/replxx/src/history.hxx View File

@@ -1,70 +1,138 @@
#ifndef REPLXX_HISTORY_HXX_INCLUDED
#define REPLXX_HISTORY_HXX_INCLUDED 1

#include <vector>
#include <list>
#include <unordered_map>

#include "unicodestring.hxx"
#include "utf8string.hxx"
#include "conversion.hxx"
#include "util.hxx"

namespace std {
template<>
struct hash<replxx::UnicodeString> {
std::size_t operator()( replxx::UnicodeString const& us_ ) const {
std::size_t h( 0 );
char32_t const* p( us_.get() );
char32_t const* e( p + us_.length() );
while ( p != e ) {
h *= 31;
h += *p;
++ p;
}
return ( h );
}
};
}

namespace replxx {

class History {
public:
typedef std::vector<UnicodeString> lines_t;
class Entry {
std::string _timestamp;
UnicodeString _text;
public:
Entry( std::string const& timestamp_, UnicodeString const& text_ )
: _timestamp( timestamp_ )
, _text( text_ ) {
}
std::string const& timestamp( void ) const {
return ( _timestamp );
}
UnicodeString const& text( void ) const {
return ( _text );
}
bool operator < ( Entry const& other_ ) const {
return ( _timestamp < other_._timestamp );
}
};
typedef std::list<Entry> entries_t;
typedef std::unordered_map<UnicodeString, entries_t::const_iterator> locations_t;
private:
lines_t _data;
entries_t _entries;
locations_t _locations;
int _maxSize;
int _maxLineLength;
int _index;
int _previousIndex;
entries_t::const_iterator _current;
entries_t::const_iterator _yankPos;
/*
* _previous and _recallMostRecent are used to allow
* HISTORY_NEXT action (a down-arrow key) to have a special meaning
* if invoked after a line from history was accepted without
* any modification.
* Special meaning is: a down arrow shall jump to the line one
* after previously accepted from history.
*/
entries_t::const_iterator _previous;
bool _recallMostRecent;
bool _unique;
public:
History( void );
void add( UnicodeString const& line );
int save( std::string const& filename );
int load( std::string const& filename );
void add( UnicodeString const& line, std::string const& when = now_ms_str() );
bool save( std::string const& filename, bool );
bool load( std::string const& filename );
void clear( void );
void set_max_size( int len );
void reset_pos( int = -1 );
UnicodeString const& operator[] ( int ) const;
void set_recall_most_recent( void ) {
_recallMostRecent = true;
void set_unique( bool unique_ ) {
_unique = unique_;
remove_duplicates();
}
void reset_yank_iterator();
bool next_yank_position( void );
void reset_recall_most_recent( void ) {
_recallMostRecent = false;
}
void drop_last( void ) {
_data.pop_back();
}
void commit_index( void ) {
_previousIndex = _recallMostRecent ? _index : -2;
}
int current_pos( void ) const {
return ( _index );
}
bool is_last( void ) const {
return ( _index == ( size() - 1 ) );
_previous = _current;
_recallMostRecent = true;
}
bool is_empty( void ) const {
return ( _data.empty() );
}
void update_last( UnicodeString const& line_ ) {
_data.back() = line_;
return ( _entries.empty() );
}
void update_last( UnicodeString const& );
void drop_last( void );
bool is_last( void ) const;
bool move( bool );
UnicodeString const& current( void ) const {
return ( _data[_index] );
return ( _current->text() );
}
void jump( bool );
UnicodeString const& yank_line( void ) const {
return ( _yankPos->text() );
}
void jump( bool, bool = true );
bool common_prefix_search( UnicodeString const&, int, bool );
int size( void ) const {
return ( static_cast<int>( _data.size() ) );
}
int max_line_length( void ) {
return ( _maxLineLength );
return ( static_cast<int>( _entries.size() ) );
}
Replxx::HistoryScan::impl_t scan( void ) const;
void save_pos( void );
void restore_pos( void );
private:
History( History const& ) = delete;
History& operator = ( History const& ) = delete;
bool move( entries_t::const_iterator&, int, bool = false ) const;
entries_t::const_iterator moved( entries_t::const_iterator, int, bool = false ) const;
void erase( entries_t::const_iterator );
void trim_to_max_size( void );
void remove_duplicate( UnicodeString const& );
void remove_duplicates( void );
bool do_load( std::string const& );
entries_t::const_iterator last( void ) const;
void sort( void );
void reset_iters( void );
};

class Replxx::HistoryScanImpl {
History::entries_t const& _entries;
History::entries_t::const_iterator _it;
mutable Utf8String _utf8Cache;
mutable Replxx::HistoryEntry _entryCache;
mutable bool _cacheValid;
public:
HistoryScanImpl( History::entries_t const& );
bool next( void );
Replxx::HistoryEntry const& get( void ) const;
};

}

+ 4
- 2
contrib/replxx/src/killring.hxx View File

@@ -17,9 +17,11 @@ class KillRing {
public:
enum action { actionOther, actionKill, actionYank };
action lastAction;
size_t lastYankSize;

KillRing() : size(0), index(0), lastAction(actionOther) {
KillRing()
: size(0)
, index(0)
, lastAction(actionOther) {
theRing.reserve(capacity);
}


+ 29
- 35
contrib/replxx/src/prompt.cxx View File

@@ -25,14 +25,13 @@ namespace replxx {
Prompt::Prompt( Terminal& terminal_ )
: _extraLines( 0 )
, _lastLinePosition( 0 )
, _previousInputLen( 0 )
, _previousLen( 0 )
, _cursorRowOffset( 0 )
, _screenColumns( 0 )
, _terminal( terminal_ ) {
}

void Prompt::write() {
_terminal.write32( _text.get(), _byteCount );
_terminal.write32( _text.get(), _text.length() );
}

void Prompt::update_screen_columns( void ) {
@@ -40,28 +39,36 @@ void Prompt::update_screen_columns( void ) {
}

void Prompt::set_text( UnicodeString const& text_ ) {
_text = text_;
update_state();
}

void Prompt::update_state() {
_cursorRowOffset -= _extraLines;
_extraLines = 0;
_lastLinePosition = 0;
_screenColumns = 0;
update_screen_columns();
// strip control characters from the prompt -- we do allow newline
_text = text_;
UnicodeString::const_iterator in( text_.begin() );
UnicodeString::const_iterator in( _text.begin() );
UnicodeString::iterator out( _text.begin() );

int len = 0;
int visibleCount = 0;
int x = 0;

bool const strip = !tty::out;

while (in != text_.end()) {
while (in != _text.end()) {
char32_t c = *in;
if ('\n' == c || !is_control_code(c)) {
*out = c;
++out;
++in;
++len;
++visibleCount;
if ('\n' == c || ++x >= _screenColumns) {
x = 0;
++_extraLines;
_lastLinePosition = len;
_lastLinePosition = visibleCount;
}
} else if (c == '\x1b') {
if ( strip ) {
@@ -69,7 +76,7 @@ void Prompt::set_text( UnicodeString const& text_ ) {
++in;
if (*in == '[') {
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
while ( ( in != _text.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
++in;
}
if (*in == 'm') {
@@ -85,7 +92,7 @@ void Prompt::set_text( UnicodeString const& text_ ) {
*out = *in;
++out;
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
while ( ( in != _text.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
*out = *in;
++out;
++in;
@@ -101,11 +108,15 @@ void Prompt::set_text( UnicodeString const& text_ ) {
++in;
}
}
_characterCount = len;
_byteCount = static_cast<int>(out - _text.begin());
_characterCount = visibleCount;
int charCount( static_cast<int>( out - _text.begin() ) );
_text.erase( charCount, _text.length() - charCount );

_cursorRowOffset += _extraLines;
}

_indentation = len - _lastLinePosition;
_cursorRowOffset = _extraLines;
int Prompt::indentation() const {
return _characterCount - _lastLinePosition;
}

// Used with DynamicPrompt (history search)
@@ -113,37 +124,20 @@ void Prompt::set_text( UnicodeString const& text_ ) {
const UnicodeString forwardSearchBasePrompt("(i-search)`");
const UnicodeString reverseSearchBasePrompt("(reverse-i-search)`");
const UnicodeString endSearchBasePrompt("': ");
UnicodeString previousSearchText; // remembered across invocations of replxx_input()

DynamicPrompt::DynamicPrompt( Terminal& terminal_, int initialDirection )
: Prompt( terminal_ )
, _searchText()
, _direction( initialDirection ) {
update_screen_columns();
_cursorRowOffset = 0;
const UnicodeString* basePrompt =
(_direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
size_t promptStartLength = basePrompt->length();
_characterCount = static_cast<int>(promptStartLength + endSearchBasePrompt.length());
_byteCount = _characterCount;
_lastLinePosition = _characterCount; // TODO fix this, we are asssuming
// that the history prompt won't wrap (!)
_previousLen = _characterCount;
_text.assign( *basePrompt ).append( endSearchBasePrompt );
calculate_screen_position(
0, 0, screen_columns(), _characterCount,
_indentation, _extraLines
);
updateSearchPrompt();
}

void DynamicPrompt::updateSearchPrompt(void) {
update_screen_columns();
const UnicodeString* basePrompt =
(_direction > 0) ? &forwardSearchBasePrompt : &reverseSearchBasePrompt;
size_t promptStartLength = basePrompt->length();
_characterCount = static_cast<int>(promptStartLength + _searchText.length() +
endSearchBasePrompt.length());
_byteCount = _characterCount;
_text.assign( *basePrompt ).append( _searchText ).append( endSearchBasePrompt );
update_state();
}

}

+ 4
- 8
contrib/replxx/src/prompt.hxx View File

@@ -4,36 +4,32 @@
#include <cstdlib>

#include "unicodestring.hxx"
#include "io.hxx"
#include "terminal.hxx"

namespace replxx {

class Prompt { // a convenience struct for grouping prompt info
public:
UnicodeString _text; // our copy of the prompt text, edited
int _characterCount; // chars in _text
int _byteCount; // bytes in _text
int _characterCount; // visible characters in _text
int _extraLines; // extra lines (beyond 1) occupied by prompt
int _indentation; // column offset to end of prompt
int _lastLinePosition; // index into _text where last line begins
int _previousInputLen; // _characterCount of previous input line, for clearing
int _cursorRowOffset; // where the cursor is relative to the start of the prompt
int _previousLen; // help erasing
private:
int _screenColumns; // width of screen in columns [cache]
Terminal& _terminal;
public:
Prompt( Terminal& );
void set_text( UnicodeString const& textPtr );
void update_state();
void update_screen_columns( void );
int screen_columns() const {
return ( _screenColumns );
}
void write();
int indentation() const;
};

extern UnicodeString previousSearchText; // remembered across invocations of replxx_input()

// changing prompt for "(reverse-i-search)`text':" etc.
//
struct DynamicPrompt : public Prompt {

+ 143
- 16
contrib/replxx/src/replxx.cxx View File

@@ -102,7 +102,17 @@
#include "replxx.h"
#include "replxx.hxx"
#include "replxx_impl.hxx"
#include "io.hxx"
#include "history.hxx"

static_assert(
static_cast<int>( replxx::Replxx::ACTION::SEND_EOF ) == static_cast<int>( REPLXX_ACTION_SEND_EOF ),
"C and C++ `ACTION` APIs are missaligned!"
);

static_assert(
static_cast<int>( replxx::Replxx::KEY::PASTE_FINISH ) == static_cast<int>( REPLXX_KEY_PASTE_FINISH ),
"C and C++ `KEY` APIs are missaligned!"
);

using namespace std;
using namespace std::placeholders;
@@ -124,6 +134,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 );
}
@@ -140,20 +154,28 @@ void Replxx::history_add( std::string const& line ) {
_impl->history_add( line );
}

int Replxx::history_save( std::string const& filename ) {
bool Replxx::history_sync( std::string const& filename ) {
return ( _impl->history_sync( filename ) );
}

bool Replxx::history_save( std::string const& filename ) {
return ( _impl->history_save( filename ) );
}

int Replxx::history_load( std::string const& filename ) {
bool Replxx::history_load( std::string const& filename ) {
return ( _impl->history_load( filename ) );
}

void Replxx::history_clear( void ) {
_impl->history_clear();
}

int Replxx::history_size( void ) const {
return ( _impl->history_size() );
}

std::string Replxx::history_line( int index ) {
return ( _impl->history_line( index ) );
Replxx::HistoryScan Replxx::history_scan( void ) const {
return ( _impl->history_scan() );
}

void Replxx::set_preload_buffer( std::string const& preloadText ) {
@@ -188,6 +210,14 @@ void Replxx::set_beep_on_ambiguous_completion( bool val ) {
_impl->set_beep_on_ambiguous_completion( val );
}

void Replxx::set_immediate_completion( bool val ) {
_impl->set_immediate_completion( val );
}

void Replxx::set_unique_history( bool val ) {
_impl->set_unique_history( val );
}

void Replxx::set_no_color( bool val ) {
_impl->set_no_color( val );
}
@@ -212,6 +242,10 @@ void Replxx::bind_key( char32_t keyPress_, key_press_handler_t handler_ ) {
_impl->bind_key( keyPress_, handler_ );
}

void Replxx::bind_key_internal( char32_t keyPress_, char const* actionName_ ) {
_impl->bind_key_internal( keyPress_, actionName_ );
}

Replxx::State Replxx::get_state( void ) const {
return ( _impl->get_state() );
}
@@ -224,6 +258,14 @@ int Replxx::install_window_change_handler( void ) {
return ( _impl->install_window_change_handler() );
}

void Replxx::enable_bracketed_paste( void ) {
_impl->enable_bracketed_paste();
}

void Replxx::disable_bracketed_paste( void ) {
_impl->disable_bracketed_paste();
}

void Replxx::print( char const* format_, ... ) {
::std::va_list ap;
va_start( ap, format_ );
@@ -236,6 +278,10 @@ void Replxx::print( char const* format_, ... ) {
return ( _impl->print( buf.get(), size ) );
}

void Replxx::write( char const* str, int length ) {
return ( _impl->print( str, length ) );
}

}

::Replxx* replxx_init() {
@@ -271,6 +317,16 @@ void replxx_bind_key( ::Replxx* replxx_, int code_, key_press_handler_t handler_
replxx->bind_key( code_, std::bind( key_press_handler_forwarder, handler_, _1, userData_ ) );
}

int replxx_bind_key_internal( ::Replxx* replxx_, int code_, char const* actionName_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
try {
replxx->bind_key_internal( code_, actionName_ );
} catch ( ... ) {
return ( -1 );
}
return ( 0 );
}

void replxx_get_state( ::Replxx* replxx_, ReplxxState* state ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx::Replxx::State s( replxx->get_state() );
@@ -303,8 +359,8 @@ void replxx_set_preload_buffer(::Replxx* replxx_, const char* preloadText) {
* user
*
* @param prompt text of prompt to display to the user
* @return the returned string belongs to the caller on return and must be
* freed to prevent memory leaks
* @return the returned string is managed by replxx library
* and it must NOT be freed in the client.
*/
char const* replxx_input( ::Replxx* replxx_, const char* prompt ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
@@ -329,6 +385,16 @@ int replxx_print( ::Replxx* replxx_, char const* format_, ... ) {
return ( size );
}

int replxx_write( ::Replxx* replxx_, char const* str, int length ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
try {
replxx->print( str, length );
} catch ( ... ) {
return ( -1 );
}
return static_cast<int>( length );
}

struct replxx_completions {
replxx::Replxx::completions_t data;
};
@@ -337,6 +403,23 @@ struct replxx_hints {
replxx::Replxx::hints_t data;
};

void modify_fwd( replxx_modify_callback_t fn, std::string& line_, int& cursorPosition_, void* userData_ ) {
#ifdef _WIN32
#define strdup _strdup
#endif
char* s( strdup( line_.c_str() ) );
#undef strdup
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 );
@@ -359,7 +442,7 @@ void highlighter_fwd( replxx_highlighter_callback_t fn, std::string const& input
return ( static_cast<ReplxxColor>( c ) );
}
);
fn( input.c_str(), colorsTmp.data(), colors.size(), userData );
fn( input.c_str(), colorsTmp.data(), static_cast<int>( colors.size() ), userData );
std::transform(
colorsTmp.begin(),
colorsTmp.end(),
@@ -395,7 +478,7 @@ void replxx_add_completion( replxx_completions* lc, const char* str ) {
lc->data.emplace_back( str );
}

void replxx_add_completion( replxx_completions* lc, const char* str, ReplxxColor color ) {
void replxx_add_color_completion( replxx_completions* lc, const char* str, ReplxxColor color ) {
lc->data.emplace_back( str, static_cast<replxx::Replxx::Color>( color ) );
}

@@ -449,19 +532,58 @@ void replxx_set_beep_on_ambiguous_completion( ::Replxx* replxx_, int val ) {
replxx->set_beep_on_ambiguous_completion( val ? true : false );
}

/* Fetch a line of the history by (zero-based) index. If the requested
* line does not exist, NULL is returned. The return value is a heap-allocated
* copy of the line. */
char const* replxx_history_line( ::Replxx* replxx_, int index ) {
void replxx_set_immediate_completion( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_immediate_completion( val ? true : false );
}

void replxx_set_unique_history( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_unique_history( val ? true : false );
}

void replxx_enable_bracketed_paste( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->enable_bracketed_paste();
}

void replxx_disable_bracketed_paste( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_line( index ).c_str() );
replxx->disable_bracketed_paste();
}

ReplxxHistoryScan* replxx_history_scan_start( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( reinterpret_cast<ReplxxHistoryScan*>( replxx->history_scan().release() ) );
}

void replxx_history_scan_stop( ::Replxx*, ReplxxHistoryScan* historyScan_ ) {
delete reinterpret_cast<replxx::Replxx::HistoryScanImpl*>( historyScan_ );
}

int replxx_history_scan_next( ::Replxx*, ReplxxHistoryScan* historyScan_, ReplxxHistoryEntry* historyEntry_ ) {
replxx::Replxx::HistoryScanImpl* historyScan( reinterpret_cast<replxx::Replxx::HistoryScanImpl*>( historyScan_ ) );
bool hasNext( historyScan->next() );
if ( hasNext ) {
replxx::Replxx::HistoryEntry const& historyEntry( historyScan->get() );
historyEntry_->timestamp = historyEntry.timestamp().c_str();
historyEntry_->text = historyEntry.text().c_str();
}
return ( hasNext ? 0 : -1 );
}

/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int replxx_history_sync( ::Replxx* replxx_, const char* filename ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_sync( filename ) ? 0 : -1 );
}

/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int replxx_history_save( ::Replxx* replxx_, const char* filename ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_save( filename ) );
return ( replxx->history_save( filename ) ? 0 : -1 );
}

/* Load the history from the specified file. If the file does not exist
@@ -471,7 +593,12 @@ int replxx_history_save( ::Replxx* replxx_, const char* filename ) {
* on error -1 is returned. */
int replxx_history_load( ::Replxx* replxx_, const char* filename ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
return ( replxx->history_load( filename ) );
return ( replxx->history_load( filename ) ? 0 : -1 );
}

void replxx_history_clear( ::Replxx* replxx_ ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->history_clear();
}

int replxx_history_size( ::Replxx* replxx_ ) {

+ 580
- 316
contrib/replxx/src/replxx_impl.cxx
File diff suppressed because it is too large
View File


+ 55
- 18
contrib/replxx/src/replxx_impl.hxx View File

@@ -38,13 +38,13 @@
#include <unordered_map>
#include <thread>
#include <mutex>
#include <chrono>

#include "replxx.hxx"
#include "history.hxx"
#include "killring.hxx"
#include "utf8string.hxx"
#include "prompt.hxx"
#include "io.hxx"

namespace replxx {

@@ -74,10 +74,10 @@ public:
}
};
typedef std::vector<Completion> completions_t;
typedef std::vector<UnicodeString> data_t;
typedef std::vector<UnicodeString> hints_t;
typedef std::unique_ptr<char[]> utf8_buffer_t;
typedef std::unique_ptr<char32_t[]> input_buffer_t;
typedef std::vector<char> char_widths_t;
typedef std::vector<char32_t> display_t;
typedef std::deque<char32_t> key_presses_t;
typedef std::deque<std::string> messages_t;
@@ -87,41 +87,51 @@ public:
TRIM,
SKIP
};
typedef std::unordered_map<std::string, Replxx::key_press_handler_t> named_actions_t;
typedef Replxx::ACTION_RESULT ( ReplxxImpl::* key_press_handler_raw_t )( char32_t );
typedef std::unordered_map<int, Replxx::key_press_handler_t> key_press_handlers_t;
private:
typedef int long long unsigned action_trait_t;
static action_trait_t const NOOP = 0;
static action_trait_t const WANT_REFRESH = 1;
static action_trait_t const RESET_KILL_ACTION = 2;
static action_trait_t const SET_KILL_ACTION = 4;
static action_trait_t const DONT_RESET_PREFIX = 8;
static action_trait_t const DONT_RESET_COMPLETIONS = 16;
static action_trait_t const NOOP = 0;
static action_trait_t const WANT_REFRESH = 1;
static action_trait_t const RESET_KILL_ACTION = 2;
static action_trait_t const SET_KILL_ACTION = 4;
static action_trait_t const DONT_RESET_PREFIX = 8;
static action_trait_t const DONT_RESET_COMPLETIONS = 16;
static action_trait_t const HISTORY_RECALL_MOST_RECENT = 32;
static action_trait_t const DONT_RESET_HIST_YANK_INDEX = 64;
private:
mutable Utf8String _utf8Buffer;
UnicodeString _data;
char_widths_t _charWidths; // character widths from mk_wcwidth()
int _pos; // character position in buffer ( 0 <= _pos <= _data[_line].length() )
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;
KillRing _killRing;
int long long _lastRefreshTime;
bool _refreshSkipped;
int _lastYankSize;
int _maxHintRows;
int _hintDelay;
char const* _breakChars;
std::string _wordBreakChars;
std::string _subwordBreakChars;
int _completionCountCutoff;
bool _overwrite;
bool _doubleTabCompletion;
bool _completeOnEmpty;
bool _beepOnAmbiguousCompletion;
bool _immediateCompletion;
bool _bracketedPaste;
bool _noColor;
named_actions_t _namedActions;
key_press_handlers_t _keyPressHandlers;
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;
@@ -132,37 +142,50 @@ private:
int _completionSelection;
std::string _preloadedBuffer; // used with set_preload_buffer
std::string _errorMessage;
UnicodeString _previousSearchText; // remembered across invocations of replxx_input()
bool _modifiedState;
Replxx::Color _hintColor;
hints_t _hintsCache;
int _hintContextLenght;
Utf8String _hintSeed;
mutable std::mutex _mutex;
public:
ReplxxImpl( FILE*, FILE*, FILE* );
virtual ~ReplxxImpl( void );
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 );
char const* input( std::string const& prompt );
void history_add( std::string const& line );
int history_save( std::string const& filename );
int history_load( std::string const& filename );
std::string history_line( int index );
bool history_sync( std::string const& filename );
bool history_save( std::string const& filename );
bool history_load( std::string const& filename );
void history_clear( void );
Replxx::HistoryScan::impl_t history_scan( void ) const;
int history_size( void ) const;
void set_preload_buffer(std::string const& preloadText);
void set_word_break_characters( char const* wordBreakers );
void set_subword_break_characters( char const* subwordBreakers );
void set_max_hint_rows( int count );
void set_hint_delay( int milliseconds );
void set_double_tab_completion( bool val );
void set_complete_on_empty( bool val );
void set_beep_on_ambiguous_completion( bool val );
void set_immediate_completion( bool val );
void set_unique_history( bool );
void set_no_color( bool val );
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 enable_bracketed_paste( void );
void disable_bracketed_paste( void );
void print( char const*, int );
Replxx::ACTION_RESULT clear_screen( char32_t );
void emulate_key_press( char32_t );
Replxx::ACTION_RESULT invoke( Replxx::ACTION, char32_t );
void bind_key( char32_t, Replxx::key_press_handler_t );
void bind_key_internal( char32_t, char const* );
Replxx::State get_state( void ) const;
void set_state( Replxx::State const& );
private:
@@ -173,21 +196,30 @@ 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 );
Replxx::ACTION_RESULT move_one_char_right( char32_t );
template <bool subword>
Replxx::ACTION_RESULT move_one_word_left( char32_t );
template <bool subword>
Replxx::ACTION_RESULT move_one_word_right( char32_t );
template <bool subword>
Replxx::ACTION_RESULT kill_word_to_left( char32_t );
template <bool subword>
Replxx::ACTION_RESULT kill_word_to_right( char32_t );
Replxx::ACTION_RESULT kill_to_whitespace_to_left( char32_t );
Replxx::ACTION_RESULT kill_to_begining_of_line( char32_t );
Replxx::ACTION_RESULT kill_to_end_of_line( char32_t );
Replxx::ACTION_RESULT yank( char32_t );
Replxx::ACTION_RESULT yank_cycle( char32_t );
Replxx::ACTION_RESULT yank_last_arg( char32_t );
template <bool subword>
Replxx::ACTION_RESULT capitalize_word( char32_t );
template <bool subword>
Replxx::ACTION_RESULT lowercase_word( char32_t );
template <bool subword>
Replxx::ACTION_RESULT uppercase_word( char32_t );
Replxx::ACTION_RESULT transpose_characters( char32_t );
Replxx::ACTION_RESULT abort_line( char32_t );
@@ -215,9 +247,13 @@ private:
Replxx::ACTION_RESULT complete( bool );
Replxx::ACTION_RESULT incremental_history_search( char32_t startChar );
Replxx::ACTION_RESULT common_prefix_search( char32_t startChar );
Replxx::ACTION_RESULT bracketed_paste( char32_t startChar );
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 );
@@ -226,10 +262,11 @@ private:
int context_length( void );
void clear( void );
void repaint( void );
template <bool subword>
bool is_word_break_character( char32_t ) const;
void dynamicRefresh(Prompt& pi, char32_t* buf32, int len, int pos);
void dynamicRefresh(Prompt& oldPrompt, Prompt& newPrompt, char32_t* buf32, int len, int pos);
char const* finalize_input( char const* );
void clear_self_to_end_of_screen( void );
void clear_self_to_end_of_screen( Prompt const* = nullptr );
typedef struct {
int index;
bool error;

+ 24
- 4
contrib/replxx/src/unicodestring.hxx View File

@@ -2,7 +2,6 @@
#define REPLXX_UNICODESTRING_HXX_INCLUDED

#include <vector>
#include <string>
#include <cstring>

#include "conversion.hxx"
@@ -26,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 );
@@ -55,15 +63,15 @@ public:
}

UnicodeString& assign( std::string const& str_ ) {
_data.resize( str_.length() );
_data.resize( static_cast<int>( str_.length() ) );
int len( 0 );
copyString8to32( _data.data(), str_.length(), len, str_.c_str() );
copyString8to32( _data.data(), static_cast<int>( str_.length() ), len, str_.c_str() );
_data.resize( len );
return *this;
}

UnicodeString& assign( char const* str_ ) {
size_t byteCount( strlen( str_ ) );
int byteCount( static_cast<int>( strlen( str_ ) ) );
_data.resize( byteCount );
int len( 0 );
copyString8to32( _data.data(), byteCount, len, str_ );
@@ -93,6 +101,10 @@ public:
return *this;
}

void push_back( char32_t c_ ) {
_data.push_back( c_ );
}

UnicodeString& append( char32_t const* src, int len ) {
_data.insert( _data.end(), src, src + len );
return *this;
@@ -149,6 +161,14 @@ public:
);
}

bool ends_with( data_buffer_t::const_iterator first_, data_buffer_t::const_iterator last_ ) const {
int len( static_cast<int>( std::distance( first_, last_ ) ) );
return (
( len <= length() )
&& ( std::equal( first_, last_, _data.end() - len ) )
);
}

bool is_empty( void ) const {
return ( _data.size() == 0 );
}

+ 28
- 5
contrib/replxx/src/utf8string.hxx View File

@@ -12,20 +12,24 @@ private:
typedef std::unique_ptr<char[]> buffer_t;
buffer_t _data;
int _bufSize;
int _len;
public:
Utf8String( void )
: _data()
, _bufSize( 0 ) {
, _bufSize( 0 )
, _len( 0 ) {
}
explicit Utf8String( UnicodeString const& src )
: _data()
, _bufSize( 0 ) {
, _bufSize( 0 )
, _len( 0 ) {
assign( src, src.length() );
}

Utf8String( UnicodeString const& src_, int len_ )
: _data()
, _bufSize( 0 ) {
, _bufSize( 0 )
, _len( 0 ) {
assign( src_, len_ );
}

@@ -34,20 +38,39 @@ public:
}

void assign( UnicodeString const& str_, int len_ ) {
assign( str_.get(), len_ );
}

void assign( char32_t const* str_, int len_ ) {
int len( len_ * 4 );
realloc( len );
copyString32to8( _data.get(), len, str_.get(), len_ );
_len = copyString32to8( _data.get(), len, str_, len_ );
}

void assign( std::string const& str_ ) {
realloc( str_.length() );
realloc( static_cast<int>( str_.length() ) );
strncpy( _data.get(), str_.c_str(), str_.length() );
_len = static_cast<int>( str_.length() );
}

void assign( Utf8String const& other_ ) {
realloc( other_._len );
strncpy( _data.get(), other_._data.get(), other_._len );
_len = other_._len;
}

char const* get() const {
return _data.get();
}

int size( void ) const {
return ( _len );
}

bool operator != ( Utf8String const& other_ ) {
return ( ( other_._len != _len ) || ( memcmp( other_._data.get(), _data.get(), _len ) != 0 ) );
}

private:
void realloc( int reqLen ) {
if ( ( reqLen + 1 ) > _bufSize ) {

+ 18
- 12
contrib/replxx/src/util.cxx View File

@@ -1,5 +1,7 @@
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <wctype.h>

#include "util.hxx"
@@ -8,18 +10,6 @@ namespace replxx {

int mk_wcwidth( char32_t );

/**
* Recompute widths of all characters in a char32_t buffer
* @param text - input buffer of Unicode characters
* @param widths - output buffer of character widths
* @param charCount - number of characters in buffer
*/
void recompute_character_widths( char32_t const* text, char* widths, int charCount ) {
for (int i = 0; i < charCount; ++i) {
widths[i] = mk_wcwidth(text[i]);
}
}

/**
* Calculate a new screen position given a starting position, screen width and
* character count
@@ -148,5 +138,21 @@ char const* ansi_color( Replxx::Color color_ ) {
return ( code );
}

std::string now_ms_str( void ) {
std::chrono::milliseconds ms( std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::system_clock::now().time_since_epoch() ) );
time_t t( ms.count() / 1000 );
tm broken;
#ifdef _WIN32
#define localtime_r( t, b ) localtime_s( ( b ), ( t ) )
#endif
localtime_r( &t, &broken );
#undef localtime_r
static int const BUFF_SIZE( 32 );
char str[BUFF_SIZE];
strftime( str, BUFF_SIZE, "%Y-%m-%d %H:%M:%S.", &broken );
snprintf( str + sizeof ( "YYYY-mm-dd HH:MM:SS" ), 5, "%03d", static_cast<int>( ms.count() % 1000 ) );
return ( str );
}

}


+ 5
- 1
contrib/replxx/src/util.hxx View File

@@ -10,10 +10,14 @@ inline bool is_control_code(char32_t testChar) {
(testChar >= 0x7F && testChar <= 0x9F); // DEL and C1 controls
}

void recompute_character_widths( char32_t const* text, char* widths, int charCount );
inline char32_t control_to_human( char32_t key ) {
return ( key < 27 ? ( key + 0x40 ) : ( key + 0x18 ) );
}

void calculate_screen_position( int x, int y, int screenColumns, int charCount, int& xOut, int& yOut );
int calculate_displayed_length( char32_t const* buf32, int size );
char const* ansi_color( Replxx::Color );
std::string now_ms_str( void );

}


+ 12
- 26
contrib/replxx/src/windows.cxx View File

@@ -4,11 +4,7 @@

#include "windows.hxx"
#include "conversion.hxx"
#include "io.hxx"

#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
static DWORD const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
#endif
#include "terminal.hxx"

using namespace std;

@@ -17,7 +13,7 @@ namespace replxx {
WinAttributes WIN_ATTR;

template<typename T>
T* HandleEsc(T* p, T* end) {
T* HandleEsc(HANDLE out_, T* p, T* end) {
if (*p == '[') {
int code = 0;

@@ -93,45 +89,37 @@ T* HandleEsc(T* p, T* end) {
++p;
}

auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(
handle,
out_,
WIN_ATTR._consoleAttribute | WIN_ATTR._consoleColor
);

return p;
}

int win_write( char const* str_, int size_ ) {
int win_write( HANDLE out_, bool autoEscape_, char const* str_, int size_ ) {
int count( 0 );
DWORD currentMode( 0 );
HANDLE consoleOut( GetStdHandle( STD_OUTPUT_HANDLE ) );
if ( tty::out && GetConsoleMode( consoleOut, &currentMode ) ) {
UINT inputCodePage( GetConsoleCP() );
UINT outputCodePage( GetConsoleOutputCP() );
SetConsoleCP( 65001 );
SetConsoleOutputCP( 65001 );
if ( tty::out ) {
DWORD nWritten( 0 );
if ( SetConsoleMode( consoleOut, currentMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) ) {
WriteConsoleA( consoleOut, str_, size_, &nWritten, nullptr );
if ( autoEscape_ ) {
WriteConsoleA( out_, str_, size_, &nWritten, nullptr );
count = nWritten;
SetConsoleMode( consoleOut, currentMode );
} else {
char const* s( str_ );
char const* e( str_ + size_ );
while ( str_ < e ) {
if ( *str_ == 27 ) {
if ( s < str_ ) {
int toWrite( str_ - s );
WriteConsoleA( consoleOut, s, static_cast<DWORD>( toWrite ), &nWritten, nullptr );
int toWrite( static_cast<int>( str_ - s ) );
WriteConsoleA( out_, s, static_cast<DWORD>( toWrite ), &nWritten, nullptr );
count += nWritten;
if ( nWritten != toWrite ) {
s = str_ = nullptr;
break;
}
}
s = HandleEsc( str_ + 1, e );
int escaped( s - str_);
s = HandleEsc( out_, str_ + 1, e );
int escaped( static_cast<int>( s - str_ ) );
count += escaped;
str_ = s;
} else {
@@ -140,12 +128,10 @@ int win_write( char const* str_, int size_ ) {
}

if ( s < str_ ) {
WriteConsoleA( consoleOut, s, static_cast<DWORD>( str_ - s ), &nWritten, nullptr );
WriteConsoleA( out_, s, static_cast<DWORD>( str_ - s ), &nWritten, nullptr );
count += nWritten;
}
}
SetConsoleCP( inputCodePage );
SetConsoleOutputCP( outputCodePage );
} else {
count = _write( 1, str_, size_ );
}

+ 1
- 1
contrib/replxx/src/windows.hxx View File

@@ -35,7 +35,7 @@ class WinAttributes {
int _consoleColor;
};

int win_write( char const*, int );
int win_write( HANDLE, bool, char const*, int );

extern WinAttributes WIN_ATTR;


Loading…
Cancel
Save