#include #include #include #include #include #include #include #include #include #include #include #include #include #include "replxx.hxx" #include "util.h" using Replxx = replxx::Replxx; using namespace replxx::color; class Tick { typedef std::vector keys_t; std::thread _thread; int _tick; int _promptState; bool _alive; keys_t _keys; bool _tickMessages; bool _promptFan; Replxx& _replxx; public: Tick( Replxx& replxx_, std::string const& keys_, bool tickMessages_, bool promptFan_ ) : _thread() , _tick( 0 ) , _promptState( 0 ) , _alive( false ) , _keys( keys_.begin(), keys_.end() ) , _tickMessages( tickMessages_ ) , _promptFan( promptFan_ ) , _replxx( replxx_ ) { } void start() { _alive = true; _thread = std::thread( &Tick::run, this ); } void stop() { _alive = false; _thread.join(); } void run() { std::string s; static char const PROMPT_STATES[] = "-\\|/"; while ( _alive ) { if ( _tickMessages ) { _replxx.print( "%d\n", _tick ); } if ( _tick < static_cast( _keys.size() ) ) { _replxx.emulate_key_press( _keys[_tick] ); } if ( ! _tickMessages && ! _promptFan && ( _tick >= static_cast( _keys.size() ) ) ) { break; } if ( _promptFan ) { for ( int i( 0 ); i < 4; ++ i ) { char prompt[] = "\x1b[1;32mreplxx\x1b[0m[ ]> "; prompt[18] = PROMPT_STATES[_promptState % 4]; ++ _promptState; _replxx.set_prompt( prompt ); std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) ); } } else { std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); } ++ _tick; } } }; // prototypes Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector const& user_data, bool); Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector const& user_data, bool); typedef std::vector> syntax_highlight_t; typedef std::unordered_map keyword_highlight_t; void hook_color( std::string const& str, Replxx::colors_t& colors, syntax_highlight_t const&, keyword_highlight_t const& ); void hook_modify( std::string& line, int& cursorPosition, Replxx* ); bool eq( std::string const& l, std::string const& r, int s, bool ic ) { if ( static_cast( l.length() ) < s ) { return false; } if ( static_cast( r.length() ) < s ) { return false; } bool same( true ); for ( int i( 0 ); same && ( i < s ); ++ i ) { same = ( ic && ( towlower( l[i] ) == towlower( r[i] ) ) ) || ( l[i] == r[i] ); } return same; } Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector const& examples, bool ignoreCase) { Replxx::completions_t completions; int utf8ContextLen( context_len( context.c_str() ) ); int prefixLen( static_cast( context.length() ) - utf8ContextLen ); if ( ( prefixLen > 0 ) && ( context[prefixLen - 1] == '\\' ) ) { -- prefixLen; ++ utf8ContextLen; } contextLen = utf8str_codepoint_len( context.c_str() + prefixLen, utf8ContextLen ); std::string prefix { context.substr(prefixLen) }; if ( prefix == "\\pi" ) { completions.push_back( "π" ); } else { for (auto const& e : examples) { bool lowerCasePrefix( std::none_of( prefix.begin(), prefix.end(), iswupper ) ); if ( eq( e, prefix, static_cast( prefix.size() ), ignoreCase && lowerCasePrefix ) ) { Replxx::Color c( Replxx::Color::DEFAULT ); if ( e.find( "brightred" ) != std::string::npos ) { c = Replxx::Color::BRIGHTRED; } else if ( e.find( "red" ) != std::string::npos ) { c = Replxx::Color::RED; } completions.emplace_back(e.c_str(), c); } } } return completions; } Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector const& examples, bool ignoreCase) { Replxx::hints_t hints; // only show hint if prefix is at least 'n' chars long // or if prefix begins with a specific character int utf8ContextLen( context_len( context.c_str() ) ); int prefixLen( static_cast( context.length() ) - utf8ContextLen ); contextLen = utf8str_codepoint_len( context.c_str() + prefixLen, utf8ContextLen ); std::string prefix { context.substr(prefixLen) }; if (prefix.size() >= 2 || (! prefix.empty() && prefix.at(0) == '.')) { bool lowerCasePrefix( std::none_of( prefix.begin(), prefix.end(), iswupper ) ); for (auto const& e : examples) { if ( eq( e, prefix, prefix.size(), ignoreCase && lowerCasePrefix ) ) { hints.emplace_back(e.c_str()); } } } // set hint color to green if single match found if (hints.size() == 1) { color = Replxx::Color::GREEN; } return hints; } inline bool is_kw( char ch ) { return isalnum( ch ) || ( ch == '_' ); } void hook_color( std::string const& context, Replxx::colors_t& colors, syntax_highlight_t const& regex_color, keyword_highlight_t const& word_color ) { // highlight matching regex sequences for (auto const& e : regex_color) { size_t pos {0}; std::string str = context; std::smatch match; while(std::regex_search(str, match, std::regex(e.first))) { std::string c{ match[0] }; std::string prefix( match.prefix().str() ); pos += utf8str_codepoint_len( prefix.c_str(), static_cast( prefix.length() ) ); int len( utf8str_codepoint_len( c.c_str(), static_cast( c.length() ) ) ); for (int i = 0; i < len; ++i) { colors.at(pos + i) = e.second; } pos += len; str = match.suffix(); } } bool inWord( false ); int wordStart( 0 ); int wordEnd( 0 ); int colorOffset( 0 ); auto dohl = [&](int i) { inWord = false; std::string intermission( context.substr( wordEnd, wordStart - wordEnd ) ); colorOffset += utf8str_codepoint_len( intermission.c_str(), intermission.length() ); int wordLen( i - wordStart ); std::string keyword( context.substr( wordStart, wordLen ) ); bool bold( false ); if ( keyword.substr( 0, 5 ) == "bold_" ) { keyword = keyword.substr( 5 ); bold = true; } bool underline( false ); if ( keyword.substr( 0, 10 ) == "underline_" ) { keyword = keyword.substr( 10 ); underline = true; } keyword_highlight_t::const_iterator it( word_color.find( keyword ) ); Replxx::Color color = Replxx::Color::DEFAULT; if ( it != word_color.end() ) { color = it->second; } if ( bold ) { color = replxx::color::bold( color ); } if ( underline ) { color = replxx::color::underline( color ); } for ( int k( 0 ); k < wordLen; ++ k ) { Replxx::Color& c( colors.at( colorOffset + k ) ); if ( color != Replxx::Color::DEFAULT ) { c = color; } } colorOffset += wordLen; wordEnd = i; }; for ( int i( 0 ); i < static_cast( context.length() ); ++ i ) { if ( !inWord ) { if ( is_kw( context[i] ) ) { inWord = true; wordStart = i; } } else if ( inWord && !is_kw( context[i] ) ) { dohl(i); } if ( ( context[i] != '_' ) && ispunct( context[i] ) ) { wordStart = i; dohl( i + 1 ); } } if ( inWord ) { dohl(context.length()); } } void hook_modify( std::string& currentInput_, int&, Replxx* rx ) { char prompt[64]; snprintf( prompt, 64, "\x1b[1;32mreplxx\x1b[0m[%lu]> ", currentInput_.length() ); rx->set_prompt( prompt ); } Replxx::ACTION_RESULT message( Replxx& replxx, std::string s, char32_t ) { replxx.invoke( Replxx::ACTION::CLEAR_SELF, 0 ); replxx.print( "%s\n", s.c_str() ); replxx.invoke( Replxx::ACTION::REPAINT, 0 ); return ( Replxx::ACTION_RESULT::CONTINUE ); } int main( int argc_, char** argv_ ) { // words to be completed std::vector examples { ".help", ".history", ".quit", ".exit", ".clear", ".prompt ", "hello", "world", "db", "data", "drive", "print", "put", "color_black", "color_red", "color_green", "color_brown", "color_blue", "color_magenta", "color_cyan", "color_lightgray", "color_gray", "color_brightred", "color_brightgreen", "color_yellow", "color_brightblue", "color_brightmagenta", "color_brightcyan", "color_white", "determinANT", "determiNATION", "deterMINE", "deteRMINISM", "detERMINISTIC", "deTERMINED", "star", "star_galaxy_cluser_supercluster_observable_universe", }; // highlight specific words // a regex string, and a color // the order matters, the last match will take precedence using cl = Replxx::Color; keyword_highlight_t word_color { // single chars {"`", cl::BRIGHTCYAN}, {"'", cl::BRIGHTBLUE}, {"\"", cl::BRIGHTBLUE}, {"-", cl::BRIGHTBLUE}, {"+", cl::BRIGHTBLUE}, {"=", cl::BRIGHTBLUE}, {"/", cl::BRIGHTBLUE}, {"*", cl::BRIGHTBLUE}, {"^", cl::BRIGHTBLUE}, {".", cl::BRIGHTMAGENTA}, {"(", cl::BRIGHTMAGENTA}, {")", cl::BRIGHTMAGENTA}, {"[", cl::BRIGHTMAGENTA}, {"]", cl::BRIGHTMAGENTA}, {"{", cl::BRIGHTMAGENTA}, {"}", cl::BRIGHTMAGENTA}, // color keywords {"color_black", cl::BLACK}, {"color_red", cl::RED}, {"color_green", cl::GREEN}, {"color_brown", cl::BROWN}, {"color_blue", cl::BLUE}, {"color_magenta", cl::MAGENTA}, {"color_cyan", cl::CYAN}, {"color_lightgray", cl::LIGHTGRAY}, {"color_gray", cl::GRAY}, {"color_brightred", cl::BRIGHTRED}, {"color_brightgreen", cl::BRIGHTGREEN}, {"color_yellow", cl::YELLOW}, {"color_brightblue", cl::BRIGHTBLUE}, {"color_brightmagenta", cl::BRIGHTMAGENTA}, {"color_brightcyan", cl::BRIGHTCYAN}, {"color_white", cl::WHITE}, // commands {"help", cl::BRIGHTMAGENTA}, {"history", cl::BRIGHTMAGENTA}, {"quit", cl::BRIGHTMAGENTA}, {"exit", cl::BRIGHTMAGENTA}, {"clear", cl::BRIGHTMAGENTA}, {"prompt", cl::BRIGHTMAGENTA}, }; syntax_highlight_t regex_color { // numbers {"[\\-|+]{0,1}[0-9]+", cl::YELLOW}, // integers {"[\\-|+]{0,1}[0-9]*\\.[0-9]+", cl::YELLOW}, // decimals {"[\\-|+]{0,1}[0-9]+e[\\-|+]{0,1}[0-9]+", cl::YELLOW}, // scientific notation // strings {"\".*?\"", cl::BRIGHTGREEN}, // double quotes {"\'.*?\'", cl::BRIGHTGREEN}, // single quotes }; static int const MAX_LABEL_NAME( 32 ); char label[MAX_LABEL_NAME]; for ( int r( 0 ); r < 6; ++ r ) { for ( int g( 0 ); g < 6; ++ g ) { for ( int b( 0 ); b < 6; ++ b ) { snprintf( label, MAX_LABEL_NAME, "rgb%d%d%d", r, g, b ); word_color.insert( std::make_pair( label, replxx::color::rgb666( r, g, b ) ) ); for ( int br( 0 ); br < 6; ++ br ) { for ( int bg( 0 ); bg < 6; ++ bg ) { for ( int bb( 0 ); bb < 6; ++ bb ) { snprintf( label, MAX_LABEL_NAME, "fg%d%d%dbg%d%d%d", r, g, b, br, bg, bb ); word_color.insert( std::make_pair( label, rgb666( r, g, b ) | replxx::color::bg( rgb666( br, bg, bb ) ) ) ); } } } } } } for ( int gs( 0 ); gs < 24; ++ gs ) { snprintf( label, MAX_LABEL_NAME, "gs%d", gs ); word_color.insert( std::make_pair( label, grayscale( gs ) ) ); for ( int bgs( 0 ); bgs < 24; ++ bgs ) { snprintf( label, MAX_LABEL_NAME, "gs%dgs%d", gs, bgs ); word_color.insert( std::make_pair( label, grayscale( gs ) | bg( grayscale( bgs ) ) ) ); } } Replxx::Color colorCodes[] = { Replxx::Color::BLACK, Replxx::Color::RED, Replxx::Color::GREEN, Replxx::Color::BROWN, Replxx::Color::BLUE, Replxx::Color::CYAN, Replxx::Color::MAGENTA, Replxx::Color::LIGHTGRAY, Replxx::Color::GRAY, Replxx::Color::BRIGHTRED, Replxx::Color::BRIGHTGREEN, Replxx::Color::YELLOW, Replxx::Color::BRIGHTBLUE, Replxx::Color::BRIGHTCYAN, Replxx::Color::BRIGHTMAGENTA, Replxx::Color::WHITE }; for ( Replxx::Color bg : colorCodes ) { for ( Replxx::Color fg : colorCodes ) { snprintf( label, MAX_LABEL_NAME, "c_%d_%d", static_cast( fg ), static_cast( bg ) ); word_color.insert( std::make_pair( label, fg | replxx::color::bg( bg ) ) ); } } bool tickMessages( false ); bool promptFan( false ); bool promptInCallback( false ); bool indentMultiline( false ); bool bracketedPaste( false ); bool ignoreCase( false ); std::string keys; std::string prompt; int hintDelay( 0 ); while ( argc_ > 1 ) { -- argc_; ++ argv_; switch ( (*argv_)[0] ) { case ( 'm' ): tickMessages = true; break; case ( 'F' ): promptFan = true; break; case ( 'P' ): promptInCallback = true; break; case ( 'I' ): indentMultiline = true; break; case ( 'i' ): ignoreCase = true; break; case ( 'k' ): keys = (*argv_) + 1; break; case ( 'd' ): hintDelay = std::stoi( (*argv_) + 1 ); break; case ( 'h' ): examples.push_back( (*argv_) + 1 ); break; case ( 'p' ): prompt = (*argv_) + 1; break; case ( 'B' ): bracketedPaste = true; break; } } // init the repl Replxx rx; Tick tick( rx, keys, tickMessages, promptFan ); rx.install_window_change_handler(); // the path to the history file std::string history_file_path {"./replxx_history.txt"}; // load the history file if it exists /* scope for ifstream object for auto-close */ { std::ifstream history_file( history_file_path.c_str() ); rx.history_load( history_file ); } // set the max history size rx.set_max_history_size(128); // set the max number of hint rows to show rx.set_max_hint_rows(3); // set the callbacks using namespace std::placeholders; rx.set_completion_callback( std::bind( &hook_completion, _1, _2, cref( examples ), ignoreCase ) ); rx.set_highlighter_callback( std::bind( &hook_color, _1, _2, cref( regex_color ), cref( word_color ) ) ); rx.set_hint_callback( std::bind( &hook_hint, _1, _2, _3, cref( examples ), ignoreCase ) ); if ( promptInCallback ) { rx.set_modify_callback( std::bind( &hook_modify, _1, _2, &rx ) ); } // other api calls rx.set_word_break_characters( " \n\t.,-%!;:=*~^'\"/?<>|[](){}" ); rx.set_completion_count_cutoff( 128 ); rx.set_hint_delay( hintDelay ); rx.set_double_tab_completion( false ); rx.set_complete_on_empty( true ); rx.set_beep_on_ambiguous_completion( false ); rx.set_no_color( false ); rx.set_indent_multiline( indentMultiline ); if ( bracketedPaste ) { rx.enable_bracketed_paste(); } rx.set_ignore_case( ignoreCase ); // showcase key bindings rx.bind_key_internal( Replxx::KEY::BACKSPACE, "delete_character_left_of_cursor" ); rx.bind_key_internal( Replxx::KEY::DELETE, "delete_character_under_cursor" ); rx.bind_key_internal( Replxx::KEY::LEFT, "move_cursor_left" ); rx.bind_key_internal( Replxx::KEY::RIGHT, "move_cursor_right" ); rx.bind_key_internal( Replxx::KEY::UP, "line_previous" ); rx.bind_key_internal( Replxx::KEY::DOWN, "line_next" ); rx.bind_key_internal( Replxx::KEY::meta( Replxx::KEY::UP ), "history_previous" ); rx.bind_key_internal( Replxx::KEY::meta( Replxx::KEY::DOWN ), "history_next" ); rx.bind_key_internal( Replxx::KEY::PAGE_UP, "history_first" ); rx.bind_key_internal( Replxx::KEY::PAGE_DOWN, "history_last" ); rx.bind_key_internal( Replxx::KEY::HOME, "move_cursor_to_begining_of_line" ); rx.bind_key_internal( Replxx::KEY::END, "move_cursor_to_end_of_line" ); rx.bind_key_internal( Replxx::KEY::TAB, "complete_line" ); rx.bind_key_internal( Replxx::KEY::control( Replxx::KEY::LEFT ), "move_cursor_one_word_left" ); rx.bind_key_internal( Replxx::KEY::control( Replxx::KEY::RIGHT ), "move_cursor_one_word_right" ); rx.bind_key_internal( Replxx::KEY::control( Replxx::KEY::UP ), "hint_previous" ); rx.bind_key_internal( Replxx::KEY::control( Replxx::KEY::DOWN ), "hint_next" ); rx.bind_key_internal( Replxx::KEY::control( Replxx::KEY::ENTER ), "commit_line" ); rx.bind_key_internal( Replxx::KEY::control( 'R' ), "history_incremental_search" ); rx.bind_key_internal( Replxx::KEY::control( 'W' ), "kill_to_begining_of_word" ); rx.bind_key_internal( Replxx::KEY::control( 'U' ), "kill_to_begining_of_line" ); rx.bind_key_internal( Replxx::KEY::control( 'K' ), "kill_to_end_of_line" ); rx.bind_key_internal( Replxx::KEY::control( 'Y' ), "yank" ); rx.bind_key_internal( Replxx::KEY::control( 'L' ), "clear_screen" ); rx.bind_key_internal( Replxx::KEY::control( 'D' ), "send_eof" ); rx.bind_key_internal( Replxx::KEY::control( 'C' ), "abort_line" ); rx.bind_key_internal( Replxx::KEY::control( 'T' ), "transpose_characters" ); #ifndef _WIN32 rx.bind_key_internal( Replxx::KEY::control( 'V' ), "verbatim_insert" ); rx.bind_key_internal( Replxx::KEY::control( 'Z' ), "suspend" ); #endif rx.bind_key_internal( Replxx::KEY::meta( Replxx::KEY::BACKSPACE ), "kill_to_whitespace_on_left" ); rx.bind_key_internal( Replxx::KEY::meta( 'p' ), "history_common_prefix_search" ); rx.bind_key_internal( Replxx::KEY::meta( 'n' ), "history_common_prefix_search" ); rx.bind_key_internal( Replxx::KEY::meta( 'd' ), "kill_to_end_of_word" ); rx.bind_key_internal( Replxx::KEY::meta( 'y' ), "yank_cycle" ); rx.bind_key_internal( Replxx::KEY::meta( 'u' ), "uppercase_word" ); rx.bind_key_internal( Replxx::KEY::meta( 'l' ), "lowercase_word" ); rx.bind_key_internal( Replxx::KEY::meta( 'c' ), "capitalize_word" ); rx.bind_key_internal( 'a', "insert_character" ); rx.bind_key_internal( Replxx::KEY::INSERT, "toggle_overwrite_mode" ); rx.bind_key( Replxx::KEY::F1, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F2, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F3, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F4, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F5, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F6, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F7, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F8, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F9, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F10, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F11, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::F12, std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F1 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F2 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F3 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F4 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F5 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F6 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F7 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F8 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F9 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F10 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F11 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::F12 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F1 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F2 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F3 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F4 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F5 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F6 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F7 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F8 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F9 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F10 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F11 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::F12 ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::TAB ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::HOME ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::HOME ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::END ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::END ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::PAGE_UP ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::control( Replxx::KEY::PAGE_DOWN ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::LEFT ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::RIGHT ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::UP ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::shift( Replxx::KEY::DOWN ), std::bind( &message, std::ref( rx ), "", _1 ) ); rx.bind_key( Replxx::KEY::meta( '\r' ), std::bind( &message, std::ref( rx ), "", _1 ) ); // display initial welcome message std::cout << "Welcome to Replxx\n" << "Press 'tab' to view autocompletions\n" << "Type '.help' for help\n" << "Type '.quit' or '.exit' to exit\n\n"; // set the repl prompt if ( prompt.empty() ) { prompt = "\x1b[1;32mreplxx\x1b[0m> "; } // main repl loop if ( ! keys.empty() || tickMessages || promptFan ) { tick.start(); } for (;;) { // display the prompt and retrieve input from the user char const* cinput{ nullptr }; do { cinput = rx.input(prompt); } while ( ( cinput == nullptr ) && ( errno == EAGAIN ) ); if (cinput == nullptr) { break; } // change cinput into a std::string // easier to manipulate std::string input {cinput}; if (input.empty()) { // user hit enter on an empty line continue; } else if (input.compare(0, 5, ".quit") == 0 || input.compare(0, 5, ".exit") == 0) { // exit the repl rx.history_add(input); break; } else if (input.compare(0, 5, ".help") == 0) { // display the help output std::cout << ".help\n\tdisplays the help output\n" << ".quit\n\texit the repl\n" << ".exit\n\texit the repl\n" << ".clear\n\tclears the screen\n" << ".history\n\tdisplays the history output\n" << ".prompt \n\tset the repl prompt to \n"; rx.history_add(input); continue; } else if (input.compare(0, 7, ".prompt") == 0) { // set the repl prompt text auto pos = input.find(" "); if (pos == std::string::npos) { std::cout << "Error: '.prompt' missing argument\n"; } else { prompt = input.substr(pos + 1) + " "; } rx.history_add(input); continue; } else if (input.compare(0, 8, ".history") == 0) { // display the current history Replxx::HistoryScan hs( rx.history_scan() ); for ( int i( 0 ); hs.next(); ++ i ) { std::cout << std::setw(4) << i << ": " << hs.get().text() << "\n"; } rx.history_add(input); continue; } else if (input.compare(0, 6, ".merge") == 0) { history_file_path = "replxx_history_alt.txt"; rx.history_add(input); continue; } else if (input.compare(0, 5, ".save") == 0) { history_file_path = "replxx_history_alt.txt"; std::ofstream history_file( history_file_path.c_str() ); rx.history_save( history_file ); continue; } else if (input.compare(0, 6, ".clear") == 0) { // clear the screen rx.clear_screen(); rx.history_add(input); continue; } else { // default action // echo the input rx.print( "%s\n", input.c_str() ); rx.history_add( input ); continue; } } if ( ! keys.empty() || tickMessages || promptFan ) { tick.stop(); } // save the history rx.history_sync( history_file_path ); if ( bracketedPaste ) { rx.disable_bracketed_paste(); } if ( bracketedPaste || promptInCallback || promptFan ) { std::cout << "\n" << prompt; } std::cout << "\nExiting Replxx\n"; return 0; }