Allow nested operation arguments

eg. syncthingctl pwd rescan
This commit is contained in:
Martchus 2017-04-06 00:01:06 +02:00
parent 864605f5c8
commit 7c189b18e1
3 changed files with 41 additions and 14 deletions

View File

@ -120,8 +120,8 @@ set(META_APP_AUTHOR "Martchus")
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "Common C++ classes and routines used by my applications such as argument parser, IO and conversion utilities")
set(META_VERSION_MAJOR 4)
set(META_VERSION_MINOR 6)
set(META_VERSION_PATCH 2)
set(META_VERSION_MINOR 7)
set(META_VERSION_PATCH 0)
# find required 3rd party libraries
include(3rdParty)

View File

@ -195,14 +195,12 @@ void ArgumentReader::read(ArgumentVector &args)
}
// first value might denote "operation"
if(!index) {
for(Argument *arg : args) {
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
lastArgDenotation = argv;
++index, ++argv;
break;
}
for(Argument *arg : args) {
if(arg->denotesOperation() && arg->name() && !strcmp(arg->name(), *argv)) {
(matchingArg = arg)->m_occurrences.emplace_back(index, parentPath, parentArg);
lastArgDenotation = argv;
++index, ++argv;
break;
}
}
@ -731,8 +729,8 @@ bool ArgumentParser::isUncombinableMainArgPresent() const
* - The same argument has not been added twice to the same parent.
* - Only one argument within a parent is default or implicit.
* - Only main arguments denote operations.
* - Argument abbreviations are unique within each level.
* - Argument names are unique within within each level.
* - Argument abbreviations are unique within the same level.
* - Argument names are unique within within the same level.
*
* \remarks
* - Verifies the sub arguments, too.
@ -752,7 +750,6 @@ void ApplicationUtilities::ArgumentParser::verifyArgs(const ArgumentVector &args
for(const Argument *arg : args) {
assert(find(verifiedArgs.cbegin(), verifiedArgs.cend(), arg) == verifiedArgs.cend());
verifiedArgs.push_back(arg);
assert(arg->isMainArgument() || !arg->denotesOperation());
assert(!arg->isImplicit() || !hasImplicit);
hasImplicit |= arg->isImplicit();
assert(!arg->abbreviation() || find(abbreviations.cbegin(), abbreviations.cend(), arg->abbreviation()) == abbreviations.cend());
@ -1026,7 +1023,7 @@ void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsi
if(reader.argv == reader.end) {
cout << '\'' << *(reader.argv - 1) << '\'' << ' ';
}
} else if(arg->denotesOperation() && (!actualArgumentCount() || (currentWordIndex == 0 && (!lastDetectedArg || (lastDetectedArg->isPresent() && lastDetectedArgIndex == 0))))) {
} else if(arg->denotesOperation()) {
cout << '\'' << arg->name() << '\'' << ' ';
} else {
cout << '\'' << '-' << '-' << arg->name() << '\'' << ' ';

View File

@ -340,6 +340,25 @@ void ArgumentParserTests::testParsing()
CPPUNIT_ASSERT(!qtConfigArgs.qtWidgetsGuiArg().isPresent());
CPPUNIT_ASSERT(!strcmp(e.what(), "Not all parameter for argument \"fields\" provided. You have to provide the following parameter: title album artist trackpos"));
}
// nested operations
const char *argv14[] = {"tageditor", "get", "fields", "album=test", "-f", "somefile"};
parser.resetArgs();
fieldsArg.setRequiredValueCount(-1);
fieldsArg.setDenotesOperation(true);
parser.parseArgs(6, argv14);
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
CPPUNIT_ASSERT(fieldsArg.isPresent());
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "album=test"));
// implicit flag still works when argument doesn't denote operation
parser.resetArgs();
fieldsArg.setDenotesOperation(false);
parser.parseArgs(6, argv14);
CPPUNIT_ASSERT(displayTagInfoArg.isPresent());
CPPUNIT_ASSERT(fieldsArg.isPresent());
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(0), "fields"));
CPPUNIT_ASSERT(!strcmp(fieldsArg.values().at(1), "album=test"));
}
/*!
@ -453,10 +472,21 @@ void ArgumentParserTests::testBashCompletion()
cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('--files' '--values' )\n"s, buffer.str());
// nested operations should be proposed as operations
buffer.str(string());
cout.rdbuf(buffer.rdbuf());
parser.resetArgs();
filesArg.setDenotesOperation(true);
reader.reset(argv2, argv2 + 1).read();
parser.printBashCompletion(1, argv2, 1, reader);
cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('files' '--values' )\n"s, buffer.str());
// specifying no args should propose all main arguments
buffer.str(string());
cout.rdbuf(buffer.rdbuf());
parser.resetArgs();
filesArg.setDenotesOperation(false);
reader.reset(nullptr, nullptr).read();
parser.printBashCompletion(0, nullptr, 0, reader);
cout.rdbuf(regularCoutBuffer);