diff --git a/include/clara.hpp b/include/clara.hpp index 13a7981..63baf75 100644 --- a/include/clara.hpp +++ b/include/clara.hpp @@ -102,7 +102,7 @@ namespace detail { if( it != itEnd ) { auto const &next = *it; - if( isOptPrefix( next[0] ) ) { + if( isOptPrefix( next[0] ) && next.size() > 1 ) { auto delimiterPos = next.find_first_of( " :=" ); if( delimiterPos != std::string::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); @@ -157,6 +157,20 @@ namespace detail { } return *this; } + + void fetchArgument() { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + + m_tokenBuffer.resize( 0 ); + + if( it != itEnd ) + m_tokenBuffer.push_back( { TokenType::Argument, *it } ); + } + } }; @@ -663,7 +677,7 @@ namespace detail { if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } else { - ++remainingTokens; + remainingTokens.fetchArgument(); if( !remainingTokens ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto const &argToken = *remainingTokens; diff --git a/single_include/clara.hpp b/single_include/clara.hpp index aa429e7..593a6db 100644 --- a/single_include/clara.hpp +++ b/single_include/clara.hpp @@ -370,7 +370,7 @@ namespace detail { template struct UnaryLambdaTraits { static const bool isValid = true; - using ArgType = typename std::remove_const::type>::type;; + using ArgType = typename std::remove_const::type>::type; using ReturnType = ReturnT; }; @@ -433,7 +433,7 @@ namespace detail { if( it != itEnd ) { auto const &next = *it; - if( isOptPrefix( next[0] ) ) { + if( isOptPrefix( next[0] ) && next.size() > 1 ) { auto delimiterPos = next.find_first_of( " :=" ); if( delimiterPos != std::string::npos ) { m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); @@ -488,6 +488,20 @@ namespace detail { } return *this; } + + void fetchArgument() { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + + m_tokenBuffer.resize( 0 ); + + if( it != itEnd ) + m_tokenBuffer.push_back( { TokenType::Argument, *it } ); + } + } }; @@ -535,7 +549,7 @@ namespace detail { return *this; } - ~ResultValueBase() { + ~ResultValueBase() override { if( m_type == Ok ) m_value.~T(); } @@ -573,7 +587,7 @@ namespace detail { auto errorMessage() const -> std::string { return m_errorMessage; } protected: - virtual void enforceOk() const { + void enforceOk() const override { // !TBD: If no exceptions, std::terminate here or something switch( m_type ) { case ResultBase::LogicError: @@ -994,7 +1008,7 @@ namespace detail { if( result.value() == ParseResultType::ShortCircuitAll ) return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); } else { - ++remainingTokens; + remainingTokens.fetchArgument(); if( !remainingTokens ) return InternalParseResult::runtimeError( "Expected argument following " + token.token ); auto const &argToken = *remainingTokens; @@ -1116,6 +1130,8 @@ namespace detail { for( auto const &cols : rows ) optWidth = (std::max)(optWidth, cols.left.size() + 2); + optWidth = (std::min)(optWidth, consoleWidth/2); + for( auto const &cols : rows ) { auto row = TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + diff --git a/src/ClaraTests.cpp b/src/ClaraTests.cpp index f0f1af7..27600b2 100644 --- a/src/ClaraTests.cpp +++ b/src/ClaraTests.cpp @@ -330,6 +330,57 @@ std::string toString( Opt const& opt ) { return oss.str(); } +TEST_CASE( "Opt value can start with a dash" ) { + std::string name; + bool showHelp = false; + auto parser + = Help( showHelp ) + | Opt( name, "name" ) + ["-n"]["--name"] + ( "the name to use" ); + + SECTION( "args" ) { + auto result = parser.parse( Args{ "TestApp", "-n", "-foo" } ); + CHECK( result ); + REQUIRE( name == "-foo" ); + } + SECTION( "arg separated by =" ) { + auto result = parser.parse( Args{ "TestApp", "-n=-bar" } ); + CHECK( result ); + REQUIRE( name == "-bar" ); + } + SECTION( "empty args" ) { + auto result = parser.parse( Args{ "TestApp", "-n", "" } ); + CHECK( result ); + REQUIRE( name == "" ); + } +} + +TEST_CASE( "dash as positional argument" ) { + std::string name; + bool showHelp = false; + auto parser + = Help( showHelp ) + | Arg( name, "input file" ) + ( "Input file" ); + + SECTION( "args" ) { + auto result = parser.parse( Args{ "cat", "filename" } ); + CHECK( result ); + REQUIRE( name == "filename" ); + } + SECTION( "dash arg" ) { + auto result = parser.parse( Args{ "cat", "-" } ); + CHECK( result ); + REQUIRE( name == "-" ); + } + SECTION( "slash arg" ) { + auto result = parser.parse( Args{ "cat", "/" } ); + CHECK( result ); + REQUIRE( name == "/" ); + } +} + TEST_CASE( "different widths" ) { std::string s;