repl: update replxx and some configuration for bracketed paste (#2784)

This commit is contained in:
Tyler Wilding 2023-06-29 15:32:48 -05:00 committed by GitHub
parent 4643129948
commit 84cd1a5c18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 3191 additions and 936 deletions

View file

@ -150,6 +150,9 @@ void Wrapper::init_settings() {
// NOTE - a nice popular project that uses replxx
// - https://github.com/ClickHouse/ClickHouse/blob/master/base/base/ReplxxLineReader.cpp#L366
repl.set_word_break_characters(" \t");
repl.set_complete_on_empty(false);
repl.set_indent_multiline(false);
repl.enable_bracketed_paste();
// Setup default keybinds
for (const auto& bind : repl_config.keybinds) {
char32_t code;
@ -168,8 +171,6 @@ void Wrapper::init_settings() {
}
}
// TODO - command to print out keybinds
void Wrapper::reload_startup_file() {
startup_file = load_user_startup_file(username, repl_config.game_version);
}

38
third-party/replxx/.github/workflows/ci.yml generated vendored Normal file
View file

@ -0,0 +1,38 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Install required software
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install g++ cmake python3-pexpect
# Runs a single command using the runners shell
- name: Build the code
run: ./build-all.sh
- name: Run regression tests
run: env SKIP=8bit_encoding ./tests.py

5
third-party/replxx/.gitignore generated vendored
View file

@ -8,7 +8,8 @@ example
linenoise_example
libreplxx.a
*.dSYM
history.txt
replxx_history.txt
replxx_history_alt.txt
*.o
*~
.vs
.vs

5
third-party/replxx/CMakeLists.txt generated vendored
View file

@ -50,7 +50,7 @@ set(REPLXX_CONTACT "amok@codestation.org")
set(is-clang $<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>)
set(is-msvc $<CXX_COMPILER_ID:MSVC>)
set(is-gnu $<CXX_COMPILER_ID:GNU>)
set(compiler-id-clang-or-gnu $<AND:$<OR:${is-clang},${is-gnu}>,$<NOT:${is-msvc}>>)
set(compiler-id-clang-or-gnu $<OR:${is-clang},${is-gnu}>)
set(coverage-config $<AND:$<CONFIG:Coverage>,$<OR:${is-gnu},${is-clang}>>)
@ -97,6 +97,7 @@ if (NOT CMAKE_VERSION VERSION_LESS 3.13)
target_link_options(
replxx
PRIVATE
$<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU>>:-g -ggdb -g3 -ggdb3>
$<${coverage-config}:--coverage>
$<${is-msvc}:/ignore:4099>
)
@ -182,6 +183,8 @@ if (REPLXX_BUILD_EXAMPLES)
)
target_compile_definitions(replxx-example-cxx-api PRIVATE REPLXX_STATIC $<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS=1>)
target_compile_definitions(replxx-example-c-api PRIVATE REPLXX_STATIC $<$<CXX_COMPILER_ID:MSVC>:_CRT_SECURE_NO_WARNINGS=1>)
target_link_options(replxx-example-cxx-api PRIVATE $<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU>>:-g -ggdb -g3 -ggdb3>)
target_link_options(replxx-example-c-api PRIVATE $<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU>>:-g -ggdb -g3 -ggdb3>)
target_link_libraries(replxx-example-cxx-api PRIVATE replxx::replxx)
target_link_libraries(
replxx-example-c-api

18
third-party/replxx/README.md generated vendored
View file

@ -1,8 +1,8 @@
# Read Evaluate Print Loop ++
![demo](https://drive.google.com/uc?export=download&id=0B53g2Y3z7rWNT2dCRGVVNldaRnc)
![demo](https://codestation.org/download/replxx.gif)
[![Build Status](https://travis-ci.org/AmokHuginnsson/replxx.svg?branch=master)](https://travis-ci.org/AmokHuginnsson/replxx)
![Build Status](https://github.com/AmokHuginnsson/replxx/actions/workflows/ci.yml/badge.svg)
A small, portable GNU readline replacement for Linux, Windows and
MacOS which is capable of handling UTF-8 characters. Unlike GNU
@ -30,13 +30,6 @@ programs.
* UTF8 aware
* support for Linux, MacOS and Windows
It deviates from Salvatore's original goal to have a minimal readline
replacement for the sake of supporting UTF8 and Windows. It deviates
from 10gen Inc.'s goal to create a C++ interface to linenoise. This
library uses C++ internally, but to the user it provides a pure C
interface that is compatible with the original linenoise API.
C interface.
## Requirements
To build this library, you will need a C++11-enabled compiler and
@ -110,10 +103,3 @@ cmake -G "Visual Studio 12 2013 Win64" -DCMAKE_BUILD_TYPE=Release ..
Please test it everywhere you can and report back!
## Let's push this forward!
Patches should be provided in the respect of linenoise sensibility for
small and easy to understand code that and the license
restrictions. Extensions must be submitted under a BSD license-style.
A contributor license is required for contributions.

View file

@ -143,6 +143,7 @@ int main( int argc, char** argv ) {
int installCompletionCallback = 1;
int installHighlighterCallback = 1;
int installHintsCallback = 1;
int indentMultiline = 0;
while ( argc > 1 ) {
-- argc;
++ argv;
@ -160,11 +161,13 @@ int main( int argc, char** argv ) {
case 'h': replxx_set_max_hint_rows( replxx, atoi( (*argv) + 1 ) ); break;
case 'H': replxx_set_hint_delay( replxx, atoi( (*argv) + 1 ) ); break;
case 's': replxx_set_max_history_size( replxx, atoi( (*argv) + 1 ) ); break;
case 'i': replxx_set_preload_buffer( replxx, recode( (*argv) + 1 ) ); break;
case 'P': replxx_set_preload_buffer( replxx, recode( (*argv) + 1 ) ); break;
case 'I': replxx_set_immediate_completion( replxx, (*argv)[1] - '0' ); break;
case 'u': replxx_set_unique_history( replxx, (*argv)[1] - '0' ); break;
case 'w': replxx_set_word_break_characters( replxx, (*argv) + 1 ); break;
case 'm': replxx_set_no_color( replxx, (*argv)[1] - '0' ); break;
case 'i': replxx_set_ignore_case( replxx, (*argv)[1] - '0' ); break;
case 'n': indentMultiline = (*argv)[1] - '0'; break;
case 'B': replxx_enable_bracketed_paste( replxx ); break;
case 'p': prompt = recode( (*argv) + 1 ); break;
case 'q': quiet = atoi( (*argv) + 1 ); break;
@ -177,6 +180,7 @@ int main( int argc, char** argv ) {
}
replxx_set_indent_multiline( replxx, indentMultiline );
const char* file = "./replxx_history.txt";
replxx_history_load( replxx, file );

View file

@ -1,12 +1,14 @@
#include <regex>
#include <string>
#include <vector>
#include <unordered_map>
#include <string>
#include <regex>
#include <cerrno>
#include <cctype>
#include <cstdlib>
#include <utility>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
@ -14,20 +16,27 @@
#include "util.h"
using Replxx = replxx::Replxx;
using namespace replxx::color;
class Tick {
typedef std::vector<char32_t> 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_ = {} )
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() {
@ -40,26 +49,56 @@ public:
}
void run() {
std::string s;
static char const PROMPT_STATES[] = "-\\|/";
while ( _alive ) {
if ( _keys.empty() ) {
if ( _tickMessages ) {
_replxx.print( "%d\n", _tick );
} else if ( _tick < static_cast<int>( _keys.size() ) ) {
}
if ( _tick < static_cast<int>( _keys.size() ) ) {
_replxx.emulate_key_press( _keys[_tick] );
} else {
}
if ( ! _tickMessages && ! _promptFan && ( _tick >= static_cast<int>( _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;
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
}
}
};
// prototypes
Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector<std::string> const& user_data);
Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector<std::string> const& user_data);
void hook_color(std::string const& str, Replxx::colors_t& colors, std::vector<std::pair<std::string, Replxx::Color>> const& user_data);
Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector<std::string> const& user_data, bool);
Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector<std::string> const& user_data, bool);
typedef std::vector<std::pair<std::string, Replxx::Color>> syntax_highlight_t;
typedef std::unordered_map<std::string, Replxx::Color> 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* );
Replxx::completions_t hook_completion(std::string const& context, int& contextLen, std::vector<std::string> const& examples) {
bool eq( std::string const& l, std::string const& r, int s, bool ic ) {
if ( static_cast<int>( l.length() ) < s ) {
return false;
}
if ( static_cast<int>( 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<std::string> const& examples, bool ignoreCase) {
Replxx::completions_t completions;
int utf8ContextLen( context_len( context.c_str() ) );
int prefixLen( static_cast<int>( context.length() ) - utf8ContextLen );
@ -74,7 +113,8 @@ Replxx::completions_t hook_completion(std::string const& context, int& contextLe
completions.push_back( "π" );
} else {
for (auto const& e : examples) {
if (e.compare(0, prefix.size(), prefix) == 0) {
bool lowerCasePrefix( std::none_of( prefix.begin(), prefix.end(), iswupper ) );
if ( eq( e, prefix, static_cast<int>( prefix.size() ), ignoreCase && lowerCasePrefix ) ) {
Replxx::Color c( Replxx::Color::DEFAULT );
if ( e.find( "brightred" ) != std::string::npos ) {
c = Replxx::Color::BRIGHTRED;
@ -89,7 +129,7 @@ Replxx::completions_t hook_completion(std::string const& context, int& contextLe
return completions;
}
Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector<std::string> const& examples) {
Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::Color& color, std::vector<std::string> const& examples, bool ignoreCase) {
Replxx::hints_t hints;
// only show hint if prefix is at least 'n' chars long
@ -101,8 +141,9 @@ Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::C
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 (e.compare(0, prefix.size(), prefix) == 0) {
if ( eq( e, prefix, prefix.size(), ignoreCase && lowerCasePrefix ) ) {
hints.emplace_back(e.c_str());
}
}
@ -116,7 +157,11 @@ Replxx::hints_t hook_hint(std::string const& context, int& contextLen, Replxx::C
return hints;
}
void hook_color(std::string const& context, Replxx::colors_t& colors, std::vector<std::pair<std::string, Replxx::Color>> const& regex_color) {
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};
@ -137,6 +182,69 @@ void hook_color(std::string const& context, Replxx::colors_t& colors, std::vecto
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<int>( 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 ) {
@ -154,31 +262,33 @@ int main( int argc_, char** argv_ ) {
"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", "color_normal",
"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;
std::vector<std::pair<std::string, cl>> regex_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},
{"`", 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},
@ -197,16 +307,16 @@ int main( int argc_, char** argv_ ) {
{"color_brightmagenta", cl::BRIGHTMAGENTA},
{"color_brightcyan", cl::BRIGHTCYAN},
{"color_white", cl::WHITE},
{"color_normal", cl::NORMAL},
// commands
{"\\.help", cl::BRIGHTMAGENTA},
{"\\.history", cl::BRIGHTMAGENTA},
{"\\.quit", cl::BRIGHTMAGENTA},
{"\\.exit", cl::BRIGHTMAGENTA},
{"\\.clear", cl::BRIGHTMAGENTA},
{"\\.prompt", cl::BRIGHTMAGENTA},
{"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
@ -216,17 +326,89 @@ int main( int argc_, char** argv_ ) {
{"\".*?\"", 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<int>( fg ), static_cast<int>( 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, argc_ > 1 ? argv_[1] : "" );
Tick tick( rx, keys, tickMessages, promptFan );
rx.install_window_change_handler();
// the path to the history file
std::string history_file {"./replxx_history.txt"};
std::string history_file_path {"./replxx_history.txt"};
// load the history file if it exists
rx.history_load(history_file);
/* 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);
@ -236,25 +418,36 @@ int main( int argc_, char** argv_ ) {
// set the callbacks
using namespace std::placeholders;
rx.set_completion_callback( std::bind( &hook_completion, _1, _2, cref( examples ) ) );
rx.set_highlighter_callback( std::bind( &hook_color, _1, _2, cref( regex_color ) ) );
rx.set_hint_callback( std::bind( &hook_hint, _1, _2, _3, cref( examples ) ) );
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( " \t.,-%!;:=*~^'\"/?<>|[](){}" );
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, "history_previous" );
rx.bind_key_internal( Replxx::KEY::DOWN, "history_next" );
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" );
@ -335,6 +528,7 @@ int main( int argc_, char** argv_ ) {
rx.bind_key( Replxx::KEY::shift( Replxx::KEY::RIGHT ), std::bind( &message, std::ref( rx ), "<S-Right>", _1 ) );
rx.bind_key( Replxx::KEY::shift( Replxx::KEY::UP ), std::bind( &message, std::ref( rx ), "<S-Up>", _1 ) );
rx.bind_key( Replxx::KEY::shift( Replxx::KEY::DOWN ), std::bind( &message, std::ref( rx ), "<S-Down>", _1 ) );
rx.bind_key( Replxx::KEY::meta( '\r' ), std::bind( &message, std::ref( rx ), "<M-Enter>", _1 ) );
// display initial welcome message
std::cout
@ -344,10 +538,12 @@ int main( int argc_, char** argv_ ) {
<< "Type '.quit' or '.exit' to exit\n\n";
// set the repl prompt
std::string prompt {"\x1b[1;32mreplxx\x1b[0m> "};
if ( prompt.empty() ) {
prompt = "\x1b[1;32mreplxx\x1b[0m> ";
}
// main repl loop
if ( argc_ > 1 ) {
if ( ! keys.empty() || tickMessages || promptFan ) {
tick.start();
}
for (;;) {
@ -413,14 +609,15 @@ int main( int argc_, char** argv_ ) {
continue;
} else if (input.compare(0, 6, ".merge") == 0) {
history_file = "replxx_history_alt.txt";
history_file_path = "replxx_history_alt.txt";
rx.history_add(input);
continue;
} else if (input.compare(0, 5, ".save") == 0) {
history_file = "replxx_history_alt.txt";
rx.history_save(history_file);
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) {
@ -440,12 +637,18 @@ int main( int argc_, char** argv_ ) {
continue;
}
}
if ( argc_ > 1 ) {
if ( ! keys.empty() || tickMessages || promptFan ) {
tick.stop();
}
// save the history
rx.history_sync(history_file);
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";

93
third-party/replxx/include/replxx.h generated vendored
View file

@ -81,9 +81,7 @@ typedef enum {
REPLXX_COLOR_BRIGHTMAGENTA = 13,
REPLXX_COLOR_BRIGHTCYAN = 14,
REPLXX_COLOR_WHITE = 15,
REPLXX_COLOR_NORMAL = REPLXX_COLOR_LIGHTGRAY,
REPLXX_COLOR_DEFAULT = -1,
REPLXX_COLOR_ERROR = -2
REPLXX_COLOR_DEFAULT = 1u << 16u
} ReplxxColor;
enum { REPLXX_KEY_BASE = 0x0010ffff + 1 };
@ -136,17 +134,21 @@ enum { REPLXX_KEY_PASTE_FINISH = REPLXX_KEY_PASTE_START + 1 };
enum { REPLXX_KEY_BACKSPACE = REPLXX_KEY_CONTROL( 'H' ) };
enum { REPLXX_KEY_TAB = REPLXX_KEY_CONTROL( 'I' ) };
enum { REPLXX_KEY_ENTER = REPLXX_KEY_CONTROL( 'M' ) };
enum { REPLXX_KEY_ABORT = REPLXX_KEY_META( REPLXX_KEY_CONTROL( 'M' ) ) };
/*! \brief List of built-in actions that act upon user input.
*/
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,
@ -155,19 +157,29 @@ typedef enum {
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,
REPLXX_ACTION_HISTORY_PREVIOUS,
REPLXX_ACTION_LINE_NEXT,
REPLXX_ACTION_LINE_PREVIOUS,
REPLXX_ACTION_HISTORY_MOVE_NEXT,
REPLXX_ACTION_HISTORY_MOVE_PREVIOUS,
REPLXX_ACTION_HISTORY_FIRST,
REPLXX_ACTION_HISTORY_LAST,
REPLXX_ACTION_HISTORY_RESTORE,
REPLXX_ACTION_HISTORY_RESTORE_CURRENT,
REPLXX_ACTION_HISTORY_INCREMENTAL_SEARCH,
REPLXX_ACTION_HISTORY_SEEDED_INCREMENTAL_SEARCH,
REPLXX_ACTION_HISTORY_COMMON_PREFIX_SEARCH,
REPLXX_ACTION_HINT_NEXT,
REPLXX_ACTION_HINT_PREVIOUS,
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
@ -381,6 +393,12 @@ REPLXX_IMPEXP void replxx_get_state( Replxx*, ReplxxState* state );
*/
REPLXX_IMPEXP void replxx_set_state( Replxx*, ReplxxState* state );
/*! \brief Enable/disable case insensitive history search and completion.
*
* \param val - if set to non-zero then history search and completion will be case insensitive.
*/
REPLXX_IMPEXP void replxx_set_ignore_case( Replxx*, int val );
/*! \brief Print formatted string to standard output.
*
* This function ensures proper handling of ANSI escape sequences
@ -400,6 +418,14 @@ REPLXX_IMPEXP int replxx_print( Replxx*, char const* fmt, ... );
*/
REPLXX_IMPEXP int replxx_write( Replxx*, char const* str, int length );
/*! \brief Asynchronously change the prompt while replxx_input() call is in efect.
*
* Can be used to change the prompt from callbacks or other threads.
*
* \param prompt - The prompt string to change to.
*/
REPLXX_IMPEXP void replxx_set_prompt( Replxx*, const char* prompt );
/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
@ -428,8 +454,8 @@ REPLXX_IMPEXP void replxx_bind_key( Replxx*, int code, key_press_handler_t handl
*
* 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".
* is \e REPLXX_ACTION_LINE_PREVIOUS so action name to be used in this
* interface for the same effect is "line_previous".
*
* \param code - handle this key-press event with following handler.
* \param actionName - name of internal action to be invoked on key press.
@ -505,6 +531,12 @@ REPLXX_IMPEXP void replxx_set_unique_history( Replxx*, int val );
*/
REPLXX_IMPEXP void replxx_set_no_color( Replxx*, int val );
/*! \brief Enable/disable (prompt width) indent for multiline entry.
*
* \param val - if set to non-zero then multiline indent will be enabled.
*/
REPLXX_IMPEXP void replxx_set_indent_multiline( Replxx*, int val );
/*! \brief Set maximum number of entries in history list.
*/
REPLXX_IMPEXP void replxx_set_max_history_size( Replxx*, int len );
@ -559,6 +591,53 @@ 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* );
/*! \brief Combine two color definitions to get encompassing color definition.
*
* To be used only for combining foreground and background colors.
*
* \param color1 - first input color.
* \param color2 - second input color.
* \return A new color definition that represent combined input colors.
*/
ReplxxColor replxx_color_combine( ReplxxColor color1, ReplxxColor color2 );
/*! \brief Transform foreground color definition into a background color definition.
*
* \param color - an input foreground color definition.
* \return A background color definition that is a transformed input \e color.
*/
ReplxxColor replxx_color_bg( ReplxxColor color );
/*! \brief Add `bold` attribute to color definition.
*
* \param color - an input color definition.
* \return A new color definition with bold attribute set.
*/
ReplxxColor replxx_color_bold( ReplxxColor color );
/*! \brief Add `underline` attribute to color definition.
*
* \param color - an input color definition.
* \return A new color definition with underline attribute set.
*/
ReplxxColor replxx_color_underline( ReplxxColor color );
/*! \brief Create a new grayscale color of given brightness level.
*
* \param level - a brightness level for new color, must be between 0 (darkest) and 23 (brightest).
* \return A new grayscale color of a given brightest \e level.
*/
ReplxxColor replxx_color_grayscale( int level );
/*! \brief Create a new color in 6×6×6 RGB color space from base component levels.
*
* \param red - a red (of RGB) component level, must be 0 and 5.
* \param green - a green (of RGB) component level, must be 0 and 5.
* \param blue - a blue (of RGB) component level, must be 0 and 5.
* \return A new color in 6×6×6 RGB color space.
*/
ReplxxColor replxx_color_rgb666( int red, int green, int blue );
#ifdef __cplusplus
}
#endif

117
third-party/replxx/include/replxx.hxx generated vendored
View file

@ -35,6 +35,7 @@
#include <vector>
#include <string>
#include <functional>
#include <iosfwd>
/*
* For use in Windows DLLs:
@ -59,6 +60,11 @@ enum { ERROR_BB1CA97EC761FC37101737BA0AA2E7C5 = ERROR };
#undef ERROR
enum { ERROR = ERROR_BB1CA97EC761FC37101737BA0AA2E7C5 };
#endif
#ifdef ABORT
enum { ABORT_8D12A2CA7E5A64036D7251A3EDA51A38 = ABORT };
#undef ABORT
enum { ABORT = ABORT_8D12A2CA7E5A64036D7251A3EDA51A38 };
#endif
#ifdef DELETE
enum { DELETE_32F68A60CEF40FAEDBC6AF20298C1A1E = DELETE };
#undef DELETE
@ -69,7 +75,7 @@ namespace replxx {
class REPLXX_IMPEXP Replxx {
public:
enum class Color {
enum class Color : int {
BLACK = 0,
RED = 1,
GREEN = 2,
@ -86,9 +92,7 @@ public:
BRIGHTMAGENTA = 13,
BRIGHTCYAN = 14,
WHITE = 15,
NORMAL = LIGHTGRAY,
DEFAULT = -1,
ERROR = -2
DEFAULT = 1u << 16u
};
struct KEY {
static char32_t const BASE = 0x0010ffff + 1;
@ -145,17 +149,21 @@ public:
static char32_t const BACKSPACE = 'H' | BASE_CONTROL;
static char32_t const TAB = 'I' | BASE_CONTROL;
static char32_t const ENTER = 'M' | BASE_CONTROL;
static char32_t const ABORT = 'C' | BASE_CONTROL | BASE_META;
};
/*! \brief List of built-in actions that act upon user input.
*/
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,
@ -164,19 +172,29 @@ public:
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,
LINE_NEXT,
LINE_PREVIOUS,
HISTORY_NEXT,
HISTORY_PREVIOUS,
HISTORY_FIRST,
HISTORY_LAST,
HISTORY_RESTORE,
HISTORY_RESTORE_CURRENT,
HISTORY_INCREMENTAL_SEARCH,
HISTORY_SEEDED_INCREMENTAL_SEARCH,
HISTORY_COMMON_PREFIX_SEARCH,
HINT_NEXT,
HINT_PREVIOUS,
CAPITALIZE_WORD,
LOWERCASE_WORD,
UPPERCASE_WORD,
CAPITALIZE_SUBWORD,
LOWERCASE_SUBWORD,
UPPERCASE_SUBWORD,
TRANSPOSE_CHARACTERS,
TOGGLE_OVERWRITE_MODE,
#ifndef _WIN32
@ -308,7 +326,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.
@ -426,6 +444,12 @@ public:
*/
void set_state( State const& state );
/*! \brief Enable/disable case insensitive history search and completion.
*
* \param val - if set to non-zero then history search and completion will be case insensitive.
*/
void set_ignore_case( bool val );
/*! \brief Print formatted string to standard output.
*
* This function ensures proper handling of ANSI escape sequences
@ -445,6 +469,14 @@ public:
*/
void write( char const* str, int length );
/*! \brief Asynchronously change the prompt while replxx_input() call is in efect.
*
* Can be used to change the prompt from callbacks or other threads.
*
* \param prompt - The prompt string to change to.
*/
void set_prompt( std::string prompt );
/*! \brief Schedule an emulated key press event.
*
* \param code - key press code to be emulated.
@ -472,8 +504,8 @@ public:
*
* 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".
* is \e Replxx::ACTION::LINE_PREVIOUS so action name to be used in this
* interface for the same effect is "line_previous".
*
* \param code - handle this key-press event with following handler.
* \param actionName - name of internal action to be invoked on key press.
@ -510,6 +542,11 @@ public:
*/
bool history_save( std::string const& filename );
/*!
* \copydoc history_save
*/
void history_save( std::ostream& out );
/*! \brief Load REPL's history from given file.
*
* \param filename - a path to the file which contains REPL's history that should be loaded.
@ -517,6 +554,11 @@ public:
*/
bool history_load( std::string const& filename );
/*!
* \copydoc history_load
*/
void history_load( std::istream& in );
/*! \brief Clear REPL's in-memory history.
*/
void history_clear( void );
@ -588,6 +630,12 @@ public:
*/
void set_no_color( bool val );
/*! \brief Enable/disable (prompt width) indent for multiline entry.
*
* \param val - if set to true then multiline indent will be enabled.
*/
void set_indent_multiline( bool val );
/*! \brief Set maximum number of entries in history list.
*/
void set_max_history_size( int len );
@ -601,6 +649,61 @@ private:
Replxx& operator = ( Replxx const& ) = delete;
};
/*! \brief Color definition related helper function.
*
* To be used to leverage 256 color terminal capabilities.
*/
namespace color {
/*! \brief Combine two color definitions to get encompassing color definition.
*
* To be used only for combining foreground and background colors.
*
* \param color1 - first input color.
* \param color2 - second input color.
* \return A new color definition that represent combined input colors.
*/
Replxx::Color operator | ( Replxx::Color color1, Replxx::Color color2 );
/*! \brief Transform foreground color definition into a background color definition.
*
* \param color - an input foreground color definition.
* \return A background color definition that is a transformed input \e color.
*/
Replxx::Color bg( Replxx::Color color );
/*! \brief Add `bold` attribute to color definition.
*
* \param color - an input color definition.
* \return A new color definition with bold attribute set.
*/
Replxx::Color bold( Replxx::Color color );
/*! \brief Add `underline` attribute to color definition.
*
* \param color - an input color definition.
* \return A new color definition with underline attribute set.
*/
Replxx::Color underline( Replxx::Color color );
/*! \brief Create a new grayscale color of given brightness level.
*
* \param level - a brightness level for new color, must be between 0 (darkest) and 23 (brightest).
* \return A new grayscale color of a given brightest \e level.
*/
Replxx::Color grayscale( int level );
/*! \brief Create a new color in 6×6×6 RGB color space from base component levels.
*
* \param red - a red (of RGB) component level, must be 0 and 5.
* \param green - a green (of RGB) component level, must be 0 and 5.
* \param blue - a blue (of RGB) component level, must be 0 and 5.
* \return A new color in 6×6×6 RGB color space.
*/
Replxx::Color rgb666( int red, int green, int blue );
}
}
#endif /* HAVE_REPLXX_HXX_INCLUDED */

View file

@ -20,11 +20,6 @@ void to_lower( std::string& s_ ) {
transform( s_.begin(), s_.end(), s_.begin(), static_cast<int(*)(int)>( &tolower ) );
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
bool is_8bit_encoding( void ) {
bool is8BitEncoding( false );
string origLC( setlocale( LC_CTYPE, nullptr ) );
@ -42,10 +37,6 @@ bool is_8bit_encoding( void ) {
return ( is8BitEncoding );
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
bool is8BitEncoding( is_8bit_encoding() );
}

76
third-party/replxx/src/history.cxx generated vendored
View file

@ -1,6 +1,8 @@
#include <algorithm>
#include <memory>
#include <fstream>
#include <ostream>
#include <istream>
#include <cstring>
#ifndef _WIN32
@ -22,6 +24,7 @@ namespace {
void delete_ReplxxHistoryScanImpl( Replxx::HistoryScanImpl* impl_ ) {
delete impl_;
}
static int const ETB = 0x17;
}
static int const REPLXX_DEFAULT_HISTORY_MAX_LEN( 1000 );
@ -130,7 +133,12 @@ bool History::save( std::string const& filename, bool sync_ ) {
_entries = entries;
reset_iters();
}
do_load( filename );
/* scope for ifstream object auto-close */ {
ifstream histFile( filename );
if ( histFile ) {
do_load( histFile );
}
}
sort();
remove_duplicates();
trim_to_max_size();
@ -142,13 +150,7 @@ bool History::save( std::string const& filename, bool sync_ ) {
umask( old_umask );
chmod( filename.c_str(), S_IRUSR | S_IWUSR );
#endif
Utf8String utf8;
for ( Entry const& h : _entries ) {
if ( ! h.text().is_empty() ) {
utf8.assign( h.text() );
histFile << "### " << h.timestamp() << "\n" << utf8.get() << endl;
}
}
save( histFile );
if ( ! sync_ ) {
_entries = std::move( entries );
_locations = std::move( locations );
@ -157,6 +159,20 @@ bool History::save( std::string const& filename, bool sync_ ) {
return ( true );
}
void History::save( std::ostream& histFile ) {
Utf8String utf8;
UnicodeString us;
for ( Entry& h : _entries ) {
h.reset_scratch();
if ( ! h.text().is_empty() ) {
us.assign( h.text() );
std::replace( us.begin(), us.end(), char32_t( '\n' ), char32_t( ETB ) );
utf8.assign( us );
histFile << "### " << h.timestamp() << "\n" << utf8.get() << endl;
}
}
}
namespace {
bool is_timestamp( std::string const& s ) {
@ -179,13 +195,10 @@ bool is_timestamp( std::string const& s ) {
}
bool History::do_load( std::string const& filename ) {
ifstream histFile( filename );
if ( ! histFile ) {
return ( false );
}
void History::do_load( std::istream& histFile ) {
string line;
string when( "0000-00-00 00:00:00.000" );
UnicodeString us;
while ( getline( histFile, line ).good() ) {
string::size_type eol( line.find_first_of( "\r\n" ) );
if ( eol != string::npos ) {
@ -196,21 +209,31 @@ bool History::do_load( std::string const& filename ) {
continue;
}
if ( ! line.empty() ) {
_entries.emplace_back( when, UnicodeString( line ) );
us.assign( line );
std::replace( us.begin(), us.end(), char32_t( ETB ), char32_t( '\n' ) );
_entries.emplace_back( when, us );
}
}
return ( true );
}
bool History::load( std::string const& filename ) {
ifstream histFile( filename );
if ( ! histFile ) {
clear();
return false;
}
load(histFile);
return true;
}
void History::load( std::istream& histFile ) {
clear();
bool success( do_load( filename ) );
do_load( histFile );
sort();
remove_duplicates();
trim_to_max_size();
_previous = _current = last();
_yankPos = _entries.end();
return ( success );
}
void History::sort( void ) {
@ -281,11 +304,12 @@ void History::restore_pos( void ) {
_current = _previous;
}
bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_ ) {
bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize_, bool back_, bool ignoreCase ) {
int step( back_ ? -1 : 1 );
entries_t::const_iterator it( moved( _current, step, true ) );
entries_t::iterator it( moved( _current, step, true ) );
bool lowerCaseContext( std::none_of( prefix_.begin(), prefix_.end(), []( char32_t x ) { return iswupper( static_cast<wint_t>( x ) ); } ) );
while ( it != _current ) {
if ( it->text().starts_with( prefix_.begin(), prefix_.begin() + prefixSize_ ) ) {
if ( it->text().starts_with( prefix_.begin(), prefix_.begin() + prefixSize_, ignoreCase && lowerCaseContext ? case_insensitive_equal : case_sensitive_equal ) ) {
_current = it;
commit_index();
return ( true );
@ -295,7 +319,7 @@ bool History::common_prefix_search( UnicodeString const& prefix_, int prefixSize
return ( false );
}
bool History::move( entries_t::const_iterator& it_, int by_, bool wrapped_ ) const {
bool History::move( entries_t::iterator& it_, int by_, bool wrapped_ ) {
if ( by_ > 0 ) {
for ( int i( 0 ); i < by_; ++ i ) {
++ it_;
@ -321,12 +345,12 @@ bool History::move( entries_t::const_iterator& it_, int by_, bool wrapped_ ) con
return ( true );
}
History::entries_t::const_iterator History::moved( entries_t::const_iterator it_, int by_, bool wrapped_ ) const {
History::entries_t::iterator History::moved( entries_t::iterator it_, int by_, bool wrapped_ ) {
move( it_, by_, wrapped_ );
return ( it_ );
}
void History::erase( entries_t::const_iterator it_ ) {
void History::erase( entries_t::iterator it_ ) {
bool invalidated( it_ == _current );
_locations.erase( it_->text() );
it_ = _entries.erase( it_ );
@ -364,6 +388,7 @@ void History::remove_duplicates( void ) {
_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 ) {
it->reset_scratch();
locations_insertion_result_t locationsInsertionResult( _locations.insert( make_pair( it->text(), it ) ) );
if ( ! locationsInsertionResult.second ) {
_entries.erase( locationsInsertionResult.first->second );
@ -382,14 +407,15 @@ void History::update_last( UnicodeString const& line_ ) {
}
void History::drop_last( void ) {
reset_current_scratch();
erase( last() );
}
bool History::is_last( void ) const {
bool History::is_last( void ) {
return ( _current == last() );
}
History::entries_t::const_iterator History::last( void ) const {
History::entries_t::iterator History::last( void ) {
return ( moved( _entries.end(), -1 ) );
}

45
third-party/replxx/src/history.hxx generated vendored
View file

@ -33,28 +33,36 @@ public:
class Entry {
std::string _timestamp;
UnicodeString _text;
UnicodeString _scratch;
public:
Entry( std::string const& timestamp_, UnicodeString const& text_ )
: _timestamp( timestamp_ )
, _text( text_ ) {
, _text( text_ )
, _scratch( text_ ) {
}
std::string const& timestamp( void ) const {
return ( _timestamp );
}
UnicodeString const& text( void ) const {
return ( _text );
return ( _scratch );
}
void set_scratch( UnicodeString const& s ) {
_scratch = s;
}
void reset_scratch( void ) {
_scratch = _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;
typedef std::unordered_map<UnicodeString, entries_t::iterator> locations_t;
private:
entries_t _entries;
locations_t _locations;
int _maxSize;
entries_t::const_iterator _current;
entries_t::iterator _current;
entries_t::const_iterator _yankPos;
/*
* _previous and _recallMostRecent are used to allow
@ -64,14 +72,16 @@ private:
* Special meaning is: a down arrow shall jump to the line one
* after previously accepted from history.
*/
entries_t::const_iterator _previous;
entries_t::iterator _previous;
bool _recallMostRecent;
bool _unique;
public:
History( void );
void add( UnicodeString const& line, std::string const& when = now_ms_str() );
bool save( std::string const& filename, bool );
void save( std::ostream& histFile );
bool load( std::string const& filename );
void load( std::istream& histFile );
void clear( void );
void set_max_size( int len );
void set_unique( bool unique_ ) {
@ -92,8 +102,19 @@ public:
}
void update_last( UnicodeString const& );
void drop_last( void );
bool is_last( void ) const;
bool is_last( void );
bool move( bool );
void set_current_scratch( UnicodeString const& s ) {
_current->set_scratch( s );
}
void reset_scratches( void ) {
for ( Entry& entry : _entries ) {
entry.reset_scratch();
}
}
void reset_current_scratch( void ) {
_current->reset_scratch();
}
UnicodeString const& current( void ) const {
return ( _current->text() );
}
@ -101,7 +122,7 @@ public:
return ( _yankPos->text() );
}
void jump( bool, bool = true );
bool common_prefix_search( UnicodeString const&, int, bool );
bool common_prefix_search( UnicodeString const&, int, bool, bool );
int size( void ) const {
return ( static_cast<int>( _entries.size() ) );
}
@ -111,14 +132,14 @@ public:
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 );
bool move( entries_t::iterator&, int, bool = false );
entries_t::iterator moved( entries_t::iterator, int, bool = false );
void erase( entries_t::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 do_load( std::istream& );
entries_t::iterator last( void );
void sort( void );
void reset_iters( void );
};

104
third-party/replxx/src/prompt.cxx generated vendored
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,77 +39,30 @@ 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;
_previousInputLen = 0;
_previousLen = 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::iterator out( _text.begin() );
UnicodeString::const_iterator in( _text.begin() );
int len = 0;
int x = 0;
int renderedSize( 0 );
_characterCount = virtual_render( _text.get(), _text.length(), x, _extraLines, _screenColumns, 0, _text.get(), &renderedSize );
_lastLinePosition = _characterCount - x;
_text.erase( renderedSize, _text.length() - renderedSize );
bool const strip = !tty::out;
_cursorRowOffset += _extraLines;
}
while (in != text_.end()) {
char32_t c = *in;
if ('\n' == c || !is_control_code(c)) {
*out = c;
++out;
++in;
++len;
if ('\n' == c || ++x >= _screenColumns) {
x = 0;
++_extraLines;
_lastLinePosition = len;
}
} else if (c == '\x1b') {
if ( strip ) {
// jump over control chars
++in;
if (*in == '[') {
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
++in;
}
if (*in == 'm') {
++in;
}
}
} else {
// copy control chars
*out = *in;
++out;
++in;
if (*in == '[') {
*out = *in;
++out;
++in;
while ( ( in != text_.end() ) && ( ( *in == ';' ) || ( ( ( *in >= '0' ) && ( *in <= '9' ) ) ) ) ) {
*out = *in;
++out;
++in;
}
if (*in == 'm') {
*out = *in;
++out;
++in;
}
}
}
} else {
++in;
}
}
_characterCount = len;
_byteCount = static_cast<int>(out - _text.begin());
_indentation = len - _lastLinePosition;
_cursorRowOffset = _extraLines;
int Prompt::indentation() const {
return _characterCount - _lastLinePosition;
}
// Used with DynamicPrompt (history search)
@ -123,31 +75,15 @@ 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();
}
}

21
third-party/replxx/src/prompt.hxx generated vendored
View file

@ -8,28 +8,27 @@
namespace replxx {
class Prompt { // a convenience struct for grouping prompt info
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 _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
UnicodeString _text; // our copy of the prompt text, edited
int _characterCount{0}; // visible characters in _text
int _extraLines{0}; // extra lines (beyond 1) occupied by prompt
int _lastLinePosition{0}; // index into _text where last line begins
int _cursorRowOffset{0}; // where the cursor is relative to the start of the prompt
private:
int _screenColumns; // width of screen in columns [cache]
int _screenColumns{0}; // 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;
};
// changing prompt for "(reverse-i-search)`text':" etc.

97
third-party/replxx/src/replxx.cxx generated vendored
View file

@ -162,10 +162,18 @@ bool Replxx::history_save( std::string const& filename ) {
return ( _impl->history_save( filename ) );
}
void Replxx::history_save( std::ostream& out ) {
_impl->history_save( out );
}
bool Replxx::history_load( std::string const& filename ) {
return ( _impl->history_load( filename ) );
}
void Replxx::history_load( std::istream& in ) {
_impl->history_load( in );
}
void Replxx::history_clear( void ) {
_impl->history_clear();
}
@ -222,6 +230,10 @@ void Replxx::set_no_color( bool val ) {
_impl->set_no_color( val );
}
void Replxx::set_indent_multiline( bool val ) {
_impl->set_indent_multiline( val );
}
void Replxx::set_max_history_size( int len ) {
_impl->set_max_history_size( len );
}
@ -254,6 +266,10 @@ void Replxx::set_state( Replxx::State const& state_ ) {
_impl->set_state( state_ );
}
void Replxx::set_ignore_case( bool val ) {
_impl->set_ignore_case( val );
}
int Replxx::install_window_change_handler( void ) {
return ( _impl->install_window_change_handler() );
}
@ -282,6 +298,45 @@ void Replxx::write( char const* str, int length ) {
return ( _impl->print( str, length ) );
}
void Replxx::set_prompt( std::string prompt ) {
return ( _impl->set_prompt( std::move( prompt ) ) );
}
namespace color {
Replxx::Color operator | ( Replxx::Color color1_, Replxx::Color color2_ ) {
return static_cast<Replxx::Color>( static_cast<int unsigned>( color1_ ) | static_cast<int unsigned>( color2_ ) );
}
Replxx::Color bg( Replxx::Color color_ ) {
return static_cast<Replxx::Color>( ( ( static_cast<int unsigned>( color_ ) & 0xFFu ) << 8 ) | color::BACKGROUND_COLOR_SET );
}
Replxx::Color bold( Replxx::Color color_ ) {
return static_cast<Replxx::Color>( static_cast<int unsigned>( color_ ) | color::BOLD );
}
Replxx::Color underline( Replxx::Color color_ ) {
return static_cast<Replxx::Color>( static_cast<int unsigned>( color_ ) | color::UNDERLINE );
}
Replxx::Color grayscale( int level_ ) {
assert( ( level_ >= 0 ) && ( level_ < 24 ) );
return static_cast<Replxx::Color>( abs( level_ ) % 24 + static_cast<int unsigned>( color::GRAYSCALE ) );
}
Replxx::Color rgb666( int red_, int green_, int blue_ ) {
assert( ( red_ >= 0 ) && ( red_ < 6 ) && ( green_ >= 0 ) && ( green_ < 6 ) && ( blue_ >= 0 ) && ( blue_ < 6 ) );
return static_cast<Replxx::Color>(
( abs( red_ ) % 6 ) * 36
+ ( abs( green_ ) % 6 ) * 6
+ ( abs( blue_ ) % 6 )
+ static_cast<int unsigned>( color::RGB666 )
);
}
}
}
::Replxx* replxx_init() {
@ -339,6 +394,11 @@ void replxx_set_state( ::Replxx* replxx_, ReplxxState* state ) {
replxx->set_state( replxx::Replxx::State( state->text, state->cursorPosition ) );
}
void replxx_set_ignore_case( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_ignore_case( val );
}
/**
* replxx_set_preload_buffer provides text to be inserted into the command buffer
*
@ -395,6 +455,11 @@ int replxx_write( ::Replxx* replxx_, char const* str, int length ) {
return static_cast<int>( length );
}
void replxx_set_prompt( ::Replxx* replxx_, const char* prompt ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_prompt( prompt );
}
struct replxx_completions {
replxx::Replxx::completions_t data;
};
@ -478,7 +543,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 ) );
}
@ -527,6 +592,11 @@ void replxx_set_no_color( ::Replxx* replxx_, int val ) {
replxx->set_no_color( val ? true : false );
}
void replxx_set_indent_multiline( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_indent_multiline( val ? true : false );
}
void replxx_set_beep_on_ambiguous_completion( ::Replxx* replxx_, int val ) {
replxx::Replxx::ReplxxImpl* replxx( reinterpret_cast<replxx::Replxx::ReplxxImpl*>( replxx_ ) );
replxx->set_beep_on_ambiguous_completion( val ? true : false );
@ -646,3 +716,28 @@ int replxx_install_window_change_handler( ::Replxx* replxx_ ) {
return ( replxx->install_window_change_handler() );
}
using namespace replxx::color;
ReplxxColor replxx_color_combine( ReplxxColor color1_, ReplxxColor color2_ ) {
return static_cast<ReplxxColor>( static_cast<replxx::Replxx::Color>( color1_ ) | static_cast<replxx::Replxx::Color>( color2_ ) );
}
ReplxxColor replxx_color_bg( ReplxxColor color_ ) {
return static_cast<ReplxxColor>( color::bg( static_cast<replxx::Replxx::Color>( color_ ) ) );
}
ReplxxColor replxx_color_bold( ReplxxColor color_ ) {
return static_cast<ReplxxColor>( color::bold( static_cast<replxx::Replxx::Color>( color_ ) ) );
}
ReplxxColor replxx_color_underline( ReplxxColor color_ ) {
return static_cast<ReplxxColor>( color::underline( static_cast<replxx::Replxx::Color>( color_ ) ) );
}
ReplxxColor replxx_color_grayscale( int level_ ) {
return static_cast<ReplxxColor>( color::grayscale( level_ ) );
}
ReplxxColor replxx_color_rgb666( int r_, int g_, int b_ ) {
return static_cast<ReplxxColor>( color::rgb666( r_, g_, b_ ) );
}

1025
third-party/replxx/src/replxx_impl.cxx generated vendored

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,7 @@
#include <thread>
#include <mutex>
#include <chrono>
#include <iosfwd>
#include "replxx.hxx"
#include "history.hxx"
@ -74,10 +75,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;
@ -92,22 +93,22 @@ public:
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 HISTORY_RECALL_MOST_RECENT = 32;
static action_trait_t const DONT_RESET_HIST_YANK_INDEX = 64;
static action_trait_t const NOOP = 0;
static action_trait_t const WANT_REFRESH = 1;
static action_trait_t const MOVE_CURSOR = 2;
static action_trait_t const RESET_KILL_ACTION = 4;
static action_trait_t const SET_KILL_ACTION = 8;
static action_trait_t const DONT_RESET_PREFIX = 16;
static action_trait_t const DONT_RESET_COMPLETIONS = 32;
static action_trait_t const HISTORY_RECALL_MOST_RECENT = 64;
static action_trait_t const DONT_RESET_HIST_YANK_INDEX = 128;
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;
@ -117,7 +118,8 @@ private:
int _lastYankSize;
int _maxHintRows;
int _hintDelay;
std::string _breakChars;
std::string _wordBreakChars;
std::string _subwordBreakChars;
int _completionCountCutoff;
bool _overwrite;
bool _doubleTabCompletion;
@ -126,6 +128,7 @@ private:
bool _immediateCompletion;
bool _bracketedPaste;
bool _noColor;
bool _indentMultiline;
named_actions_t _namedActions;
key_press_handlers_t _keyPressHandlers;
Terminal _terminal;
@ -137,6 +140,8 @@ private:
Replxx::hint_callback_t _hintCallback;
key_presses_t _keyPresses;
messages_t _messages;
std::string _asyncPrompt;
bool _updatePrompt;
completions_t _completions;
int _completionContextLength;
int _completionSelection;
@ -148,6 +153,10 @@ private:
hints_t _hintsCache;
int _hintContextLenght;
Utf8String _hintSeed;
bool _hasNewlines;
int _oldPos;
bool _moveCursor;
bool _ignoreCase;
mutable std::mutex _mutex;
public:
ReplxxImpl( FILE*, FILE*, FILE* );
@ -160,12 +169,15 @@ public:
void history_add( std::string const& line );
bool history_sync( std::string const& filename );
bool history_save( std::string const& filename );
void history_save( std::ostream& out );
bool history_load( std::string const& filename );
void history_load( std::istream& in );
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 );
@ -174,12 +186,14 @@ public:
void set_immediate_completion( bool val );
void set_unique_history( bool );
void set_no_color( bool val );
void set_indent_multiline( bool val );
void set_max_history_size( int len );
void set_completion_count_cutoff( int len );
int install_window_change_handler( void );
void enable_bracketed_paste( void );
void disable_bracketed_paste( void );
void print( char const*, int );
void set_prompt( std::string prompt );
Replxx::ACTION_RESULT clear_screen( char32_t );
void emulate_key_press( char32_t );
Replxx::ACTION_RESULT invoke( Replxx::ACTION, char32_t );
@ -187,6 +201,7 @@ public:
void bind_key_internal( char32_t, char const* );
Replxx::State get_state( void ) const;
void set_state( Replxx::State const& );
void set_ignore_case( bool val );
private:
ReplxxImpl( ReplxxImpl const& ) = delete;
ReplxxImpl& operator = ( ReplxxImpl const& ) = delete;
@ -195,13 +210,18 @@ 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 );
@ -209,8 +229,11 @@ private:
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 );
@ -218,11 +241,15 @@ private:
Replxx::ACTION_RESULT delete_character( char32_t );
Replxx::ACTION_RESULT backspace_character( char32_t );
Replxx::ACTION_RESULT commit_line( char32_t );
Replxx::ACTION_RESULT line_next( char32_t );
Replxx::ACTION_RESULT line_previous( char32_t );
Replxx::ACTION_RESULT history_next( char32_t );
Replxx::ACTION_RESULT history_previous( char32_t );
Replxx::ACTION_RESULT history_move( bool );
Replxx::ACTION_RESULT history_first( char32_t );
Replxx::ACTION_RESULT history_last( char32_t );
Replxx::ACTION_RESULT history_restore( char32_t );
Replxx::ACTION_RESULT history_restore_current( char32_t );
Replxx::ACTION_RESULT history_jump( bool );
Replxx::ACTION_RESULT hint_next( char32_t );
Replxx::ACTION_RESULT hint_previous( char32_t );
@ -246,15 +273,22 @@ private:
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 move_cursor( void );
void indent( void );
int virtual_render( char32_t const*, int, int&, int&, Prompt const* = nullptr );
void render( char32_t );
void render( HINT_ACTION );
int handle_hints( HINT_ACTION );
void handle_hints( HINT_ACTION );
void set_color( Replxx::Color );
int context_length( void );
int prev_newline_position( int ) const;
int next_newline_position( int ) const;
int pos_in_line( void ) const;
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 dynamic_refresh(Prompt& oldPrompt, Prompt& newPrompt, char32_t* buf32, int len, int pos);
char const* finalize_input( char const* );
void clear_self_to_end_of_screen( Prompt const* = nullptr );
typedef struct {

199
third-party/replxx/src/terminal.cxx generated vendored
View file

@ -95,6 +95,7 @@ Terminal::Terminal( void )
, _empty()
#else
: _origTermios()
, _rawModeTermios()
, _interrupt()
#endif
, _rawMode( false )
@ -126,11 +127,13 @@ void Terminal::write32( char32_t const* text32, int len32 ) {
void Terminal::write8( char const* data_, int size_ ) {
#ifdef _WIN32
if ( ! _rawMode ) {
bool temporarilyEnabled( false );
if ( _consoleOut == INVALID_HANDLE_VALUE ) {
enable_out();
temporarilyEnabled = true;
}
int nWritten( win_write( _consoleOut, _autoEscape, data_, size_ ) );
if ( ! _rawMode ) {
if ( temporarilyEnabled ) {
disable_out();
}
#else
@ -179,8 +182,8 @@ inline int notty( void ) {
void Terminal::enable_out( void ) {
#ifdef _WIN32
_consoleOut = GetStdHandle( STD_OUTPUT_HANDLE );
SetConsoleOutputCP( 65001 );
_consoleOut = GetStdHandle( STD_OUTPUT_HANDLE );
GetConsoleMode( _consoleOut, &_origOutMode );
_autoEscape = SetConsoleMode( _consoleOut, _origOutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) != 0;
#endif
@ -206,70 +209,88 @@ void Terminal::disable_bracketed_paste( void ) {
}
int Terminal::enable_raw_mode( void ) {
if ( ! _rawMode ) {
#ifdef _WIN32
_consoleIn = GetStdHandle( STD_INPUT_HANDLE );
SetConsoleCP( 65001 );
GetConsoleMode( _consoleIn, &_origInMode );
SetConsoleMode(
_consoleIn,
_origInMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT )
);
enable_out();
#else
struct termios raw;
if ( ! tty::in ) {
return ( notty() );
}
if ( tcgetattr( 0, &_origTermios ) == -1 ) {
return ( notty() );
}
raw = _origTermios; /* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* output modes - disable post processing */
// this is wrong, we don't want raw output, it turns newlines into straight
// linefeeds
// raw.c_oflag &= ~(OPOST);
/* control modes - set 8 bit chars */
raw.c_cflag |= (CS8);
/* local modes - echoing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
raw.c_cc[VMIN] = 1;
raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
/* put terminal in raw mode after flushing */
if ( tcsetattr(0, TCSADRAIN, &raw) < 0 ) {
return ( notty() );
}
_terminal_ = this;
#endif
_rawMode = true;
if ( _rawMode ) {
return ( 0 );
}
#ifdef _WIN32
_consoleIn = GetStdHandle( STD_INPUT_HANDLE );
GetConsoleMode( _consoleIn, &_origInMode );
#else
if ( ! tty::in ) {
return ( notty() );
}
if ( tcgetattr( 0, &_origTermios ) == -1 ) {
return ( notty() );
}
_rawModeTermios = _origTermios; /* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
_rawModeTermios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* output modes - disable post processing */
// this is wrong, we don't want _rawModeTermios output, it turns newlines into straight
// linefeeds
// _rawModeTermios.c_oflag &= ~(OPOST);
/* control modes - set 8 bit chars */
_rawModeTermios.c_cflag |= (CS8);
/* local modes - echoing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
_rawModeTermios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
_rawModeTermios.c_cc[VMIN] = 1;
_rawModeTermios.c_cc[VTIME] = 0; /* 1 byte, no timer */
#endif
_rawMode = true;
if ( reset_raw_mode() < 0 ) {
_rawMode = false;
return ( notty() );
}
#ifndef _WIN32
_terminal_ = this;
#endif
return ( 0 );
}
void Terminal::disable_raw_mode(void) {
if ( _rawMode ) {
#ifdef _WIN32
disable_out();
SetConsoleMode( _consoleIn, _origInMode );
SetConsoleCP( _inputCodePage );
_consoleIn = INVALID_HANDLE_VALUE;
#else
_terminal_ = nullptr;
if ( tcsetattr( 0, TCSADRAIN, &_origTermios ) == -1 ) {
return;
}
#endif
_rawMode = false;
int Terminal::reset_raw_mode( void ) {
if ( ! _rawMode ) {
return ( -1 );
}
#ifdef _WIN32
SetConsoleMode(
_consoleIn,
( _origInMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT ) ) | ENABLE_QUICK_EDIT_MODE
);
SetConsoleCP( 65001 );
enable_out();
return ( 0 );
#else
/* put terminal in raw mode after flushing */
return ( tcsetattr( 0, TCSADRAIN, &_rawModeTermios ) );
#endif
}
void Terminal::disable_raw_mode(void) {
if ( ! _rawMode ) {
return;
}
#ifdef _WIN32
disable_out();
SetConsoleMode( _consoleIn, _origInMode );
SetConsoleCP( _inputCodePage );
_consoleIn = INVALID_HANDLE_VALUE;
#else
_terminal_ = nullptr;
if ( tcsetattr( 0, TCSADRAIN, &_origTermios ) == -1 ) {
return;
}
#endif
_rawMode = false;
return;
}
#ifndef _WIN32
@ -390,7 +411,10 @@ char32_t Terminal::read_char( void ) {
}
int key( rec.Event.KeyEvent.uChar.UnicodeChar );
if ( key == 0 ) {
switch (rec.Event.KeyEvent.wVirtualKeyCode) {
if ( rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED ) {
modifierKeys |= Replxx::KEY::BASE_SHIFT;
}
switch ( rec.Event.KeyEvent.wVirtualKeyCode ) {
case VK_LEFT:
return modifierKeys | Replxx::KEY::LEFT;
case VK_RIGHT:
@ -443,6 +467,9 @@ char32_t Terminal::read_char( void ) {
highSurrogate = key - 0xD800;
continue;
} else {
if ( ( key == 13 ) && ( rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED ) ) {
key = 10;
}
// we got a real character, return it
if ( ( key >= 0xDC00 ) && ( key <= 0xDFFF ) ) {
key -= 0xDC00;
@ -535,7 +562,7 @@ char32_t Terminal::read_char( void ) {
Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) {
#ifdef _WIN32
std::array<HANDLE,2> handles = { _consoleIn, _interrupt };
std::array<HANDLE, 2> handles = { _consoleIn, _interrupt };
while ( true ) {
DWORD event( WaitForMultipleObjects( static_cast<DWORD>( handles.size() ), handles.data(), false, timeout_ > 0 ? timeout_ : INFINITE ) );
switch ( event ) {
@ -552,22 +579,34 @@ Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) {
// read the event to unsignal the handle
ReadConsoleInputW( _consoleIn, &rec, 1, &count );
continue;
} else if (rec.EventType == KEY_EVENT) {
int key(rec.Event.KeyEvent.uChar.UnicodeChar);
if (key == 0) {
switch (rec.Event.KeyEvent.wVirtualKeyCode) {
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
} else if ( rec.EventType == KEY_EVENT ) {
int key( rec.Event.KeyEvent.uChar.UnicodeChar );
if ( key == 0 ) {
switch ( rec.Event.KeyEvent.wVirtualKeyCode ) {
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
case VK_F1:
case VK_F2:
case VK_F3:
case VK_F4:
case VK_F5:
case VK_F6:
case VK_F7:
case VK_F8:
case VK_F9:
case VK_F10:
case VK_F11:
case VK_F12:
break;
default:
ReadConsoleInputW(_consoleIn, &rec, 1, &count);
default:
ReadConsoleInputW( _consoleIn, &rec, 1, &count );
continue; // in raw mode, ReadConsoleInput shows shift, ctrl - ignore them
}
}
@ -665,7 +704,7 @@ void Terminal::clear_screen( CLEAR_SCREEN clearScreen_ ) {
_empty.resize( toWrite - 1, ' ' );
WriteConsoleA( consoleOut, _empty.data(), toWrite - 1, &nWritten, nullptr );
} else {
COORD scrollTarget = { 0, -inf.dwSize.Y };
COORD scrollTarget = { 0, static_cast<SHORT>( -inf.dwSize.Y ) };
CHAR_INFO fill{ TEXT( ' ' ), inf.wAttributes };
SMALL_RECT scrollRect = { 0, 0, inf.dwSize.X, inf.dwSize.Y };
ScrollConsoleScreenBuffer( consoleOut, &scrollRect, nullptr, scrollTarget, &fill );

View file

@ -38,6 +38,7 @@ private:
std::vector<char> _empty;
#else
struct termios _origTermios; /* in order to restore at exit */
struct termios _rawModeTermios; /* in order to reset raw mode after callbacks */
int _interrupt[2];
#endif
bool _rawMode; /* for destructor to check if restore is needed */
@ -57,6 +58,7 @@ public:
void enable_bracketed_paste( void );
void disable_bracketed_paste( void );
int enable_raw_mode(void);
int reset_raw_mode(void);
void disable_raw_mode(void);
char32_t read_char(void);
void clear_screen( CLEAR_SCREEN );

View file

@ -3,11 +3,21 @@
#include <vector>
#include <cstring>
#include <cwctype>
#include <cassert>
#include "conversion.hxx"
namespace replxx {
inline bool case_sensitive_equal( char32_t l, char32_t r ) {
return l == r;
}
inline bool case_insensitive_equal( char32_t l, char32_t r ) {
return towlower( static_cast<wint_t>( l ) ) == towlower( static_cast<wint_t>( r ) );
}
class UnicodeString {
public:
typedef std::vector<char32_t> data_buffer_t;
@ -25,6 +35,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 );
@ -87,6 +106,10 @@ public:
return ( _data != other_._data );
}
bool operator < ( UnicodeString const& other_ ) const {
return std::lexicographical_compare(begin(), end(), other_.begin(), other_.end());
}
UnicodeString& append( UnicodeString const& other ) {
_data.insert( _data.end(), other._data.begin(), other._data.end() );
return *this;
@ -137,11 +160,13 @@ public:
_data.clear();
}
const char32_t& operator[]( size_t pos ) const {
const char32_t& operator[]( int pos ) const {
assert( ( pos >= 0 ) && ( pos < static_cast<int>( _data.size() ) ) );
return _data[pos];
}
char32_t& operator[]( size_t pos ) {
char32_t& operator[]( int pos ) {
assert( ( pos >= 0 ) && ( pos < static_cast<int>( _data.size() ) ) );
return _data[pos];
}
@ -152,6 +177,14 @@ public:
);
}
template <class BinaryPredicate>
bool starts_with( data_buffer_t::const_iterator first_, data_buffer_t::const_iterator last_, BinaryPredicate&& pred ) const {
return (
( std::distance( first_, last_ ) <= length() )
&& ( std::equal( first_, last_, _data.begin(), std::forward<BinaryPredicate>( pred ) ) )
);
}
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 (
@ -183,6 +216,10 @@ public:
iterator end( void ) {
return ( _data.end() );
}
char32_t back( void ) const {
return ( _data.back() );
}
};
}

View file

@ -68,7 +68,13 @@ public:
}
bool operator != ( Utf8String const& other_ ) {
return ( ( other_._len != _len ) || ( memcmp( other_._data.get(), _data.get(), _len ) != 0 ) );
return (
( other_._len != _len )
|| (
( _len != 0 )
&& ( memcmp( other_._data.get(), _data.get(), _len ) != 0 )
)
);
}
private:

247
third-party/replxx/src/util.cxx generated vendored
View file

@ -5,149 +5,158 @@
#include <wctype.h>
#include "util.hxx"
#include "terminal.hxx"
#undef min
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
* @param x - initial x position (zero-based)
* @param y - initial y position (zero-based)
* @param screenColumns - screen column count
* @param charCount - character positions to advance
* @param xOut - returned x position (zero-based)
* @param yOut - returned y position (zero-based)
*/
void calculate_screen_position(
int x, int y, int screenColumns,
int charCount, int& xOut, int& yOut
) {
xOut = x;
yOut = y;
int charsRemaining = charCount;
while ( charsRemaining > 0 ) {
int charsThisRow = ( ( x + charsRemaining ) < screenColumns )
? charsRemaining
: screenColumns - x;
xOut = x + charsThisRow;
yOut = y;
charsRemaining -= charsThisRow;
x = 0;
++ y;
}
if ( xOut == screenColumns ) { // we have to special-case line wrap
xOut = 0;
++ yOut;
}
}
/**
* Calculate a column width using mk_wcswidth()
* @param buf32 - text to calculate
* @param len - length of text to calculate
*/
int calculate_displayed_length( char32_t const* buf32_, int size_ ) {
int len( 0 );
for ( int i( 0 ); i < size_; ++ i ) {
char32_t c( buf32_[i] );
int virtual_render( char32_t const* display_, int size_, int& x_, int& y_, int screenColumns_, int promptLen_, char32_t* rendered_, int* renderedSize_ ) {
char32_t* out( rendered_ );
int visibleCount( 0 );
auto render = [&rendered_, &renderedSize_, &out, &visibleCount]( char32_t c_, bool visible_, bool renderAttributes_ = true ) {
if ( rendered_ && renderedSize_ && renderAttributes_ ) {
*out = c_;
++ out;
if ( visible_ ) {
++ visibleCount;
}
}
};
bool wrapped( false );
auto advance_cursor = [&x_, &y_, &screenColumns_, &wrapped]( int by_ = 1 ) {
wrapped = false;
x_ += by_;
if ( x_ >= screenColumns_ ) {
x_ = 0;
++ y_;
wrapped = true;
}
};
bool const renderAttributes( !!tty::out );
int pos( 0 );
while ( pos < size_ ) {
char32_t c( display_[pos] );
if ( ( c == '\n' ) || ( c == '\r' ) ) {
render( c, true );
if ( ( c == '\n' ) && ! wrapped ) {
++ y_;
}
x_ = promptLen_;
++ pos;
continue;
}
if ( c == '\b' ) {
render( c, true );
-- x_;
if ( x_ < 0 ) {
x_ = screenColumns_ - 1;
-- y_;
}
++ pos;
continue;
}
if ( c == '\033' ) {
int escStart( i );
++ i;
if ( ( i < size_ ) && ( buf32_[i] != '[' ) ) {
i = escStart;
++ len;
render( c, false, renderAttributes );
++ pos;
if ( pos >= size_ ) {
advance_cursor( 2 );
continue;
}
++ i;
for ( ; i < size_; ++ i ) {
c = buf32_[i];
c = display_[pos];
if ( c != '[' ) {
advance_cursor( 2 );
continue;
}
render( c, false, renderAttributes );
++ pos;
if ( pos >= size_ ) {
advance_cursor( 3 );
continue;
}
int codeLen( 0 );
while ( pos < size_ ) {
c = display_[pos];
if ( ( c != ';' ) && ( ( c < '0' ) || ( c > '9' ) ) ) {
break;
}
render( c, false, renderAttributes );
++ codeLen;
++ pos;
}
if ( ( i < size_ ) && ( buf32_[i] == 'm' ) ) {
if ( pos >= size_ ) {
continue;
}
i = escStart;
len += 2;
} else if ( is_control_code( c ) ) {
len += 2;
} else {
int wcw( mk_wcwidth( c ) );
if ( wcw < 0 ) {
len = -1;
break;
c = display_[pos];
if ( c != 'm' ) {
advance_cursor( 3 + codeLen );
continue;
}
len += wcw;
render( c, false, renderAttributes );
++ pos;
continue;
}
if ( is_control_code( c ) ) {
render( c, true );
advance_cursor( 2 );
++ pos;
continue;
}
int wcw( mk_wcwidth( c ) );
if ( wcw < 0 ) {
break;
}
render( c, true );
advance_cursor( wcw );
++ pos;
}
return ( len );
if ( rendered_ && renderedSize_ ) {
*renderedSize_ = out - rendered_;
}
return ( visibleCount );
}
char const* ansi_color( Replxx::Color color_ ) {
static char const reset[] = "\033[0m";
static char const black[] = "\033[0;22;30m";
static char const red[] = "\033[0;22;31m";
static char const green[] = "\033[0;22;32m";
static char const brown[] = "\033[0;22;33m";
static char const blue[] = "\033[0;22;34m";
static char const magenta[] = "\033[0;22;35m";
static char const cyan[] = "\033[0;22;36m";
static char const lightgray[] = "\033[0;22;37m";
int unsigned code( static_cast<int unsigned>( color_ ) );
int unsigned fg( code & 0xFFu );
int unsigned bg( ( code >> 8 ) & 0xFFu );
char const* bold( ( code & color::BOLD ) != 0 ? ";1" : "" );
char const* underline = ( ( code & color::UNDERLINE ) != 0 ? ";4" : "" );
static int const MAX_COLOR_CODE_SIZE( 32 );
static char colorBuffer[MAX_COLOR_CODE_SIZE];
int pos( 0 );
if ( ( code & static_cast<int unsigned>( Replxx::Color::DEFAULT ) ) != 0 ) {
pos = snprintf( colorBuffer, MAX_COLOR_CODE_SIZE, "\033[0%s%sm", underline, bold );
} else if ( fg <= static_cast<int unsigned>( Replxx::Color::LIGHTGRAY ) ) {
pos = snprintf( colorBuffer, MAX_COLOR_CODE_SIZE, "\033[0;22;3%d%s%sm", fg, underline, bold );
} else if ( fg <= static_cast<int unsigned>( Replxx::Color::WHITE ) ) {
#ifdef _WIN32
static bool const has256colorDefault( true );
static bool const has256colorDefault( true );
#else
static bool const has256colorDefault( false );
static bool const has256colorDefault( false );
#endif
static char const* TERM( getenv( "TERM" ) );
static bool const has256color( TERM ? ( strstr( TERM, "256" ) != nullptr ) : has256colorDefault );
static char const* gray = has256color ? "\033[0;1;90m" : "\033[0;1;30m";
static char const* brightred = has256color ? "\033[0;1;91m" : "\033[0;1;31m";
static char const* brightgreen = has256color ? "\033[0;1;92m" : "\033[0;1;32m";
static char const* yellow = has256color ? "\033[0;1;93m" : "\033[0;1;33m";
static char const* brightblue = has256color ? "\033[0;1;94m" : "\033[0;1;34m";
static char const* brightmagenta = has256color ? "\033[0;1;95m" : "\033[0;1;35m";
static char const* brightcyan = has256color ? "\033[0;1;96m" : "\033[0;1;36m";
static char const* white = has256color ? "\033[0;1;97m" : "\033[0;1;37m";
static char const error[] = "\033[101;1;33m";
char const* code( reset );
switch ( color_ ) {
case Replxx::Color::BLACK: code = black; break;
case Replxx::Color::RED: code = red; break;
case Replxx::Color::GREEN: code = green; break;
case Replxx::Color::BROWN: code = brown; break;
case Replxx::Color::BLUE: code = blue; break;
case Replxx::Color::MAGENTA: code = magenta; break;
case Replxx::Color::CYAN: code = cyan; break;
case Replxx::Color::LIGHTGRAY: code = lightgray; break;
case Replxx::Color::GRAY: code = gray; break;
case Replxx::Color::BRIGHTRED: code = brightred; break;
case Replxx::Color::BRIGHTGREEN: code = brightgreen; break;
case Replxx::Color::YELLOW: code = yellow; break;
case Replxx::Color::BRIGHTBLUE: code = brightblue; break;
case Replxx::Color::BRIGHTMAGENTA: code = brightmagenta; break;
case Replxx::Color::BRIGHTCYAN: code = brightcyan; break;
case Replxx::Color::WHITE: code = white; break;
case Replxx::Color::ERROR: code = error; break;
case Replxx::Color::DEFAULT: code = reset; break;
static char const* TERM( getenv( "TERM" ) );
static bool const has256color( TERM ? ( strstr( TERM, "256" ) != nullptr ) : has256colorDefault );
static char const* ansiEscapeCodeTemplate = has256color ? "\033[0;9%d%s%sm" : "\033[0;1;3%d%s%sm";
pos = snprintf( colorBuffer, MAX_COLOR_CODE_SIZE, ansiEscapeCodeTemplate, fg - static_cast<int>( Replxx::Color::GRAY ), underline, bold );
} else {
pos = snprintf( colorBuffer, MAX_COLOR_CODE_SIZE, "\033[0;38;5;%d%s%sm", fg, underline, bold );
}
return ( code );
if ( ( code & color::BACKGROUND_COLOR_SET ) == 0 ) {
return colorBuffer;
}
if ( bg <= static_cast<int unsigned>( Replxx::Color::WHITE ) ) {
if ( bg <= static_cast<int unsigned>( Replxx::Color::LIGHTGRAY ) ) {
snprintf( colorBuffer + pos, MAX_COLOR_CODE_SIZE - pos, "\033[4%dm", bg );
} else {
snprintf( colorBuffer + pos, MAX_COLOR_CODE_SIZE - pos, "\033[10%dm", bg - static_cast<int>( Replxx::Color::GRAY ) );
}
} else {
snprintf( colorBuffer + pos, MAX_COLOR_CODE_SIZE - pos, "\033[48;5;%dm", bg );
}
return colorBuffer;
}
std::string now_ms_str( void ) {

12
third-party/replxx/src/util.hxx generated vendored
View file

@ -5,6 +5,14 @@
namespace replxx {
namespace color {
static int unsigned const RGB666 = 16u;
static int unsigned const GRAYSCALE = 232u;
static int unsigned const BOLD = 1u << 17u;
static int unsigned const UNDERLINE = 1u << 18u;
static int unsigned const BACKGROUND_COLOR_SET = 1u << 19u;
}
inline bool is_control_code(char32_t testChar) {
return (testChar < ' ') || // C0 controls
(testChar >= 0x7F && testChar <= 0x9F); // DEL and C1 controls
@ -14,9 +22,7 @@ inline char32_t control_to_human( char32_t key ) {
return ( key < 27 ? ( key + 0x40 ) : ( key + 0x18 ) );
}
void recompute_character_widths( char32_t const* text, char* widths, int charCount );
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 );
int virtual_render( char32_t const*, int, int&, int&, int, int, char32_t* = nullptr, int* = nullptr );
char const* ansi_color( Replxx::Color );
std::string now_ms_str( void );

2
third-party/replxx/src/windows.cxx generated vendored
View file

@ -113,7 +113,7 @@ int win_write( HANDLE out_, bool autoEscape_, char const* str_, int size_ ) {
int toWrite( static_cast<int>( str_ - s ) );
WriteConsoleA( out_, s, static_cast<DWORD>( toWrite ), &nWritten, nullptr );
count += nWritten;
if ( nWritten != toWrite ) {
if ( static_cast<int>( nWritten ) != toWrite ) {
s = str_ = nullptr;
break;
}

1571
third-party/replxx/tests.py generated vendored

File diff suppressed because it is too large Load diff

View file

@ -33,3 +33,5 @@ third-party/imgui:
git: https://github.com/ocornut/imgui/tree/v1.89.2
third-party/tree-sitter:
git: https://github.com/tree-sitter/tree-sitter/tree/v0.20.8
third-party/replxx:
git: https://github.com/AmokHuginnsson/replxx/commit/1f149bfe20bf6e49c1afd4154eaf0032c8c2fda2