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:
parent
c966e3fb1b
commit
1af88c964e
|
@ -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_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_MAJOR 4)
|
||||||
set(META_VERSION_MINOR 9)
|
set(META_VERSION_MINOR 9)
|
||||||
set(META_VERSION_PATCH 1)
|
set(META_VERSION_PATCH 2)
|
||||||
|
|
||||||
# find required 3rd party libraries
|
# find required 3rd party libraries
|
||||||
include(3rdParty)
|
include(3rdParty)
|
||||||
|
|
|
@ -858,23 +858,47 @@ void ArgumentParser::printBashCompletion(int argc, const char *const *argv, unsi
|
||||||
bool nextArgumentOrValue;
|
bool nextArgumentOrValue;
|
||||||
if (lastDetectedArg && lastDetectedArg->isPresent()) {
|
if (lastDetectedArg && lastDetectedArg->isPresent()) {
|
||||||
if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
|
if ((nextArgumentOrValue = (currentWordIndex > lastDetectedArgIndex))) {
|
||||||
// parameter values of the last arg are possible completions
|
// define function to add parameter values of argument as possible completions
|
||||||
auto currentValueCount = lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size();
|
const auto addValueCompletionsForArg = [&relevantPreDefinedValues, &completeFiles, &completeDirs](const Argument *arg) {
|
||||||
if (currentValueCount) {
|
if (arg->valueCompletionBehaviour() & ValueCompletionBehavior::PreDefinedValues) {
|
||||||
currentValueCount -= (currentWordIndex - lastDetectedArgIndex);
|
relevantPreDefinedValues.push_back(arg);
|
||||||
}
|
|
||||||
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)
|
if (!(arg->valueCompletionBehaviour() & ValueCompletionBehavior::FileSystemIfNoPreDefinedValues)
|
||||||
|| !lastDetectedArg->preDefinedCompletionValues()) {
|
|| !arg->preDefinedCompletionValues()) {
|
||||||
completeFiles = completeFiles || lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
|
completeFiles = completeFiles || arg->valueCompletionBehaviour() & ValueCompletionBehavior::Files;
|
||||||
completeDirs = completeDirs || lastDetectedArg->valueCompletionBehaviour() & ValueCompletionBehavior::Directories;
|
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) {
|
||||||
|
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()) {
|
|| lastDetectedArg->values(lastDetectedArg->occurrences() - 1).size() >= lastDetectedArg->requiredValueCount()) {
|
||||||
// sub arguments of the last arg are possible completions
|
// sub arguments of the last arg are possible completions
|
||||||
for (const Argument *subArg : lastDetectedArg->subArguments()) {
|
for (const Argument *subArg : lastDetectedArg->subArguments()) {
|
||||||
|
|
|
@ -429,7 +429,7 @@ void ArgumentParserTests::testBashCompletion()
|
||||||
Argument valuesArg("values", '\0', "specifies the fields");
|
Argument valuesArg("values", '\0', "specifies the fields");
|
||||||
valuesArg.setRequiredValueCount(-1);
|
valuesArg.setRequiredValueCount(-1);
|
||||||
valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
|
valuesArg.setPreDefinedCompletionValues("title album artist trackpos");
|
||||||
valuesArg.setImplicit(true);
|
valuesArg.setImplicit(false);
|
||||||
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
|
valuesArg.setValueCompletionBehavior(ValueCompletionBehavior::PreDefinedValues | ValueCompletionBehavior::AppendEquationSign);
|
||||||
Argument getArg("get", 'g', "gets tag values");
|
Argument getArg("get", 'g', "gets tag values");
|
||||||
getArg.setSubArguments({ &fieldsArg, &filesArg });
|
getArg.setSubArguments({ &fieldsArg, &filesArg });
|
||||||
|
@ -519,6 +519,15 @@ void ArgumentParserTests::testBashCompletion()
|
||||||
cout.rdbuf(regularCoutBuffer);
|
cout.rdbuf(regularCoutBuffer);
|
||||||
CPPUNIT_ASSERT_EQUAL("COMPREPLY=('album=' 'artist=' ); compopt -o nospace\n"s, buffer.str());
|
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
|
// file names
|
||||||
string iniFilePath = TestUtilities::testFilePath("test.ini");
|
string iniFilePath = TestUtilities::testFilePath("test.ini");
|
||||||
iniFilePath.resize(iniFilePath.size() - 4);
|
iniFilePath.resize(iniFilePath.size() - 4);
|
||||||
|
|
Loading…
Reference in New Issue