forked from Alepha/Alepha
Online documentation for single-letter options.
It's mostly worked out, but there's a few odd corner cases, especially around the auto-gen of negation options. It also got me to start thinking about "is a required negatable option still required of its negation?" Similar questions revolve around requiring such options. I'm punting on these for now, but I think it makes sense to perhaps make those incompatible with such domains. Or to treat the two options as a shared-fate unit. But is `-O -o -O -o` a violation of exclusivity? If we wind up returning to the default state, have we actually passed that option, with respect to "requirement"? I have to think about that some more. A commit message isn't the best place to capture this, but I didn't want to lose this thought.
This commit is contained in:
@ -46,6 +46,8 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
std::map< std::type_index, std::set< const DomainBase * > > domains;
|
std::map< std::type_index, std::set< const DomainBase * > > domains;
|
||||||
|
|
||||||
ArgumentStance argumentStance;
|
ArgumentStance argumentStance;
|
||||||
|
|
||||||
|
std::set< char > shortAliases;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@ -62,6 +64,8 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
|
|
||||||
struct ExclusivityEntry
|
struct ExclusivityEntry
|
||||||
{
|
{
|
||||||
|
// This gets used during the parsing process to indicate which previous entry from this domain
|
||||||
|
// has been seen, if any.
|
||||||
std::optional< std::string > previous;
|
std::optional< std::string > previous;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,6 +161,20 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
incompatibles.erase( name );
|
incompatibles.erase( name );
|
||||||
if( incompatibles.empty() ) return;
|
if( incompatibles.empty() ) return;
|
||||||
|
|
||||||
|
std::set< std::string > incompatibleAliases;
|
||||||
|
for( const auto &incompat: incompatibles )
|
||||||
|
{
|
||||||
|
const auto &aliases= programOptions().at( incompat ).shortAliases;
|
||||||
|
for( const auto alias: aliases )
|
||||||
|
{
|
||||||
|
incompatibleAliases.insert( "-"s + alias );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for( const auto &alias: incompatibleAliases )
|
||||||
|
{
|
||||||
|
incompatibles.insert( alias );
|
||||||
|
}
|
||||||
|
|
||||||
out << "\nIncompatible with: \n\n";
|
out << "\nIncompatible with: \n\n";
|
||||||
bool first= true;
|
bool first= true;
|
||||||
for( const auto &incompat: incompatibles )
|
for( const auto &incompat: incompatibles )
|
||||||
@ -168,6 +186,19 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
out << std::endl;
|
out << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
aliasHelpText( std::ostream &out, const std::string &name )
|
||||||
|
{
|
||||||
|
const auto &aliases= programOptions().at( name ).shortAliases;
|
||||||
|
|
||||||
|
if( aliases.empty() ) return;
|
||||||
|
|
||||||
|
for( const auto &alias: aliases )
|
||||||
|
{
|
||||||
|
out << "-" << alias << ":" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
printAllOptionsHelp( const std::optional< std::string > canonicalProgramName )
|
printAllOptionsHelp( const std::optional< std::string > canonicalProgramName )
|
||||||
{
|
{
|
||||||
@ -200,9 +231,10 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
{
|
{
|
||||||
// When turning off wrapping, here, we also emit a newline between entries.
|
// When turning off wrapping, here, we also emit a newline between entries.
|
||||||
AutoRAII endline{ []{}, []{ std::cout << std::endl; } };
|
AutoRAII endline{ []{}, []{ std::cout << std::endl; } };
|
||||||
|
aliasHelpText( std::cout, name );
|
||||||
auto wrapping= adaptStream( StartWrap{ width, alignmentWidth }, std::cout );
|
auto wrapping= adaptStream( StartWrap{ width, alignmentWidth }, std::cout );
|
||||||
|
|
||||||
const auto &[ _, helpText, defaultBuilder, domains, argumentStance ]= def;
|
const auto &[ _, helpText, defaultBuilder, domains, argumentStance, aliases ]= def;
|
||||||
|
|
||||||
VariableMap substitutions=
|
VariableMap substitutions=
|
||||||
{
|
{
|
||||||
@ -244,6 +276,7 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
std::cout << EndWrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +340,8 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
throw std::logic_error{ "Short form alias mapping of `-"s + key + "` for option `--"
|
throw std::logic_error{ "Short form alias mapping of `-"s + key + "` for option `--"
|
||||||
+ name + "` is invalid, because the long name doesn't exist." };
|
+ name + "` is invalid, because the long name doesn't exist." };
|
||||||
}
|
}
|
||||||
const auto &[ _, help, builder, domains, stance ]= programOptions().at( "--" + name );
|
|
||||||
|
const auto &stance= programOptions().at( "--" + name ).argumentStance;
|
||||||
if( stance == ArgumentStance::Required )
|
if( stance == ArgumentStance::Required )
|
||||||
{
|
{
|
||||||
throw std::logic_error{ "Short form alias mapping of `-"s + key + "` for option `--"
|
throw std::logic_error{ "Short form alias mapping of `-"s + key + "` for option `--"
|
||||||
@ -322,7 +356,21 @@ namespace Alepha::Hydrogen ::detail:: ProgramOptions_m
|
|||||||
{
|
{
|
||||||
validateShortAliases( shortAliases );
|
validateShortAliases( shortAliases );
|
||||||
|
|
||||||
// TODO: Incorporate short aliases into the help...
|
for( const auto &[ alias, option ]: shortAliases )
|
||||||
|
{
|
||||||
|
programOptions().at( "--"s + option ).shortAliases.insert( alias );
|
||||||
|
|
||||||
|
const auto &domainMap= programOptions().at( "--"s + option ).domains;
|
||||||
|
for( const auto &[ requirement, domains ]: domainMap )
|
||||||
|
{
|
||||||
|
if( requirement != typeid( RequirementDomain ) ) continue;
|
||||||
|
|
||||||
|
for( const auto &domain: domains )
|
||||||
|
{
|
||||||
|
requiredOptions()[ domain ].push_back( "-"s + alias );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector< std::string >
|
std::vector< std::string >
|
||||||
|
14
example.cc
14
example.cc
@ -16,11 +16,16 @@ namespace
|
|||||||
|
|
||||||
bool optionC= false;
|
bool optionC= false;
|
||||||
|
|
||||||
|
const Alepha::RequirementDomain need;
|
||||||
|
const Alepha::ExclusivityDomain block;
|
||||||
|
//const Alepha::detail::ProgramOptions_m::Domain< nil > need;
|
||||||
|
//const Alepha::detail::ProgramOptions_m::Domain< nil > block;
|
||||||
|
|
||||||
auto init= enroll <=[]
|
auto init= enroll <=[]
|
||||||
{
|
{
|
||||||
--"set-a"_option << optionA << "The option is an integer. !default!";
|
--"set-a"_option << need << optionA << "The option is an integer. !default!";
|
||||||
--"set-b"_option << optionB << "The option is a string, no defaults.";
|
--"set-b"_option << block << optionB << "The option is a string, no defaults.";
|
||||||
--"set-c"_option << optionC << "This sets the 'C' flag.";
|
--"set-c"_option << block << need << optionC << "This sets the 'C' flag.";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +33,8 @@ int
|
|||||||
main( const int argcnt, const char *const *const argvec )
|
main( const int argcnt, const char *const *const argvec )
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto args= Alepha::handleOptions( argcnt, argvec, { { 'c', "set-c" }, { 'C', "no-set-c" } } );
|
const auto args= Alepha::handleOptions( argcnt, argvec, { { 'c', "set-c" }, { 'C', "no-set-c" }, { 'D', "dump-color-env-var" },
|
||||||
|
{ 'L', "list-color-variables" }, } );
|
||||||
|
|
||||||
std::cout << "A is set to: " << optionA << std::endl;
|
std::cout << "A is set to: " << optionA << std::endl;
|
||||||
std::cout << "B is set to: " << ( optionB.has_value() ? optionB.value() : "nullopt"s ) << std::endl;
|
std::cout << "B is set to: " << ( optionB.has_value() ? optionB.value() : "nullopt"s ) << std::endl;
|
||||||
|
Reference in New Issue
Block a user