QCommandLineParser: Wrap very long option names to leave room for descriptions
Fixes: QTBUG-79926 Change-Id: I3302e0ed5b58949a35ccb001c71b22a6400a6c81 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
a426326e99
commit
f0ea852d4d
@ -1063,18 +1063,23 @@ QString QCommandLineParser::helpText() const
|
|||||||
return d->helpText(false);
|
return d->helpText(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString wrapText(const QString &names, int longestOptionNameString, const QString &description)
|
static QString wrapText(const QString &names, int optionNameMaxWidth, const QString &description)
|
||||||
{
|
{
|
||||||
const QLatin1Char nl('\n');
|
const QLatin1Char nl('\n');
|
||||||
const QLatin1String indentation(" ");
|
const QLatin1String indentation(" ");
|
||||||
if (description.isEmpty())
|
|
||||||
return indentation + names + nl;
|
|
||||||
|
|
||||||
QString text = indentation + names.leftJustified(longestOptionNameString) + QLatin1Char(' ');
|
// In case the list of option names is very long, wrap it as well
|
||||||
const int indent = text.length();
|
int nameIndex = 0;
|
||||||
|
auto nextNameSection = [&]() {
|
||||||
|
QString section = names.mid(nameIndex, optionNameMaxWidth);
|
||||||
|
nameIndex += section.size();
|
||||||
|
return section;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString text;
|
||||||
int lineStart = 0;
|
int lineStart = 0;
|
||||||
int lastBreakable = -1;
|
int lastBreakable = -1;
|
||||||
const int max = 79 - indent;
|
const int max = 79 - (indentation.size() + optionNameMaxWidth + 1);
|
||||||
int x = 0;
|
int x = 0;
|
||||||
const int len = description.length();
|
const int len = description.length();
|
||||||
|
|
||||||
@ -1103,8 +1108,7 @@ static QString wrapText(const QString &names, int longestOptionNameString, const
|
|||||||
if (breakAt != -1) {
|
if (breakAt != -1) {
|
||||||
const int numChars = breakAt - lineStart;
|
const int numChars = breakAt - lineStart;
|
||||||
//qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
|
//qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
|
||||||
if (lineStart > 0)
|
text += indentation + nextNameSection().leftJustified(optionNameMaxWidth) + QLatin1Char(' ');
|
||||||
text += QString(indent, QLatin1Char(' '));
|
|
||||||
text += description.midRef(lineStart, numChars) + nl;
|
text += description.midRef(lineStart, numChars) + nl;
|
||||||
x = 0;
|
x = 0;
|
||||||
lastBreakable = -1;
|
lastBreakable = -1;
|
||||||
@ -1115,6 +1119,10 @@ static QString wrapText(const QString &names, int longestOptionNameString, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (nameIndex < names.size()) {
|
||||||
|
text += indentation + nextNameSection() + nl;
|
||||||
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1158,11 +1166,12 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
|
|||||||
longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
|
longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
|
||||||
}
|
}
|
||||||
++longestOptionNameString;
|
++longestOptionNameString;
|
||||||
|
const int optionNameMaxWidth = qMin(50, longestOptionNameString);
|
||||||
auto optionNameIterator = optionNameList.cbegin();
|
auto optionNameIterator = optionNameList.cbegin();
|
||||||
for (const QCommandLineOption &option : qAsConst(options)) {
|
for (const QCommandLineOption &option : qAsConst(options)) {
|
||||||
if (option.flags() & QCommandLineOption::HiddenFromHelp)
|
if (option.flags() & QCommandLineOption::HiddenFromHelp)
|
||||||
continue;
|
continue;
|
||||||
text += wrapText(*optionNameIterator, longestOptionNameString, option.description());
|
text += wrapText(*optionNameIterator, optionNameMaxWidth, option.description());
|
||||||
++optionNameIterator;
|
++optionNameIterator;
|
||||||
}
|
}
|
||||||
if (!positionalArgumentDefinitions.isEmpty()) {
|
if (!positionalArgumentDefinitions.isEmpty()) {
|
||||||
@ -1170,7 +1179,7 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
|
|||||||
text += nl;
|
text += nl;
|
||||||
text += QCommandLineParser::tr("Arguments:") + nl;
|
text += QCommandLineParser::tr("Arguments:") + nl;
|
||||||
for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
|
for (const PositionalArgumentDefinition &arg : positionalArgumentDefinitions)
|
||||||
text += wrapText(arg.name, longestOptionNameString, arg.description);
|
text += wrapText(arg.name, optionNameMaxWidth, arg.description);
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,13 @@ int main(int argc, char *argv[])
|
|||||||
parser.process(app);
|
parser.process(app);
|
||||||
const QString size = parser.value("size");
|
const QString size = parser.value("size");
|
||||||
printf("Resizing %s to %s and saving to %s\n", qPrintable(parser.value("load")), qPrintable(size), qPrintable(parser.value("o")));
|
printf("Resizing %s to %s and saving to %s\n", qPrintable(parser.value("load")), qPrintable(size), qPrintable(parser.value("o")));
|
||||||
|
} else if (command == "long") {
|
||||||
|
// A very long option (QTBUG-79926)
|
||||||
|
QCommandLineOption longOption(QStringList{QStringLiteral("looooooooooooong-option"), QStringLiteral("looooong-opt-alias")});
|
||||||
|
longOption.setDescription(QStringLiteral("Short description"));
|
||||||
|
longOption.setValueName(QStringLiteral("looooooooooooong-value-name"));
|
||||||
|
parser.addOption(longOption);
|
||||||
|
parser.process(app);
|
||||||
} else {
|
} else {
|
||||||
// Call process again, to handle unknown options this time.
|
// Call process again, to handle unknown options this time.
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
|
@ -78,6 +78,7 @@ private slots:
|
|||||||
void testUnknownOption();
|
void testUnknownOption();
|
||||||
void testHelpAll_data();
|
void testHelpAll_data();
|
||||||
void testHelpAll();
|
void testHelpAll();
|
||||||
|
void testVeryLongOptionNames();
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *empty_argv[] = { 0 };
|
static char *empty_argv[] = { 0 };
|
||||||
@ -737,6 +738,38 @@ void tst_QCommandLineParser::testHelpAll()
|
|||||||
#endif // QT_CONFIG(process)
|
#endif // QT_CONFIG(process)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QCommandLineParser::testVeryLongOptionNames()
|
||||||
|
{
|
||||||
|
#if !QT_CONFIG(process)
|
||||||
|
QSKIP("This test requires QProcess support");
|
||||||
|
#else
|
||||||
|
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
|
||||||
|
QSKIP("Deploying executable applications to file system on Android not supported.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QCoreApplication app(empty_argc, empty_argv);
|
||||||
|
QProcess process;
|
||||||
|
process.start("testhelper/qcommandlineparser_test_helper", QStringList() << "0" << "long" << "--help");
|
||||||
|
QVERIFY(process.waitForFinished(5000));
|
||||||
|
QCOMPARE(process.exitStatus(), QProcess::NormalExit);
|
||||||
|
QString output = process.readAll();
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
output.replace(QStringLiteral("\r\n"), QStringLiteral("\n"));
|
||||||
|
#endif
|
||||||
|
const QStringList lines = output.split('\n');
|
||||||
|
const int last = lines.count() - 1;
|
||||||
|
// Let's not compare everything, just the final parts.
|
||||||
|
QCOMPARE(lines.at(last - 7), " cdefghijklmnopqrstuvwxyz");
|
||||||
|
QCOMPARE(lines.at(last - 6), " --looooooooooooong-option, --looooong-opt-alias <l Short description");
|
||||||
|
QCOMPARE(lines.at(last - 5), " ooooooooooooong-value-name>");
|
||||||
|
QCOMPARE(lines.at(last - 4), "");
|
||||||
|
QCOMPARE(lines.at(last - 3), "Arguments:");
|
||||||
|
QCOMPARE(lines.at(last - 2), " parsingMode The parsing mode to test.");
|
||||||
|
QCOMPARE(lines.at(last - 1), " command The command to execute.");
|
||||||
|
|
||||||
|
#endif // QT_CONFIG(process)
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(tst_QCommandLineParser)
|
QTEST_APPLESS_MAIN(tst_QCommandLineParser)
|
||||||
#include "tst_qcommandlineparser.moc"
|
#include "tst_qcommandlineparser.moc"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user