forked from Alepha/Alepha
Add a simple commandline parser example.
Also cleaned up a few files. Since these were pulled from various scratch repos from informal ISO discussions over the years, they don't quite line up. This at least gets everything building again.
This commit is contained in:
20
AutoRAII.h
20
AutoRAII.h
@ -79,6 +79,26 @@ namespace Alepha::Hydrogen
|
||||
decltype( auto ) operator->() const { return value; }
|
||||
};
|
||||
|
||||
template< typename Dtor >
|
||||
class AutoRAII< void, Dtor > : boost::noncopyable
|
||||
{
|
||||
private:
|
||||
Dtor dtor;
|
||||
|
||||
public:
|
||||
~AutoRAII()
|
||||
{
|
||||
if constexpr( std::is_same_v< Dtor, std::function< void () > > )
|
||||
{
|
||||
if( dtor == nullptr ) return;
|
||||
}
|
||||
dtor();
|
||||
}
|
||||
|
||||
template< typename Ctor >
|
||||
explicit AutoRAII( Ctor ctor, Dtor dtor ) : dtor( std::move( dtor ) ) { ctor(); }
|
||||
};
|
||||
|
||||
template< typename Ctor, typename Dtor >
|
||||
explicit AutoRAII( Ctor ctor, Dtor ) -> AutoRAII< decltype( ctor() ), Dtor >;
|
||||
}
|
||||
|
17
Concepts.h
17
Concepts.h
@ -7,6 +7,7 @@ static_assert( __cplusplus > 2020'00 );
|
||||
#include <iosfwd>
|
||||
|
||||
#include "meta.h"
|
||||
#include "function_traits.h"
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
{
|
||||
@ -25,7 +26,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
concept ConvertibleTo= std::convertible_to< T, U >;
|
||||
|
||||
template< typename T, typename Base >
|
||||
concept DerivedFrom= std::derived_from< T, Base >
|
||||
concept DerivedFrom= std::derived_from< T, Base >;
|
||||
|
||||
template< typename T >
|
||||
concept FloatingPoint= std::floating_point< T >;
|
||||
@ -50,7 +51,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
template< typename T, typename Target >
|
||||
concept ConvertibleToButNotSameAs= true
|
||||
and not SameAs< T, Target >
|
||||
and ConveritbleTo< T, Target >
|
||||
and ConvertibleTo< T, Target >
|
||||
;
|
||||
|
||||
|
||||
@ -64,7 +65,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
|
||||
template< typename T >
|
||||
concept IStreamable=
|
||||
requires( const T &t, std::istream &os )
|
||||
requires( const T &t, std::istream &is )
|
||||
{
|
||||
{ is >> t } -> SameAs< std::istream & >;
|
||||
};
|
||||
@ -188,7 +189,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
;
|
||||
|
||||
template< typename T >
|
||||
concept Primitive= Floatish< T > or Intish< T >;
|
||||
concept Primitive= FloatingPoint< T > or Integral< T >;
|
||||
|
||||
template< typename T >
|
||||
concept Aggregate= std::is_aggregate_v< T >;
|
||||
@ -203,6 +204,9 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
template< typename T >
|
||||
concept NotFunctional= not Functional< T >;
|
||||
|
||||
template< typename T >
|
||||
concept UnaryFunction= Functional< T > and function_traits< T >::args_size == 1;
|
||||
|
||||
template< typename T >
|
||||
concept StandardLayout= std::is_standard_layout_v< T >;
|
||||
|
||||
@ -237,7 +241,7 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
;
|
||||
|
||||
template< typename T, typename Member >
|
||||
concept SpecializedOn= is_specialized_on< T, Member >;
|
||||
concept SpecializedOn= is_specialized_on_v< T, Member >;
|
||||
|
||||
template< typename Member, typename Seq >
|
||||
concept SequenceOf= Sequence< Seq > and SpecializedOn< Seq, Member >;
|
||||
@ -276,4 +280,5 @@ namespace Alepha::inline Cavorite ::detail:: core_concepts
|
||||
|
||||
namespace Alepha::Cavorite::inline exports::inline core_concepts
|
||||
{
|
||||
|
||||
using namespace detail::core_concepts::exports;
|
||||
}
|
||||
|
175
Console.cpp
175
Console.cpp
@ -1,13 +1,25 @@
|
||||
static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include "console.h"
|
||||
#include "Console.h"
|
||||
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <ext/stdio_filebuf.h>
|
||||
|
||||
#include "ProgramOptions.h"
|
||||
#include "file_help.h"
|
||||
#include "Enum.h"
|
||||
#include "ProgramOptions.h"
|
||||
#include "StaticValue.h"
|
||||
#include "string_algorithms.h"
|
||||
#include "AutoRAII.h"
|
||||
|
||||
/*
|
||||
* All of the terminal control code in this library uses ANSI escape sequences (https://en.wikipedia.org/wiki/ANSI_escape_code).
|
||||
@ -51,17 +63,22 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
if( applicationName().empty() ) applicationName()= "ALEPHA";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
exports::setApplicationName( std::string name )
|
||||
{
|
||||
storage::applicationName()= std::move( name );
|
||||
}
|
||||
|
||||
const std::string &
|
||||
exports::applicationName()
|
||||
{
|
||||
return storage::applicationName();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
auto screenWidthEnv() { return applicationName() + "_SCREEN_WIDTH"; }
|
||||
auto screenWidthEnvLimit() { return applicationName() + "_SCREEN_WIDTH_LIMIT"; }
|
||||
auto disableColorsEnv() { return applicationName() + "_DISABLE_COLOR_TEXT"; }
|
||||
@ -74,7 +91,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
if( getenv( env.c_str() ) )
|
||||
try
|
||||
{
|
||||
return boost::lexical_cast< int >( getenv( env.v_str() ) );
|
||||
return boost::lexical_cast< int >( getenv( env.c_str() ) );
|
||||
}
|
||||
catch( const boost::bad_lexical_cast & ) {}
|
||||
return d;
|
||||
@ -82,7 +99,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
|
||||
int cachedScreenWidth= evaluate <=[]
|
||||
{
|
||||
const int underlying getEnvOrDefault( screenWidthEnv(), getScreenSize().columns );
|
||||
const int underlying= getEnvOrDefault( screenWidthEnv(), Console::main().getScreenSize().columns );
|
||||
return std::min( underlying, getEnvOrDefault( screenWidthEnvLimit(), C::defaultScreenWidthLimit ) );
|
||||
};
|
||||
|
||||
@ -92,11 +109,11 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
bool
|
||||
colorEnabled()
|
||||
{
|
||||
if( not colorState.has_value() ) return getenv( disableColorsEnv() );
|
||||
if( not colorState.has_value() ) return getenv( disableColorsEnv().c_str() );
|
||||
|
||||
if( colorState == "never"_value ) return false;
|
||||
if( colorState == "always"_value ) return true;
|
||||
assert( colorState == "auto"_value );
|
||||
if( colorState.value() == "never"_value ) return false;
|
||||
if( colorState.value() == "always"_value ) return true;
|
||||
assert( colorState.value() == "auto"_value );
|
||||
|
||||
return ::isatty( 1 ); // Auto means only do this for TTYs.
|
||||
}
|
||||
@ -113,7 +130,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
{
|
||||
for( const auto [ name, sgr ]: colorVariables() )
|
||||
{
|
||||
std::cout << name.name << ": ^[[": << sgr.code << "m" << std::endl;
|
||||
std::cout << name.name << ": ^[[" << sgr.code << "m" << std::endl;
|
||||
}
|
||||
}
|
||||
<< "Emit a list with the color variables supported by this application. For use with the `" << colorsEnv()
|
||||
@ -122,6 +139,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
--"dump-color-env-var"_option << []
|
||||
{
|
||||
std::cout << "export " << colorsEnv() << "-\"";
|
||||
bool first= true;
|
||||
for( const auto &[ name, sgr ]: colorVariables() )
|
||||
{
|
||||
if( not first ) std::cout << ":";
|
||||
@ -135,11 +153,11 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
<< "application.";
|
||||
|
||||
parse_environment_variable_for_color:
|
||||
if( getenv( colorsEnv() ) )
|
||||
if( getenv( colorsEnv().c_str() ) )
|
||||
{
|
||||
const std::string contents= getenv( colorsEnv() );
|
||||
const std::string contents= getenv( colorsEnv().c_str() );
|
||||
|
||||
for( const auto var: split( varString, ':' ) )
|
||||
for( const auto var: split( contents, ':' ) )
|
||||
{
|
||||
const auto parsed= split( var, '=' );
|
||||
if( parsed.size() != 2 )
|
||||
@ -199,11 +217,36 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
return os;
|
||||
}
|
||||
|
||||
enum exports::Console::Mode
|
||||
|
||||
enum ConsoleMode
|
||||
{
|
||||
cooked, raw, noblock,
|
||||
};
|
||||
|
||||
|
||||
struct Console::Impl
|
||||
{
|
||||
int fd;
|
||||
// TODO: Do we want to make this not gnu libstdc++ specific?
|
||||
__gnu_cxx::stdio_filebuf< char > filebuf;
|
||||
std::ostream stream;
|
||||
std::stack< std::pair< struct termios, ConsoleMode > > modeStack;
|
||||
ConsoleMode mode= cooked;
|
||||
std::optional< int > cachedScreenWidth;
|
||||
|
||||
explicit
|
||||
Impl( const int fd )
|
||||
: fd( fd ), filebuf( fd, std::ios::out ), stream( &filebuf )
|
||||
{}
|
||||
};
|
||||
|
||||
auto
|
||||
Console::getMode() const
|
||||
{
|
||||
return pimpl().mode;
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -212,15 +255,15 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
BadScreenStateError() : std::runtime_error( "Error in getting terminal dimensions." ) {}
|
||||
};
|
||||
|
||||
struct UnknowScreenError : std::runtime_error
|
||||
struct UnknownScreenError : std::runtime_error
|
||||
{
|
||||
UnknowScreenError() : std::runtime_error( "Terminal is unrecognized. Using defaults." ) {}
|
||||
UnknownScreenError() : std::runtime_error( "Terminal is unrecognized. Using defaults." ) {}
|
||||
};
|
||||
|
||||
auto
|
||||
rawModeGuard( Console console )
|
||||
{
|
||||
const bool skip= console.getMode() == Console::raw;
|
||||
const bool skip= console.getMode() == ConsoleMode::raw;
|
||||
return AutoRAII
|
||||
{
|
||||
[skip, &console]
|
||||
@ -237,20 +280,6 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
}
|
||||
}
|
||||
|
||||
struct Console::Impl
|
||||
{
|
||||
int fd;
|
||||
// TODO: Do we want to make this not gnu libstdc++ specific?
|
||||
__gnu_cxx::stdio_filebuf< char > filebuf;
|
||||
std::ostream stream;
|
||||
std::stack< std::pair< struct termios, decltype( mode ) > > modeStack;
|
||||
ConsoleMode mode= cooked;
|
||||
|
||||
explicit
|
||||
Impl( const int fd )
|
||||
: fd( fd ), filebuf( fd, std::ios::out ), stream( &filebuf )
|
||||
{}
|
||||
};
|
||||
|
||||
Console::Console( const int fd )
|
||||
: impl( std::make_unique< Impl >( fd ) )
|
||||
@ -262,18 +291,11 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
return pimpl().stream;
|
||||
}
|
||||
|
||||
|
||||
Console::Mode
|
||||
Console::getMode() const
|
||||
{
|
||||
return pimpl().mode;
|
||||
}
|
||||
|
||||
void
|
||||
Console::popTermMode()
|
||||
{
|
||||
tcsetattr( pimpl().fd, TCSAFLUSH, &pimpl().modeStack.top().first );
|
||||
mode= pimpl().modeStack.top().second;
|
||||
pimpl().mode= pimpl().modeStack.top().second;
|
||||
pimpl().modeStack.pop();
|
||||
}
|
||||
|
||||
@ -289,12 +311,12 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
|
||||
next.c_iflag&= ~( BRKINT | ICRNL | INPCK | ISTRIP | IXON );
|
||||
next.c_oflag&= ~( OPOST );
|
||||
next.c_flag|= CS8;
|
||||
next.c_lflag&= !( ECHO | ICANNON | IEXTEN | ISIG );
|
||||
next.c_cflag|= CS8;
|
||||
next.c_lflag&= !( ECHO | ICANON | IEXTEN | ISIG );
|
||||
next.c_cc[ VMIN ]= min;
|
||||
next.c_cc[ VTIME ]= 0;
|
||||
|
||||
if( tcsetattr( pimpl().fd, TCSAFLUSH, &next ) ) throw UnknownScreenException{};
|
||||
if( tcsetattr( fd, TCSAFLUSH, &next ) ) throw UnknownScreenError{};
|
||||
|
||||
return now;
|
||||
}
|
||||
@ -303,17 +325,17 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
void
|
||||
Console::setRaw()
|
||||
{
|
||||
setRawModeWithMin( pimpl().fd, 1 );
|
||||
orig.emplace_back( now, mode );
|
||||
mode= raw;
|
||||
const auto old= setRawModeWithMin( pimpl().fd, 1 );
|
||||
pimpl().modeStack.emplace( old, pimpl().mode );
|
||||
pimpl().mode= raw;
|
||||
}
|
||||
|
||||
void
|
||||
Console::setNoblock()
|
||||
{
|
||||
setRawModeWithMin( pimpl().fd, 0 );
|
||||
orig.emplace_back( now, mode );
|
||||
mode= raw;
|
||||
const auto old= setRawModeWithMin( pimpl().fd, 0 );
|
||||
pimpl().modeStack.emplace( old, pimpl().mode );
|
||||
pimpl().mode= raw;
|
||||
}
|
||||
|
||||
void Console::killLineTail() { csi() << 'K'; }
|
||||
@ -329,7 +351,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
void Console::gotoX( const int x ) { csi() << x << 'G'; }
|
||||
|
||||
void
|
||||
Console::gotoY( const int x )
|
||||
Console::gotoY( const int y )
|
||||
{
|
||||
cursorUp( 1'000'000 );
|
||||
cursorDown( y );
|
||||
@ -349,7 +371,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
SGR_String exports::setBlink() { return { "5" }; }
|
||||
|
||||
SGR_String
|
||||
exports::setFGColor( const BasicTextColor c )
|
||||
exports::setFgColor( const BasicTextColor c )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << '3' << int( c );
|
||||
@ -357,7 +379,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
}
|
||||
|
||||
SGR_String
|
||||
exports::setBGColor( const BasicTextColor c )
|
||||
exports::setBgColor( const BasicTextColor c )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << '4' << int( c );
|
||||
@ -368,7 +390,7 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
exports::setColor( const BasicTextColor fg, const BasicTextColor bg )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << '3' << fg << ";4" << int( bg );
|
||||
oss << '3' << int( fg ) << ";4" << int( bg );
|
||||
return { std::move( oss ).str() };
|
||||
}
|
||||
|
||||
@ -393,7 +415,54 @@ namespace Alepha::Cavorite ::detail:: console
|
||||
exports::setExtColor( const TextColor fg, const TextColor bg )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "38;2" << fg << "48;2" << int( bg );
|
||||
oss << "38;2" << int( fg ) << "48;2" << int( bg );
|
||||
return { std::move( oss ).str() };
|
||||
}
|
||||
|
||||
int
|
||||
exports::getConsoleWidth()
|
||||
{
|
||||
return cachedScreenWidth;
|
||||
}
|
||||
|
||||
int
|
||||
Console::getScreenWidth()
|
||||
{
|
||||
if( not pimpl().cachedScreenWidth.has_value() )
|
||||
{
|
||||
pimpl().cachedScreenWidth= getScreenSize().columns;
|
||||
}
|
||||
|
||||
return pimpl().cachedScreenWidth.value();
|
||||
}
|
||||
|
||||
ScreenSize
|
||||
Console::getScreenSize()
|
||||
try
|
||||
{
|
||||
if( not isatty( pimpl().fd ) ) throw UnknownScreenError{};
|
||||
|
||||
// Use the `ioctl( TIOCGWINSZ )`, but we'll just defer to 24x80 if we fail that...
|
||||
struct winsize ws;
|
||||
const int ec= ioctl( pimpl().fd, TIOCGWINSZ, &ws );
|
||||
if( ec == -1 or ws.ws_col == 0 ) throw UnknownScreenError{};
|
||||
|
||||
return { ws.ws_row, ws.ws_col };
|
||||
}
|
||||
catch( const UnknownScreenError & ) { return { 24, 80 }; } // Fallback position....
|
||||
|
||||
namespace
|
||||
{
|
||||
namespace storage
|
||||
{
|
||||
std::unique_ptr< Console > console;
|
||||
}
|
||||
}
|
||||
|
||||
Console &
|
||||
Console::main()
|
||||
{
|
||||
if( not storage::console ) storage::console= std::make_unique< Console >( 1 ); // stdout
|
||||
return *storage::console;
|
||||
}
|
||||
}
|
||||
|
34
Console.h
34
Console.h
@ -2,6 +2,11 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <Alepha/TotalOrder.h>
|
||||
|
||||
// These are some terminal/console control primitives.
|
||||
// There are several "modern" terminal assumptions built
|
||||
// into this library.
|
||||
@ -20,8 +25,6 @@ namespace Alepha::inline Cavorite ::detail:: console
|
||||
|
||||
class Console;
|
||||
|
||||
Console &console() noexcept;
|
||||
|
||||
struct SGR_String
|
||||
{
|
||||
std::string code;
|
||||
@ -52,7 +55,7 @@ namespace Alepha::inline Cavorite ::detail:: console
|
||||
|
||||
// TODO: Move this to its own library.
|
||||
const std::string &applicationName();
|
||||
void setApplication( std::string name );
|
||||
void setApplicationName( std::string name );
|
||||
}
|
||||
|
||||
struct exports::ScreenSize
|
||||
@ -77,13 +80,13 @@ namespace Alepha::inline Cavorite ::detail:: console
|
||||
|
||||
std::ostream &csi();
|
||||
|
||||
|
||||
public:
|
||||
// A console object can only be constructed on a raw UNIX file descriptor.
|
||||
explicit Console( int fd );
|
||||
|
||||
enum Mode;
|
||||
|
||||
Mode getMode() const;
|
||||
static Console &main();
|
||||
auto getMode() const;
|
||||
|
||||
int getScreenWidth();
|
||||
int getScreenHeight();
|
||||
@ -142,12 +145,12 @@ namespace Alepha::inline Cavorite ::detail:: console
|
||||
|
||||
[[nodiscard]] SGR_String setBlink();
|
||||
|
||||
[[nodiscard]] SGR_String setFGColor( BasicTextColor fg );
|
||||
[[nodiscard]] SGR_String setBGColor( BasicTextColor bg );
|
||||
[[nodiscard]] SGR_String setFgColor( BasicTextColor fg );
|
||||
[[nodiscard]] SGR_String setBgColor( BasicTextColor bg );
|
||||
[[nodiscard]] SGR_String setColor( BasicTextColor fg, BasicTextColor bg );
|
||||
|
||||
[[nodiscard]] SGR_String setExtFGColor( TextColor fg );
|
||||
[[nodiscard]] SGR_String setExtBGColor( TextColor fg );
|
||||
[[nodiscard]] SGR_String setExtFgColor( TextColor fg );
|
||||
[[nodiscard]] SGR_String setExtBgColor( TextColor fg );
|
||||
[[nodiscard]] SGR_String setExtColor( TextColor fg, TextColor bg );
|
||||
|
||||
// Basic color wrapping aliases:
|
||||
@ -159,11 +162,18 @@ namespace Alepha::inline Cavorite ::detail:: console
|
||||
[[nodiscard]] inline SGR_String setExtColor( const BasicTextColor fg, const BasicTextColor bg ) { return setExtColor( static_cast< TextColor >( fg ), static_cast< TextColor >( bg ) ); }
|
||||
|
||||
[[nodiscard]] SGR_String setFgTrueColor( int rgb );
|
||||
[[nodiscard]] SGR_String setFgTrueColor( int r, int g, int b )
|
||||
[[nodiscard]] SGR_String setFgTrueColor( int r, int g, int b );
|
||||
|
||||
[[nodiscard]] SGR_String setBgTrueColor( int rgb );
|
||||
[[nodiscard]] SGR_String setBgTrueColor( int r, int g, int b )
|
||||
[[nodiscard]] SGR_String setBgTrueColor( int r, int g, int b );
|
||||
|
||||
void sendSGR( std::ostream &os, SGR_String );
|
||||
|
||||
int getConsoleWidth();
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Cavorite::inline exports::inline console
|
||||
{
|
||||
using namespace detail::console::exports;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
|
||||
namespace Alepha::Hydrogen ::detail:: constexpr_string
|
||||
namespace Alepha::Cavorite ::detail:: constexpr_string
|
||||
{
|
||||
namespace C
|
||||
{
|
||||
@ -63,7 +63,7 @@ namespace Alepha::Hydrogen ::detail:: constexpr_string
|
||||
constexpr ConstexprString()= default;
|
||||
|
||||
constexpr
|
||||
CosntexprString( const char *const s, std::size_t len )
|
||||
ConstexprString( const char *const s, std::size_t len )
|
||||
{
|
||||
if( len >= C::maxSize ) throw BadConstantStringAllocationError{};
|
||||
|
||||
|
6
Makefile
6
Makefile
@ -1 +1,5 @@
|
||||
CXXFLAGS+= -std=c++17 -I .
|
||||
CXXFLAGS+= -std=c++20 -I .
|
||||
|
||||
all: example
|
||||
|
||||
example: ProgramOptions.o string_algorithms.o Console.o word_wrap.o
|
||||
|
@ -1,10 +1,14 @@
|
||||
static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include "Options.h"
|
||||
#include "ProgramOptions.h"
|
||||
|
||||
#include <set>
|
||||
#include <exception>
|
||||
|
||||
#include "algorithm.h"
|
||||
#include <Alepha/Console.h>
|
||||
#include <Alepha/word_wrap.h>
|
||||
#include <Alepha/StaticValue.h>
|
||||
#include <Alepha/error.h>
|
||||
|
||||
namespace Alepha::Cavorite ::detail:: program_options
|
||||
{
|
||||
@ -30,7 +34,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
void
|
||||
printString( const std::string &s, const std::size_t indent )
|
||||
{
|
||||
const std::size_t width= Console::main().getScreenWidth();
|
||||
const std::size_t width= getConsoleWidth();
|
||||
std::cout << wordWrap( s, width, indent ) << std::endl;
|
||||
}
|
||||
}
|
||||
@ -72,7 +76,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
impl::checkArgument( const std::optional< std::string > &argument, const std::string &name )
|
||||
{
|
||||
if( argument.has_value() ) return;
|
||||
throw OptionMissingArgumentError( '`' + name "` requires an argument." );
|
||||
throw OptionMissingArgumentError( '`' + name + "` requires an argument." );
|
||||
}
|
||||
|
||||
const OptionBinding &
|
||||
@ -105,7 +109,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
OptionBinding::operator << ( std::function< void ( std::optional< std::string > ) core ) const
|
||||
OptionBinding::operator << ( std::function< void ( std::string ) > core ) const
|
||||
{
|
||||
// So that users do not have to implement their own checking for argument present,
|
||||
// we do it for them.
|
||||
@ -133,7 +137,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
namespace
|
||||
{
|
||||
std::string
|
||||
buildIncompatibleHelpText( const std::string &name, const auto &domains )
|
||||
buildIncompatibleHelpText( const std::string &name, const auto &domains, const auto &exclusivityMembers )
|
||||
{
|
||||
if( not domains.contains( typeid( ExclusivityDomain ) )
|
||||
or domains.at( typeid( ExclusivityDomain ) ).empty() )
|
||||
@ -144,8 +148,8 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
std::set< std::string > incompatibles;
|
||||
for( const auto &domain: domains.at( typeid( ExclusivityDomain ) ) )
|
||||
{
|
||||
std::transform( mutuallyExclusiveOptions.lower_bound( domain ),
|
||||
mutuallyExclusiveOptions.upper_bound( domain ),
|
||||
std::transform( exclusivityMembers.lower_bound( domain ),
|
||||
exclusivityMembers.upper_bound( domain ),
|
||||
std::inserter( incompatibles, end( incompatibles ) ),
|
||||
[]( const auto &item ) { return item.second; } );
|
||||
}
|
||||
@ -167,13 +171,13 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
void
|
||||
printAllOptionsHelp( const std::optional< std::string > canonicalProgramName )
|
||||
{
|
||||
const auto maxOptionLength= std::max_element( begin( programOptions(), end( programOptions ),
|
||||
const auto longestOption= std::max_element( begin( programOptions() ), end( programOptions() ),
|
||||
[]( const auto &lhs, const auto &rhs )
|
||||
{
|
||||
return lhs.first.size() < rhs.first.size();
|
||||
} );
|
||||
// Account for the `:` and the ` ` in the output table format.
|
||||
const std::size_t alignmentWidth= maxOptionLength + 2;
|
||||
const std::size_t alignmentWidth= longestOption->first.size() + 2;
|
||||
|
||||
//
|
||||
std::multimap< const DomainBase *, std::string > exclusivityMembers;
|
||||
@ -193,14 +197,14 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
{
|
||||
const auto &[ _, helpText, defaultBuilder, domains ]= def;
|
||||
// How much unused of the max width there will be
|
||||
const std::size_t padding= alignmentWidth - optionName.size() - 2;
|
||||
const std::size_t padding= alignmentWidth - name.size() - 2;
|
||||
|
||||
VariableMap substitutions=
|
||||
{
|
||||
// This uses a GNU extension, but it's fine. We can always make this
|
||||
// portable, later.
|
||||
{ "program-name"s, lambaste<=::program_invocation_short_name },
|
||||
{ "option-name"s, lambaste<=optionName },
|
||||
{ "option-name"s, lambaste<=name },
|
||||
{ "default"s, [&defaultBuilder= defaultBuilder, &name= name]
|
||||
{
|
||||
return "Default is `" + name + defaultBuilder() + "`";
|
||||
@ -208,23 +212,24 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
};
|
||||
if( canonicalProgramName.has_value() )
|
||||
{
|
||||
substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();
|
||||
substitutions[ "canonical-name"s ]= lambaste<=canonicalProgramName.value();
|
||||
}
|
||||
|
||||
std::string substitutionTemplate= name + ": " + std::string( padding, ' ' )
|
||||
+ helpText.str() + "\n";
|
||||
|
||||
// Append the incompatibility text, when we see mutually-exclusive options.
|
||||
substitutionTemplate+= buildIncompatibleHelpText( name, domains );
|
||||
substitutionTemplate+= buildIncompatibleHelpText( name, domains, exclusivityMembers );
|
||||
|
||||
const std::string helpString= expandVariables( substitutionTemplate, substitutions, '!' );
|
||||
printString( helpString, alignmentWidth );
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// Check for required options, and print a summary of those:
|
||||
if( not requiredOptions().empty() ) for( const auto &[ _, group ]: requiredOptions )
|
||||
if( not requiredOptions().empty() ) for( const auto &[ _, group ]: requiredOptions() )
|
||||
{
|
||||
const std::size_t width= Console::main().getScreenWidth();
|
||||
const std::size_t width= getConsoleWidth();
|
||||
std::ostringstream oss;
|
||||
oss << "At least one of the options in this group are required: ";
|
||||
bool first= true;
|
||||
@ -232,7 +237,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
{
|
||||
if( not first ) oss << ", ";
|
||||
first= false;
|
||||
oss << '`' << required << '`':
|
||||
oss << '`' << required << '`';
|
||||
}
|
||||
|
||||
std::cout << wordWrap( oss.str(), width ) << std::endl;
|
||||
@ -244,7 +249,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
std::ostream &
|
||||
OptionBinding::operator << ( bool &flag ) const
|
||||
{
|
||||
--OptionString{ "no-" + name.substr( 2 ) };
|
||||
--OptionString{ "no-" + name.substr( 2 ) }
|
||||
<< [&flag] { flag= false; } << "Disable `" + name + "`. See that option for more details.";
|
||||
return self() << [&flag] { flag= true; };
|
||||
}
|
||||
@ -267,7 +272,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
}
|
||||
|
||||
[[noreturn]] void
|
||||
impl::usage( const std::string &helpmessage, const std::optional< std::string > &canonicalName )
|
||||
impl::usage( const std::string &helpMessage, const std::optional< std::string > &canonicalName )
|
||||
{
|
||||
if( not helpMessage.empty() )
|
||||
{
|
||||
@ -278,11 +283,11 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
};
|
||||
|
||||
if( canonicalName.has_value() ) substitutions[ "canonical-name"s ]= lambaste<=canonicalName.value();
|
||||
std::cout << wordWrap( expandVariables( helpMessage, substitutions, '!' ), Console::main().getScreenWidth() )
|
||||
std::cout << wordWrap( expandVariables( helpMessage, substitutions, '!' ), getConsoleWidth() )
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
printOptionsHelp( canonicalName );
|
||||
printAllOptionsHelp( canonicalName );
|
||||
::exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
@ -331,12 +336,12 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
// doing a map lookup.
|
||||
for( const auto &[ name, def ]: opts )
|
||||
{
|
||||
if( C::debugMatching ) error() << "Attempting to match `" << name << "` to `" << arg << "`" << std::endl;
|
||||
if( C::debugMatching ) error() << "Attempting to match `" << name << "` to `" << param << "`" << std::endl;
|
||||
|
||||
const auto &handler= def.handler;
|
||||
std::optional< std::string > argument;
|
||||
if( param == name ) argument= std::nullopt;
|
||||
else if( param.starts_with( name ) and "=:"s.contains( param.at( name.size() ) ) )
|
||||
else if( param.starts_with( name ) and "=:"s.find( param.at( name.size() ) ) != std::string::npos )
|
||||
{
|
||||
argument= param.substr( name.size() + 1 );
|
||||
}
|
||||
@ -352,12 +357,12 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
if( C::debugExclusions )
|
||||
{
|
||||
error() << "I see " << exclusions.size() << " mutual exclusions against `"
|
||||
<< name << "`" std::endl;
|
||||
<< name << "`" << std::endl;
|
||||
}
|
||||
for( const auto &exclusion: exclusions )
|
||||
{
|
||||
// Look up this domain, and see if something from it was used.
|
||||
auto &other= exclusiveOptions()[ exclusion ].previousOption;
|
||||
auto &other= mutuallyExclusiveOptions()[ exclusion ].previous;
|
||||
if( other.has_value() and other != name )
|
||||
{
|
||||
throw std::runtime_error{ "Options `" + other.value() + "` and `"
|
||||
@ -380,8 +385,8 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if( C::debugMatching and not found ) error() << "No match for `" << param << "` was found." << std::endl;
|
||||
if( found ) continue;
|
||||
if( C::debugMatching and not matched ) error() << "No match for `" << param << "` was found." << std::endl;
|
||||
if( matched ) continue;
|
||||
rv.push_back( param );
|
||||
|
||||
if( param.starts_with( "--" ) )
|
||||
@ -392,8 +397,8 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
}
|
||||
catch( const OptionMissingArgumentError &e )
|
||||
{
|
||||
if( next == end( argsForProcessing ) or next->startsWith( "--" ) ) throw;
|
||||
throw std::runtime_error( ex.what() + " did you mean: `"s + param + "=" + *next + "`?" );
|
||||
if( next == end( argsToProcess ) or next->starts_with( "--" ) ) throw;
|
||||
throw std::runtime_error( e.what() + " did you mean: `"s + param + "=" + *next + "`?" );
|
||||
}
|
||||
|
||||
if( endOfArgs != end( args ) ) std::copy( endOfArgs + 1, end( args ), back_inserter( rv ) );
|
||||
@ -402,14 +407,14 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
|
||||
// If we're not doing a help-run, then we need to validate the required
|
||||
// options were all passed.
|
||||
if( requiredOptions.size() != requiredOptionsSeen.size() )
|
||||
if( requiredOptions().size() != requiredOptionsSeen.size() )
|
||||
{
|
||||
for( auto [ required, opts ]: requiredOptions() )
|
||||
{
|
||||
if( requiredOptionsSeen.contains( required ) ) continue;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss <<< "Required option missing. At least one of ";
|
||||
oss << "Required option missing. At least one of ";
|
||||
bool first= true;
|
||||
for( const auto &name: opts )
|
||||
{
|
||||
@ -422,7 +427,7 @@ namespace Alepha::Cavorite ::detail:: program_options
|
||||
throw std::runtime_error( oss.str() );
|
||||
}
|
||||
|
||||
throw std::runtime_error{ "A required option was missing, and it couldn't be identified." );
|
||||
throw std::runtime_error{ "A required option was missing, and it couldn't be identified." };
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -81,6 +81,17 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <Alepha/Alepha.h>
|
||||
#include <Alepha/Concepts.h>
|
||||
#include <Alepha/string_algorithms.h>
|
||||
#include <Alepha/evaluation_helpers.h>
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
{
|
||||
@ -142,7 +153,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
|
||||
using RequirementDomain= Domain< requirement_tag >;
|
||||
|
||||
using PreHelpDoimain= Domain< pre_help_tag >;
|
||||
using PreHelpDomain= Domain< pre_help_tag >;
|
||||
inline const PreHelpDomain affectsHelp;
|
||||
}
|
||||
|
||||
@ -159,7 +170,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
throw std::runtime_error( "Error parsing option `" + argName + "`, with parameter string: `" + s + "` (full option: `" + fullOption + "`)" );
|
||||
}
|
||||
|
||||
namespace impl
|
||||
inline namespace impl
|
||||
{
|
||||
struct ProgramOption;
|
||||
|
||||
@ -211,7 +222,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
{
|
||||
return self() << [&list, name= name]( const std::string param )
|
||||
{
|
||||
for( const std:;string &datum: parseCommas( param ) )
|
||||
for( const std::string &datum: parseCommas( param ) )
|
||||
{
|
||||
if constexpr( Integral< T > )
|
||||
{
|
||||
@ -240,7 +251,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
|
||||
// Boolean flag options are a special case of the value-binding system.
|
||||
// They generate `--no-` forms of the option as well.
|
||||
OptionBinding operator << ( bool &flag ) const;
|
||||
std::ostream &operator << ( bool &flag ) const;
|
||||
|
||||
template< NotFunctional T >
|
||||
[[nodiscard]] std::ostream &
|
||||
@ -269,12 +280,12 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
operator << ( UnaryFunction auto handler ) const
|
||||
{
|
||||
using arg_type= get_arg_t< std::decay_t< decltype( handler ) >, 0 >;
|
||||
if constexpr( is_vector_v< arg_type > )
|
||||
if constexpr( Vector< arg_type > )
|
||||
{
|
||||
// TODO: This should steal the impl from the vector form, above, and that should defer to this.
|
||||
|
||||
using parse_type= typename arg_type::value_type;
|
||||
auto handler= [core, name= name]( std::optional< std::string > argument )
|
||||
auto handler= [handler, name= name]( std::optional< std::string > argument )
|
||||
{
|
||||
impl::checkArgument( argument, name );
|
||||
|
||||
@ -293,28 +304,26 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
core( parsed );
|
||||
handler( parsed );
|
||||
};
|
||||
return registerHandler( handler );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto handler= [core, name= name]( std::optional< std::string > argument )
|
||||
auto wrapped= [handler, name= name]( std::optional< std::string > argument )
|
||||
{
|
||||
impl::checkArgument( argument, name );
|
||||
|
||||
const auto value= argumentFromString< arg_type >( argument.value(), name, name + "=" + argument.value() );
|
||||
return core( value );
|
||||
return handler( value );
|
||||
};
|
||||
return registerHandler( handler );
|
||||
return registerHandler( wrapped );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void printString( const std::string &s, const std::size_t indent );
|
||||
|
||||
void printOptionsHelp();
|
||||
|
||||
struct OptionString { std::string name; };
|
||||
|
||||
namespace exports::inline literals
|
||||
@ -324,7 +333,7 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
|
||||
inline namespace impl
|
||||
{
|
||||
[[nodiscard]] OptoinBinding operator --( OptionString option );
|
||||
[[nodiscard]] OptionBinding operator --( OptionString option );
|
||||
}
|
||||
|
||||
struct ProgramDescription
|
||||
@ -354,23 +363,23 @@ namespace Alepha::inline Cavorite ::detail:: program_options
|
||||
auto
|
||||
handleOptions( const std::vector< std::string > &args )
|
||||
{
|
||||
return impl::handleOptions( args, usageWrap< T > );
|
||||
return impl::handleOptions( args, usageWrap< Supplement > );
|
||||
}
|
||||
|
||||
template< typename Supplement >
|
||||
auto
|
||||
handleOptions( const int argcnt, const char *const *const argvec )
|
||||
{
|
||||
return handleOptions< T >( { argvec + 1, argvec + argcnt }, usageWrap< T > );
|
||||
return handleOptions< Supplement >( { argvec + 1, argvec + argcnt } );
|
||||
}
|
||||
|
||||
auto
|
||||
inline auto
|
||||
handleOptions( const std::vector< std::string > &args )
|
||||
{
|
||||
return handleOptions< ProgramDescription >( args );
|
||||
}
|
||||
|
||||
auto
|
||||
inline auto
|
||||
handleOptions( const int argcnt, const char *const *const argvec )
|
||||
{
|
||||
return handleOptions< ProgramDescription >( argcnt, argvec );
|
||||
|
@ -4,7 +4,7 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/noncoyable.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: static_value
|
||||
{
|
||||
@ -121,7 +121,7 @@ namespace Alepha::inline Cavorite ::detail:: static_value
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
template< typename T, typename init_helper= default_init >
|
||||
template< typename T, typename init_helper= default_init< T > >
|
||||
class exports::StaticValue : boost::noncopyable
|
||||
{
|
||||
private:
|
||||
@ -141,7 +141,7 @@ namespace Alepha::inline Cavorite ::detail:: static_value
|
||||
return *storage;
|
||||
}
|
||||
|
||||
constexpr auto
|
||||
constexpr decltype( auto )
|
||||
operator() () noexcept
|
||||
{
|
||||
return get();
|
||||
|
35
example.cc
Normal file
35
example.cc
Normal file
@ -0,0 +1,35 @@
|
||||
static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include "ProgramOptions.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace Alepha::literals::option_literals;
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
int optionA= 42;
|
||||
std::optional< std::string > optionB;
|
||||
|
||||
auto init= Alepha::enroll <=[]
|
||||
{
|
||||
--"set-a"_option << optionA << "The option is an integer. !default!";
|
||||
--"set-b"_option << optionB << "The option is a string, no defaults.";
|
||||
};
|
||||
}
|
||||
|
||||
int
|
||||
main( const int argcnt, const char *const *const argvec )
|
||||
try
|
||||
{
|
||||
const auto args= Alepha::handleOptions( argcnt, argvec );
|
||||
|
||||
std::cout << "A is set to: " << optionA << std::endl;
|
||||
std::cout << "B is set to: " << ( optionB.has_value() ? optionB.value() : "nullopt"s ) << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch( const std::exception &ex )
|
||||
{
|
||||
std::cerr << "Error: " << ex.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
@ -5,7 +5,7 @@ static_assert( __cplusplus > 2020'00 );
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
||||
#include "error.hpp"
|
||||
#include "error.h"
|
||||
|
||||
namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||
{
|
||||
@ -31,7 +31,7 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||
|
||||
for( const char ch: text )
|
||||
{
|
||||
if( mode == normal and ch == sigil )
|
||||
if( mode == Normal and ch == sigil )
|
||||
{
|
||||
mode= Symbol;
|
||||
varName.clear();
|
||||
@ -46,7 +46,7 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||
{
|
||||
throw std::runtime_error( "No such variable: `" + varName + "`" );
|
||||
}
|
||||
if( C::debugVariableExpansion ) error() << "Expanding variable with name `" << varName << "`" << std::endl;
|
||||
if( C::debugExpansion ) error() << "Expanding variable with name `" << varName << "`" << std::endl;
|
||||
rv+= vars.at( varName )();
|
||||
}
|
||||
else rv+= sigil;
|
||||
@ -95,4 +95,22 @@ namespace Alepha::Cavorite ::detail:: string_algorithms
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::vector< std::string >
|
||||
exports::split( const std::string &s, const char token )
|
||||
{
|
||||
std::vector< std::string > rv;
|
||||
std::string next;
|
||||
for( const char ch: s )
|
||||
{
|
||||
if( ch != token )
|
||||
{
|
||||
next+= ch;
|
||||
continue;
|
||||
}
|
||||
rv.push_back( std::move( next ) );
|
||||
next.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,16 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <Alepha/Concepts.h>
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
||||
{
|
||||
inline namespace exports {}
|
||||
@ -18,6 +24,8 @@ namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
||||
|
||||
inline namespace exports
|
||||
{
|
||||
using VariableMap= VarMap;
|
||||
|
||||
/*!
|
||||
* Returns a new string with text-replacement variables expanded.
|
||||
*
|
||||
@ -39,6 +47,7 @@ namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
||||
*/
|
||||
std::vector< std::string > parseCommas( const std::string &text );
|
||||
|
||||
std::vector< std::string > split( const std::string &s, char token );
|
||||
|
||||
/*!
|
||||
* Parses an integral range description into a vector of values.
|
||||
@ -63,9 +72,10 @@ namespace Alepha::inline Cavorite ::detail:: string_algorithms
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Cavorite::inline exports::inline string_algorithms
|
||||
{
|
||||
using namespace detail::exports::string_algorithms;
|
||||
using namespace detail::string_algorithms::exports;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Alepha::inline Cavorite ::detail:: type_lisp
|
||||
namespace exports
|
||||
{
|
||||
template< typename Type >
|
||||
concept TypeListType= is_type_list_v< Type >:
|
||||
concept TypeListType= is_type_list_v< Type >;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
|
@ -2,6 +2,13 @@ static_assert( __cplusplus > 2020 );
|
||||
|
||||
#include "word_wrap.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <tuple>
|
||||
#include <iostream>
|
||||
|
||||
#include "evaluation_helpers.h"
|
||||
|
||||
namespace Alepha::Cavorite ::detail:: word_wrap
|
||||
{
|
||||
namespace
|
||||
@ -16,8 +23,14 @@ namespace Alepha::Cavorite ::detail:: word_wrap
|
||||
{
|
||||
if( currentLineWidth + word.size() > maximumWidth )
|
||||
{
|
||||
//std::cerr << "Going to newline on word: " << word << "(currentLineWidth= " << currentLineWidth << ")" << std::endl;
|
||||
result+= '\n';
|
||||
std::fill_n( back_inserter( result ), ' ', nextLineOffset );
|
||||
//const auto orig= result.size();
|
||||
std::fill_n( back_inserter( result ), nextLineOffset, ' ' );
|
||||
//assert( orig + nextLineOffset == result.size() );
|
||||
//std::cerr << "orig: " << orig << " nextLineOffset: " << nextLineOffset << " result: " << result.size() << std::endl;
|
||||
|
||||
//result+= '%';
|
||||
return nextLineOffset;
|
||||
}
|
||||
else return currentLineWidth;
|
||||
@ -32,9 +45,10 @@ namespace Alepha::Cavorite ::detail:: word_wrap
|
||||
std::string
|
||||
exports::wordWrap( const std::string &text, const std::size_t width, const std::size_t nextLineOffset )
|
||||
{
|
||||
auto putWord= [[nodiscard]] [width, nextLineOffset]( std::string &&word, std::string &line, const std::size_t lineLength )
|
||||
auto putWord= [width, nextLineOffset]( std::string &&word, std::string &line, const std::size_t lineLength )
|
||||
{
|
||||
return applyWordToLine( width, nextLineOffset, line.size(), std::move( word ), line );
|
||||
const auto rv= applyWordToLine( width, nextLineOffset, lineLength, std::move( word ), line );
|
||||
return rv;
|
||||
};
|
||||
|
||||
std::string result;
|
||||
@ -54,7 +68,7 @@ namespace Alepha::Cavorite ::detail:: word_wrap
|
||||
word.clear();
|
||||
if( lineLength < width )
|
||||
{
|
||||
line+= ' ';
|
||||
result+= ' ';
|
||||
lineLength++;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ static_assert( __cplusplus > 2020'00 );
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Alepha::inline Cavorite ::detail:: word_wrap
|
||||
{
|
||||
inline namespace exports
|
||||
@ -11,3 +13,8 @@ namespace Alepha::inline Cavorite ::detail:: word_wrap
|
||||
std::string wordWrap( const std::string &text, std::size_t width, std::size_t nextLineOffset= 0 );
|
||||
}
|
||||
}
|
||||
|
||||
namespace Alepha::Cavorite::inline exports::inline word_wrap
|
||||
{
|
||||
using namespace detail::word_wrap::exports;
|
||||
}
|
||||
|
Reference in New Issue
Block a user