bash completion: Show values for implicit args

So eg. `tageditor get [tab][tab]` also suggests specifying
field names directly instead of only via --fields.
This commit is contained in:
Martchus 2017-07-28 17:32:07 +02:00
parent c966e3fb1b
commit 1af88c964e
3 changed files with 48 additions and 15 deletions

View File

@ -127,7 +127,7 @@ 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 9)
set(META_VERSION_PATCH 1)
set(META_VERSION_PATCH 2)
# find required 3rd party libraries
include(3rdParty)

View File

@ -858,23 +858,47 @@ void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsi
bool nextArgumentOrValue;
if (lastDetectedArg && lastDetectedArg->isPresent()) {
if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
// parameter values of the last arg are possible completions
// define function to add parameter values of argument as possible completions
const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
relevantPreDefinedValues.push_back(arg);
}
if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
|| !arg->preDefinedCompletionValues()) {
completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
completeDirs = completeDirs || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
}
};
// detect number of specified values
auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
// ignore values which are specified after the current word
if (currentValueCount) {
currentValueCount -= (currentWordIndex - lastDetectedArgIndex);
}
if (lastDetectedArg->requiredValueCount() == static_cast<size_t>(-1) || (currentValueCount < lastDetectedArg->requiredValueCount())) {
if (lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
relevantPreDefinedValues.push_back(lastDetectedArg);
}
if (!(lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
|| !lastDetectedArg->preDefinedCompletionValues()) {
completeFiles = completeFiles || lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
completeDirs = completeDirs || lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
const auto currentWordIndexRelativeToLastDetectedArg = currentWordIndex - lastDetectedArgIndex;
if (currentValueCount > currentWordIndexRelativeToLastDetectedArg) {
currentValueCount -= currentWordIndexRelativeToLastDetectedArg;
} else {
currentValueCount = 0;
}
}
if (lastDetectedArg->requiredValueCount() == static_cast<size_t>(-1)
// add value completions for implicit child if there are no value specified and there are no values required by the
// last detected argument itself
if (!currentValueCount && !lastDetectedArg->requiredValueCount()) {
for (const Argument *child : lastDetectedArg->subArguments()) {
if (child->isImplicit() && child->requiredValueCount()) {
addValueCompletionsForArg(child);
break;
}
}
}
// add value completions for last argument if there are further values required
if (lastDetectedArg->requiredValueCount() == Argument::varValueCount || (currentValueCount < lastDetectedArg->requiredValueCount())) {
addValueCompletionsForArg(lastDetectedArg);
}
if (lastDetectedArg->requiredValueCount() == Argument::varValueCount
|| lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
// sub arguments of the last arg are possible completions
for (const Argument *subArg : lastDetectedArg->subArguments()) {

View File

@ -429,7 +429,7 @@ void ArgumentParserTests::testBashCompletion()
Argument valuesArg("values", '\0', "specifies the fields");
valuesArg.setRequiredValueCount(-1);
valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
valuesArg.setImplicit(true);
valuesArg.setImplicit(false);
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
Argument getArg("get", 'g', "gets tag values");
getArg.setSubArguments({ &fieldsArg, &filesArg });
@ -519,6 +519,15 @@ void ArgumentParserTests::testBashCompletion()
cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n"s, buffer.str());
// pre-defined values for implicit argument
buffer.str(string());
cout.rdbuf(buffer.rdbuf());
parser.resetArgs();
reader.reset(argv3, argv3 + 1).read();
parser.printBashCompletion(1, argv3, 2, reader);
cout.rdbuf(regularCoutBuffer);
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('title' 'album' 'artist' 'trackpos' '--fields' '--files' )\n"s, buffer.str());
// file names
string iniFilePath = TestUtilities::testFilePath("test.ini");
iniFilePath.resize(iniFilePath.size() - 4);