13#include <QtCore/qfile.h>
14#include <QtCore/qregularexpression.h>
15#include <QtCore/qtextstream.h>
23using namespace Qt::StringLiterals;
139} cmds[] = { {
"a",
CMD_A,
true },
141 {
"b",
CMD_B,
true },
146 {
"c",
CMD_C,
true },
154 {
"e",
CMD_E,
true },
179 {
"i",
CMD_I,
true },
252
253
254
255
258 qsizetype colonPos = link.indexOf(
':');
259 QString cleaned{link};
260 cleaned.replace(
"\\#"_L1,
"#"_L1);
261 if ((colonPos == -1) || (!link.startsWith(
"file:") && !link.startsWith(
"mailto:")))
263 return cleaned.mid(colonPos + 1).simplified();
273 while (cmds[i]
.name) {
277 Location::internalError(QStringLiteral(
"command %1 missing").arg(i));
283 const auto &outputFormats = config.getOutputFormats();
284 for (
const auto &format : outputFormats)
285 DocParser::s_quoting = DocParser::s_quoting
290 DocParser::file_resolver = &file_resolver;
294
295
296
297
298
299
300
301
303 const QSet<QString> &metaCommandSet,
const QSet<QString> &possibleTopics)
307 m_inputLength = m_input.size();
308 m_cachedLocation = docPrivate->m_start_loc;
309 m_cachedPosition = 0;
310 m_private = docPrivate;
314 m_paragraphState = OutsideParagraph;
315 m_inTableHeader =
false;
316 m_inTableRow =
false;
317 m_inTableItem =
false;
318 m_indexStartedParagraph =
false;
324 m_openedCommands.push(CMD_OMIT);
328 Atom *currentLinkAtom =
nullptr;
330 QStack<
bool> preprocessorSkipping;
331 int numPreprocessorSkipping = 0;
337 QString codeLanguage;
339 while (m_position < m_inputLength) {
340 QChar ch = m_input.at(m_position);
342 switch (ch.unicode()) {
345 m_backslashPosition = m_position;
347 while (m_position < m_inputLength) {
348 ch = m_input.at(m_position);
349 if (ch.isLetterOrNumber()) {
356 m_endPosition = m_position;
357 if (cmdStr.isEmpty()) {
358 if (m_position < m_inputLength) {
359 QChar nextCh = m_input.at(m_position);
360 if (nextCh ==
'\\') {
361 appendEscapedIdentifier();
362 }
else if (nextCh.isSpace()) {
365 appendChar(QLatin1Char(
' '));
368 appendChar(m_input.at(m_position++));
385 m_private->m_params.insert(p1);
389 appendAtom(Atom(Atom::CodeBad,
390 getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr))));
397 location().warning(QStringLiteral(
"'\\bold' is deprecated. Use '\\b'"));
404 enterPara(Atom::BriefLeft, Atom::BriefRight);
408 p1 = untabifyEtc(getArgument(ArgumentParsingOptions::Verbatim));
409 marker = CodeMarker::markerForCode(p1);
410 appendAtom(
Atom(
Atom::C, marker->markedUpCode(p1,
nullptr, location())));
414 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
418 codeLanguage = getLanguageArgument(&marker);
422 appendAtom(Atom(Atom::Code, getCode(CMD_CODE, marker, getMetaCommandArgument(cmdStr)), codeLanguage));
426 appendAtom(Atom(Atom::Qml,
427 getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String(
"QML")),
428 getMetaCommandArgument(cmdStr))));
433 if (!isBlankLine()) {
434 if (isLeftBraceAhead()) {
435 enterPara(Atom::DetailsSummaryLeft, Atom::DetailsSummaryRight);
437 location().warning(u"Expected '{' when parsing \\%1 argument"_s.arg(cmdName(cmd)));
438 std::ignore = getRestOfLine();
441 m_openedCommands.push(cmd);
450 p1 = getArgument(ArgumentParsingOptions::Verbatim);
452 m_openedCommands.push(cmd);
464 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
469 QString arg = getOptionalArgument();
476 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
479 int indent = arg.toInt();
480 for (
int i = 0; i < indent; ++i)
482 appendToCode(
"...\n");
486 if (!preprocessorSkipping.empty()) {
487 if (preprocessorSkipping.top()) {
488 --numPreprocessorSkipping;
490 ++numPreprocessorSkipping;
492 preprocessorSkipping.top() = !preprocessorSkipping.top();
493 (
void)getRestOfLine();
494 if (numPreprocessorSkipping)
495 skipToNextPreprocessorCommand();
498 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ELSE)));
508 if (closeCommand(cmd)) {
514 if (preprocessorSkipping.size() > 0) {
515 if (preprocessorSkipping.pop())
516 --numPreprocessorSkipping;
517 (
void)getRestOfLine();
518 if (numPreprocessorSkipping)
519 skipToNextPreprocessorCommand();
522 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDIF)));
526 if (closeCommand(cmd)) {
532 if (closeCommand(cmd)) {
540 if (closeCommand(cmd)) {
542 if (m_openedLists.top().isStarted()) {
543 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
544 appendAtom(Atom(Atom::ListRight, m_openedLists.top().styleString()));
553 if (closeCommand(cmd)) {
560 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDRAW)));
575 if (closeCommand(cmd)) {
581 if (closeCommand(cmd)) {
587 if (openCommand(cmd)) {
594 if (isLeftBracketAhead())
595 p2 = getBracketedArgument();
598 appendAtom(Atom(Atom::AnnotatedList, getArgument(), p2));
602 appendAtom(Atom(Atom::SinceList, getRestOfLine().simplified()));
606 if (isLeftBracketAhead())
607 p2 = getBracketedArgument();
610 QString arg1 = getArgument();
611 QString arg2 = getOptionalArgument();
614 appendAtom(Atom(Atom::GeneratedList, arg1, p2));
617 if (m_openedCommands.top() == CMD_TABLE) {
620 m_inTableHeader =
true;
622 if (m_openedCommands.contains(CMD_TABLE))
623 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
625 cmdName(m_openedCommands.top())));
628 QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
633 location().warning(QStringLiteral(
634 "'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
644 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
645 if (preprocessorSkipping.top())
646 ++numPreprocessorSkipping;
647 if (numPreprocessorSkipping)
648 skipToNextPreprocessorCommand();
655 enterPara(Atom::ImportantLeft, Atom::ImportantRight);
659 QString fileName = getArgument();
660 QStringList parameters;
662 if (isLeftBraceAhead()) {
663 identifier = getArgument();
664 while (isLeftBraceAhead() && parameters.size() < 9)
665 parameters << getArgument();
667 identifier = getRestOfLine();
669 include(fileName, identifier, parameters);
676 if (m_paragraphState == OutsideParagraph) {
678 m_indexStartedParagraph =
true;
681 if (m_indexStartedParagraph
684 m_indexStartedParagraph =
false;
690 insertKeyword(getRestOfLine());
693 if (m_openedCommands.top() != CMD_TOC) {
694 location().warning(
"Command '\\%1' outside of '\\%2'"_L1
695 .arg(cmdName(cmd), cmdName(CMD_TOC)));
701 if (isLeftBracketAhead())
702 p2 = getBracketedArgument();
706 appendAtom(LinkAtom(p1, p2, location()));
708 if (isLeftBraceAhead()) {
713 appendAtom(Atom(Atom::String, cleanLink(p1)));
722 if (openCommand(cmd))
727 if (openCommand(cmd)) {
730 appendAtom(
LinkAtom(p1, {}, location()));
732 skipSpacesOrOneEndl();
736 if (openCommand(cmd)) {
738 m_openedLists.push(OpenedList(location(), getOptionalArgument()));
748 enterPara(Atom::NoteLeft, Atom::NoteRight);
750 case CMD_NOTRANSLATE:
754 location().warning(QStringLiteral(
"'\\o' is deprecated. Use '\\li'"));
758 if (m_openedCommands.top() == CMD_LIST) {
759 if (m_openedLists.top().isStarted())
760 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
762 appendAtom(Atom(Atom::ListLeft, m_openedLists.top().styleString()));
763 m_openedLists.top().next();
764 appendAtom(Atom(Atom::ListItemNumber, m_openedLists.top().numberString()));
765 appendAtom(Atom(Atom::ListItemLeft, m_openedLists.top().styleString()));
767 }
else if (m_openedCommands.top() == CMD_TABLE) {
770 if (isLeftBraceAhead()) {
772 if (isLeftBraceAhead())
776 if (!m_inTableHeader && !m_inTableRow) {
778 QStringLiteral(
"Missing '\\%1' or '\\%2' before '\\%3'")
783 }
else if (m_inTableItem) {
785 m_inTableItem =
false;
788 appendAtom(Atom(Atom::TableItemLeft, p1, p2));
789 m_inTableItem =
true;
792 QStringLiteral(
"Command '\\%1' outside of '\\%2' and '\\%3'")
801 if (!m_private->m_enumItemList.contains(p1))
802 m_private->m_enumItemList.append(p1);
803 if (!m_private->m_omitEnumItemList.contains(p1))
804 m_private->m_omitEnumItemList.append(p1);
805 skipSpacesOrOneEndl();
807 while (m_position < m_inputLength && !isBlankLine()) {
809 if (qsizetype pos = m_position; pos < m_input.size()
810 && m_input.at(pos++).unicode() ==
'\\') {
812 while (pos < m_input.size() && m_input[pos].isLetterOrNumber())
813 nextCmdStr += m_input[pos++];
815 if (nextCmd == cmd || nextCmd ==
CMD_VALUE)
824 p1 = getRestOfLine();
825 if (openCommand(cmd))
829 if (closeCommand(cmd)) {
841 QString rest = getRestOfLine();
846 if (cmd == CMD_PRINTLINE)
847 appendToCode(m_quoter.quoteLine(location(), cmdStr, rest), atomType, codeLanguage);
848 else if (cmd == CMD_PRINTTO)
849 appendToCode(m_quoter.quoteTo(location(), cmdStr, rest), atomType, codeLanguage);
851 appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest), atomType, codeLanguage);
855 if (openCommand(cmd)) {
863 codeLanguage = getLanguageArgument(&marker);
866 QString fileName = getArgument();
872 marker = CodeMarker::markerForFileName(fileName);
874 quoteFromFile(fileName, marker);
876 appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString()), codeLanguage));
883 p1 = getRestOfLine();
885 location().warning(QStringLiteral(
"Missing format name after '\\%1'")
893 if (m_openedCommands.top() == CMD_TABLE) {
895 if (isLeftBraceAhead())
896 p1 = getArgument(ArgumentParsingOptions::Verbatim);
901 if (m_openedCommands.contains(CMD_TABLE))
902 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
904 cmdName(m_openedCommands.top())));
906 location().warning(QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
926 if (openCommand(cmd)) {
933 QString rest = getRestOfLine();
938 m_quoter.quoteLine(location(), cmdStr, rest);
943 QString rest = getRestOfLine();
948 m_quoter.quoteTo(location(), cmdStr, rest);
953 QString rest = getRestOfLine();
958 m_quoter.quoteUntil(location(), cmdStr, rest);
963 startFormat(p1, cmd);
967 codeLanguage = getLanguageArgument(&marker);
970 QString snippet = getArgument();
971 QString identifier = getRestOfLine();
978 marker = CodeMarker::markerForFileName(snippet);
980 quoteFromFile(snippet, marker);
981 appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType(), codeLanguage);
992 p1 = getOptionalArgument();
993 p2 = getOptionalArgument();
994 if (openCommand(cmd)) {
996 appendAtom(Atom(Atom::TableLeft, p1, p2));
997 m_inTableHeader =
false;
998 m_inTableRow =
false;
999 m_inTableItem =
false;
1003 location().report(
"\\%1 is deprecated and will be removed in a future version."_L1.arg(cmdName(cmd)));
1004 if (isLeftBraceAhead())
1008 if (m_openedCommands.top() == CMD_TABLE && !m_inTableItem) {
1009 location().warning(
"Found a \\target command outside table item in a table.\n"
1010 "Move the \\target inside the \\li to resolve this warning.");
1012 insertTarget(getRestOfLine());
1016 enterPara(Atom::TitleLeft, Atom::TitleRight);
1020 if (m_paragraphState != InSingleLineParagraph)
1024 if (openCommand(cmd)) {
1030 if (closeCommand(cmd)) {
1048 uint unicodeChar = p1.toUInt(&ok, 0);
1049 if (!ok || (unicodeChar == 0x0000) || (unicodeChar > 0xFFFE))
1051 QStringLiteral(
"Invalid Unicode character '%1' specified with '%2'")
1059 if (m_openedLists.top().style() == OpenedList::Value) {
1062 if (p1.startsWith(QLatin1String(
"[since "))
1063 && p1.endsWith(QLatin1String(
"]"))) {
1064 p2 = p1.mid(7, p1.size() - 8);
1067 if (!m_private->m_enumItemList.contains(p1))
1068 m_private->m_enumItemList.append(p1);
1070 m_openedLists.top().next();
1074 if (!p2.isEmpty()) {
1081 skipSpacesOrOneEndl();
1090 enterPara(Atom::WarningLeft, Atom::WarningRight);
1096 if (metaCommandSet.contains(cmdStr)) {
1098 QString bracketedArg;
1099 m_private->m_metacommandsUsed.insert(cmdStr);
1100 if (isLeftBracketAhead())
1101 bracketedArg = getBracketedArgument();
1104 if (m_position < m_inputLength
1105 && (cmdStr == QLatin1String(
"obsolete")
1106 || cmdStr == QLatin1String(
"deprecated")))
1107 m_input[m_position] =
'\n';
1109 arg = getMetaCommandArgument(cmdStr);
1111 if (possibleTopics.contains(cmdStr)) {
1112 if (!cmdStr.endsWith(QLatin1String(
"propertygroup")))
1115 }
else if (s_utilities
.macroHash.contains(cmdStr)) {
1117 QStringList macroArgs;
1118 int numPendingFi = 0;
1119 int numFormatDefs = 0;
1120 for (
auto it = macro.m_otherDefs.constBegin();
1121 it != macro.m_otherDefs.constEnd(); ++it) {
1122 if (it.key() !=
"match") {
1123 if (numFormatDefs == 0)
1124 macroArgs = getMacroArguments(cmdStr, macro);
1126 expandMacro(*it, macroArgs);
1128 if (it == macro.m_otherDefs.constEnd()) {
1136 while (numPendingFi-- > 0)
1139 if (!macro.m_defaultDef.isEmpty()) {
1140 if (numFormatDefs > 0) {
1141 macro.m_defaultDefLocation.warning(
1142 QStringLiteral(
"Macro cannot have both "
1143 "format-specific and qdoc-"
1144 "syntax definitions"));
1146 QString expanded = expandMacroToString(cmdStr, macro);
1147 m_input.replace(m_backslashPosition,
1148 m_endPosition - m_backslashPosition, expanded);
1149 m_inputLength = m_input.size();
1150 m_position = m_backslashPosition;
1153 }
else if (isAutoLinkString(cmdStr)) {
1156 if (!cmdStr.endsWith(
"propertygroup")) {
1159 location().warning(QStringLiteral(
"Unknown command '\\%1'").arg(cmdStr),
1160 detailsUnknownCommand(metaCommandSet, cmdStr));
1171 qsizetype dashCount = 1;
1175 while ((m_position < m_inputLength) && (m_input.at(m_position) ==
'-')) {
1180 if (dashCount == 3) {
1182 const QChar emDash(8212);
1184 }
else if (dashCount == 2) {
1186 const QChar enDash(8211);
1192 for (qsizetype i = 0; i < dashCount; ++i)
1201 if (m_paragraphState != InBraceDelimitedParagraph)
1210 auto format = m_pendingFormats.find(m_braceDepth);
1211 if (format == m_pendingFormats.end()) {
1212 if (m_paragraphState == InBraceDelimitedParagraph) {
1222 if (m_indexStartedParagraph)
1227 if (currentLinkAtom && currentLinkAtom->string().endsWith(
"::")) {
1231 currentLinkAtom->concatenateString(suffix);
1233 currentLinkAtom =
nullptr;
1237 m_pendingFormats.erase(format);
1243 if (m_position + 2 < m_inputLength)
1244 if (m_input.at(m_position + 1) ==
'/')
1245 if (m_input.at(m_position + 2) ==
'!') {
1248 if (m_input.at(m_position - 1) ==
'\n')
1264 if (m_paragraphState == OutsideParagraph) {
1276 && (m_paragraphState == InSingleLineParagraph || isBlankLine())) {
1289 qsizetype startPos = m_position;
1291 bool autolink = (!m_pendingFormats.isEmpty() &&
1293 || m_paragraphState == InSingleLineParagraph ?
1294 false : isAutoLinkString(m_input, m_position);
1295 if (m_position == startPos) {
1296 if (!ch.isSpace()) {
1301 QString word = m_input.mid(startPos, m_position - startPos);
1303 if (s_ignoreWords.contains(word) || word.startsWith(QString(
"__")))
1318 if (m_openedCommands.top() == CMD_LEGALESE) {
1320 m_openedCommands.pop();
1323 if (m_openedCommands.top() != CMD_OMIT) {
1325 QStringLiteral(
"Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
1326 }
else if (preprocessorSkipping.size() > 0) {
1327 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(
CMD_ENDIF)));
1331 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1339
1340
1343 while (!m_openedInputs.isEmpty() && m_openedInputs.top() <= m_position) {
1344 m_cachedLocation.pop();
1345 m_cachedPosition = m_openedInputs.pop();
1347 while (m_cachedPosition < m_position)
1348 m_cachedLocation.advance(m_input.at(m_cachedPosition++));
1349 return m_cachedLocation;
1352QString
DocParser::detailsUnknownCommand(
const QSet<QString> &metaCommandSet,
const QString &str)
1354 QSet<QString> commandSet = metaCommandSet;
1356 while (cmds[i]
.name !=
nullptr) {
1357 commandSet.insert(cmds[i]
.name);
1361 return "Maybe you meant '\\%1'?"_L1.arg(suggestName(str, commandSet));
1365
1366
1367
1368
1369
1370
1371
1373 const QString &cmdString,
const QString &previousDefinition)
1375 if (duplicateDefinition.isEmpty()) {
1376 location.warning(
"Expected an argument for \\%1"_L1.arg(cmdString));
1379 "Duplicate %3 name '%1'. The previous occurrence is here: %2"_L1
1380 .arg(duplicateDefinition, previousDefinition, cmdString));
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398void DocParser::insertTarget(
const QString &target)
1400 if (target.isEmpty() || m_targetMap.contains(target))
1401 return warnAboutEmptyOrPreexistingTarget(location(), target,
1402 s_utilities.cmdHash.key(CMD_TARGET), m_targetMap[target].toString());
1404 m_targetMap.insert(target, location());
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425void DocParser::insertKeyword(
const QString &keyword)
1427 if (keyword.isEmpty() || m_targetMap.contains(keyword))
1428 return warnAboutEmptyOrPreexistingTarget(location(), keyword,
1429 s_utilities.cmdHash.key(CMD_KEYWORD), m_targetMap[keyword].toString());
1431 m_targetMap.insert(keyword, location());
1438void DocParser::include(
const QString &fileName,
const QString &identifier,
const QStringList ¶meters)
1441 location().fatal(QStringLiteral(
"Too many nested '\\%1's").arg(cmdName(
CMD_INCLUDE)));
1442 QString filePath = Config::instance().getIncludeFilePath(fileName);
1443 if (filePath.isEmpty()) {
1444 location().warning(QStringLiteral(
"Cannot find qdoc include file '%1'").arg(fileName));
1446 QFile inFile(filePath);
1447 if (!inFile.open(QFile::ReadOnly)) {
1449 QStringLiteral(
"Cannot open qdoc include file '%1'").arg(filePath));
1451 location().push(fileName);
1452 QTextStream inStream(&inFile);
1453 QString includedContent = inStream.readAll();
1456 if (identifier.isEmpty()) {
1457 expandArgumentsInString(includedContent, parameters);
1458 m_input.insert(m_position, includedContent);
1459 m_inputLength = m_input.size();
1460 m_openedInputs.push(m_position + includedContent.size());
1462 auto isSnippetMarker = [&identifier](QStringView trimmedLine) ->
bool {
1463 if (!trimmedLine.startsWith(QLatin1String(
"//!")))
1465 auto bracketStart = trimmedLine.indexOf(QLatin1Char(
'['));
1466 auto bracketEnd = trimmedLine.indexOf(QLatin1Char(
']'));
1467 if (bracketStart < 0 || bracketEnd <= bracketStart)
1469 auto name = trimmedLine.mid(bracketStart + 1, bracketEnd - bracketStart - 1)
1471 return name == identifier;
1474 QStringList lineBuffer = includedContent.split(QLatin1Char(
'\n'));
1475 qsizetype bufLen{lineBuffer.size()};
1477 QStringView trimmedLine;
1478 for (i = 0; i < bufLen; ++i) {
1479 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1480 if (isSnippetMarker(trimmedLine))
1483 if (i < bufLen - 1) {
1487 QStringLiteral(
"Cannot find '%1' in '%2'").arg(identifier, filePath));
1492 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1493 if (isSnippetMarker(trimmedLine))
1496 result += lineBuffer[i] + QLatin1Char(
'\n');
1498 }
while (i < bufLen);
1500 expandArgumentsInString(result, parameters);
1501 if (result.isEmpty()) {
1502 location().warning(QStringLiteral(
"Empty qdoc snippet '%1' in '%2'")
1503 .arg(identifier, filePath));
1505 m_input.insert(m_position, result);
1506 m_inputLength = m_input.size();
1507 m_openedInputs.push(m_position + result.size());
1514void DocParser::startFormat(
const QString &format,
int cmd)
1518 for (
const auto &item : std::as_const(m_pendingFormats)) {
1519 if (item == format) {
1520 location().warning(QStringLiteral(
"Cannot nest '\\%1' commands").arg(cmdName(cmd)));
1527 if (isLeftBraceAhead()) {
1528 skipSpacesOrOneEndl();
1529 m_pendingFormats.insert(m_braceDepth, format);
1533 const auto &arg{getArgument()};
1538 m_indexStartedParagraph =
false;
1547 int outer = m_openedCommands.top();
1550 if ((cmd == CMD_COMPARESWITH || cmd == CMD_TOC)
1551 && m_openedCommands.contains(cmd)) {
1552 location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
1569 m_openedCommands.push(cmd);
1572 QStringLiteral(
"Can't use '\\%1' in '\\%2'").arg(cmdName(cmd), cmdName(outer)));
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654inline bool DocParser::isAutoLinkString(
const QString &word)
1656 qsizetype start = 0;
1657 return isAutoLinkString(word, start) && (start == word.size());
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681bool DocParser::isAutoLinkString(
const QString &word, qsizetype &curPos)
1683 qsizetype len = word.size();
1684 qsizetype startPos = curPos;
1685 int numUppercase = 0;
1686 int numLowercase = 0;
1687 int numStrangeSymbols = 0;
1689 while (curPos < len) {
1690 unsigned char latin1Ch = word.at(curPos).toLatin1();
1691 if (islower(latin1Ch)) {
1694 }
else if (isupper(latin1Ch)) {
1695 if (curPos > startPos)
1698 }
else if (isdigit(latin1Ch)) {
1699 if (curPos > startPos)
1703 }
else if (latin1Ch ==
'_' || latin1Ch ==
'@') {
1704 ++numStrangeSymbols;
1706 }
else if ((latin1Ch ==
':') && (curPos < len - 1)
1707 && (word.at(curPos + 1) == QLatin1Char(
':'))) {
1708 ++numStrangeSymbols;
1710 }
else if (latin1Ch ==
'(') {
1711 if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(
')'))) {
1712 ++numStrangeSymbols;
1722 return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
1727 if (endCmdFor(m_openedCommands.top()) == endCmd && m_openedCommands.size() > 1) {
1728 m_openedCommands.pop();
1731 bool contains =
false;
1732 QStack<
int> opened2 = m_openedCommands;
1733 while (opened2.size() > 1) {
1742 while (endCmdFor(m_openedCommands.top()) != endCmd && m_openedCommands.size() > 1) {
1744 QStringLiteral(
"Missing '\\%1' before '\\%2'")
1745 .arg(endCmdName(m_openedCommands.top()), cmdName(endCmd)));
1746 m_openedCommands.pop();
1749 location().warning(QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(endCmd)));
1760 m_currentSection =
static_cast<
Doc::
Sections>(unit);
1763 endSection(unit, cmd);
1766 appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
1769 m_private
->extra->m_tableOfContentsLevels.append(unit);
1770 enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit));
1771 m_currentSection = unit;
1777 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1811 const QString imageFileName = getArgument();
1813 bool hasAltTextArgument{
false};
1814 if (isLeftBraceAhead()) {
1815 hasAltTextArgument =
true;
1816 imageText = getArgument();
1818 imageText = getRestOfLine();
1821 if (imageText.length() > 1) {
1822 if (imageText.front() ==
'"' && imageText.back() ==
'"') {
1823 imageText.removeFirst();
1824 imageText.removeLast();
1828 if (!hasAltTextArgument && imageText.isEmpty() && Config::instance().reportMissingAltTextForImages())
1829 location().report(QStringLiteral(
"\\%1 %2 is without a textual description, "
1830 "QDoc will not generate an alt text for the image.")
1832 .arg(imageFileName));
1833 appendAtom(
Atom(imageAtom, imageFileName));
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1852 const QString cmd{
"overload"};
1855 m_private->m_metacommandsUsed.insert(cmd);
1856 QString overloadArgument = isBlankLine() ? getMetaCommandArgument(cmd) : getRestOfLine();
1861 if (overloadArgument.trimmed() ==
"primary")
1862 overloadArgument =
"__qdoc_primary_overload__"_L1;
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1882 auto line_comment = [
this]() ->
bool {
1884 if (m_position + 2 > m_inputLength)
1886 if (m_input[m_position].unicode() ==
'/') {
1887 if (m_input[m_position + 1].unicode() ==
'/') {
1888 if (m_input[m_position + 2].unicode() ==
'!') {
1896 auto skip_everything_until_newline = [
this]() ->
void {
1897 while (m_position < m_inputLength && m_input[m_position] !=
'\n')
1903 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
1906 bool skipMe =
false;
1908 if (m_input[m_position] ==
'{') {
1909 target = getArgument();
1910 if (isLeftBraceAhead()) {
1911 str = getArgument();
1914 if (target.endsWith(
"::"))
1920 target = getArgument();
1921 str = cleanLink(target);
1922 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1928 also <<
LinkAtom(target, {}, location())
1937 skip_everything_until_newline();
1939 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1942 skip_everything_until_newline();
1943 skipSpacesOrOneEndl();
1944 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1945 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1963 if (ch == QLatin1Char(
' ')) {
1964 if (!atom->string().endsWith(QLatin1Char(
' ')))
1965 atom->appendChar(QLatin1Char(
' '));
1967 atom->appendChar(ch);
1970void DocParser::appendWord(
const QString &word)
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990void DocParser::appendEscapedIdentifier()
1992 Q_ASSERT(m_position < m_inputLength);
1993 Q_ASSERT(m_input.at(m_position) ==
'\\');
1998 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
1999 identifier += m_input.at(m_position);
2004 if (!identifier.isEmpty()) {
2009 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
2012 appendChar(QLatin1Char(
'\\'));
2016void DocParser::appendToCode(
const QString &markedCode)
2018 if (!isCode(m_lastAtom)) {
2022 m_lastAtom->concatenateString(markedCode);
2026
2027
2028
2029
2030
2031void DocParser::appendToCode(
const QString &markedCode,
Atom::
AtomType defaultType,
const QString &language)
2033 if (!isCode(m_lastAtom)) {
2034 appendAtom(
Atom(defaultType, markedCode, language));
2037 m_lastAtom->concatenateString(markedCode);
2043 if (m_paragraphState != OutsideParagraph)
2054 const bool inValueList = !m_openedLists.isEmpty()
2055 && m_openedLists.top().style() == OpenedList::Value;
2059 if (!inValueList || !isAdmonition)
2063 appendAtom(
Atom(leftType, string));
2064 m_indexStartedParagraph =
false;
2065 m_pendingParagraphLeftType = leftType;
2066 m_pendingParagraphRightType = rightType;
2067 m_pendingParagraphString = string;
2069 m_paragraphState = InSingleLineParagraph;
2071 m_paragraphState = InBraceDelimitedParagraph;
2073 m_paragraphState = InMultiLineParagraph;
2075 skipSpacesOrOneEndl();
2080 if (m_paragraphState == OutsideParagraph)
2082 if (!m_pendingFormats.isEmpty() || (m_paragraphState == InBraceDelimitedParagraph && m_braceDepth > 0)) {
2083 location().warning(u"Missing '}'"_s);
2084 m_pendingFormats.clear();
2100 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2103 m_paragraphState = OutsideParagraph;
2104 m_indexStartedParagraph =
false;
2105 m_pendingParagraphRightType =
Atom::Nop;
2106 m_pendingParagraphString.clear();
2112 if (m_openedLists.isEmpty()) {
2113 m_openedLists.push(OpenedList(OpenedList::Value));
2125 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2130 m_openedLists.pop();
2136 if (m_inTableItem) {
2139 m_inTableItem =
false;
2141 if (m_inTableHeader) {
2143 m_inTableHeader =
false;
2147 m_inTableRow =
false;
2152
2153
2154
2155
2169 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2170 if (!maybe_resolved_file) {
2179 QString details =
std::transform_reduce(
2180 (*file_resolver).get_search_directories().cbegin(),
2181 (*file_resolver).get_search_directories().cend(),
2182 u"Searched directories:"_s,
2184 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2187 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2204 marker = CodeMarker::markerForFileName(QString{});
2205 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235bool DocParser::expandMacro(ArgumentParsingOptions options)
2237 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2239 if (options == ArgumentParsingOptions::Verbatim)
2243 qsizetype backslashPos = m_position++;
2244 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2245 cmdStr += m_input[m_position++];
2247 m_endPosition = m_position;
2248 if (!cmdStr.isEmpty()) {
2249 if (s_utilities
.macroHash.contains(cmdStr)) {
2251 if (!macro.m_defaultDef.isEmpty()) {
2252 QString expanded = expandMacroToString(cmdStr, macro);
2253 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2254 m_inputLength = m_input.size();
2255 m_position = backslashPos;
2258 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2262 m_position = backslashPos;
2263 if (options != ArgumentParsingOptions::MacroArguments
2265 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2269 }
else if (m_input[m_position].isSpace()) {
2271 }
else if (m_input[m_position].unicode() ==
'\\') {
2273 m_input.remove(m_position--, 1);
2279void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2281 if (args.isEmpty()) {
2286 for (
int j = 0; j < def.size(); ++j) {
2287 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2288 if (!rawString.isEmpty()) {
2294 rawString += def[j];
2297 if (!rawString.isEmpty())
2302QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2304 const QString &def{macro.m_defaultDef};
2308 rawString = macro.m_defaultDef;
2310 QStringList args{getMacroArguments(name, macro)};
2312 for (
int j = 0; j < def.size(); ++j) {
2313 int paramNo = def[j].unicode();
2314 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2317 QString matchExpr{macro.m_otherDefs.value(
"match")};
2318 if (matchExpr.isEmpty())
2322 QRegularExpression re(matchExpr);
2323 int capStart = (re.captureCount() > 0) ? 1 : 0;
2325 QRegularExpressionMatch match;
2326 while ((match = re.match(rawString, i)).hasMatch()) {
2327 for (
int c = capStart; c <= re.captureCount(); ++c)
2328 result += match.captured(c);
2329 i = match.capturedEnd();
2337 QString name = getOptionalArgument();
2339 if (name ==
"section1") {
2341 }
else if (name ==
"section2") {
2343 }
else if (name ==
"section3") {
2345 }
else if (name ==
"section4") {
2347 }
else if (name.isEmpty()) {
2350 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2369 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2371 while (m_position < m_input.size() && delimDepth >= 0) {
2372 switch (m_input[m_position].unicode()) {
2375 arg += QLatin1Char(
'{');
2380 if (delimDepth >= 0)
2381 arg += QLatin1Char(
'}');
2385 if (!expandMacro(options))
2386 arg += m_input[m_position++];
2389 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2392 arg += m_input[m_position];
2397 location().warning(QStringLiteral(
"Missing '}'"));
2399 m_endPosition = m_position;
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419QString
DocParser::getArgument(ArgumentParsingOptions options)
2421 skipSpacesOrOneEndl();
2424 qsizetype startPos = m_position;
2425 QString arg = getBracedArgument(options);
2426 if (arg.isEmpty()) {
2427 while ((m_position < m_input.size())
2428 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2429 switch (m_input[m_position].unicode()) {
2434 arg += m_input[m_position];
2441 if (m_position == startPos || delimDepth >= 0) {
2442 arg += m_input[m_position];
2447 if (!expandMacro(options))
2448 arg += m_input[m_position++];
2451 arg += m_input[m_position];
2455 m_endPosition = m_position;
2456 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2457 && !arg.endsWith(
"...")) {
2458 arg.truncate(arg.size() - 1);
2461 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2462 arg.truncate(arg.size() - 2);
2466 return arg.simplified();
2470
2471
2472
2473
2474
2475QString
DocParser::getBracketedArgument()
2479 skipSpacesOrOneEndl();
2480 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2482 while (m_position < m_input.size() && delimDepth >= 0) {
2483 switch (m_input[m_position].unicode()) {
2486 arg += QLatin1Char(
'[');
2491 if (delimDepth >= 0)
2492 arg += QLatin1Char(
']');
2496 arg += m_input[m_position];
2500 arg += m_input[m_position];
2505 location().warning(QStringLiteral(
"Missing ']'"));
2511
2512
2513
2514
2515
2516
2517
2521 if (isLeftBracketAhead(0)) {
2522 value = getBracketedArgument();
2523 *marker = markerForLanguage(value);
2528 return value.toLower();
2532
2533
2534
2535
2536
2537QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2541 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2542 args << getArgument(ArgumentParsingOptions::MacroArguments);
2544 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2545 " arguments (expected %2, got %3)")
2556
2557
2558
2559
2562 skipSpacesOrOneEndl();
2563 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2564 && m_input[m_position + 1].isLetterOrNumber()) {
2567 return getArgument();
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2588 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2589 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2590 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2591 trailingBackslash =
true;
2595 trailingBackslash =
false;
2599 return trailingBackslash;
2602 QString rest_of_line;
2604 bool trailing_backslash{
false };
2605 bool return_simplified_string{
false };
2607 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2608 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2610 if (!rest_of_line.isEmpty())
2611 rest_of_line += QLatin1Char(
' ');
2612 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2614 if (trailing_backslash) {
2615 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2616 return_simplified_string =
true;
2619 if (m_position < m_inputLength)
2622 if (!trailing_backslash)
2624 start_position = m_position;
2627 if (return_simplified_string)
2628 return rest_of_line.simplified();
2630 return rest_of_line.trimmed();
2634
2635
2636
2637
2638QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2642 qsizetype begin = m_position;
2645 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2646 if (m_input.at(m_position) ==
'(')
2648 else if (m_input.at(m_position) ==
')')
2650 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2654 if (m_position == m_input.size() && parenDepth > 0) {
2656 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2659 QString t = m_input.mid(begin, m_position - begin).simplified();
2667 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2669 auto match = rx.match(m_input, m_position);
2671 if (!match.hasMatch()) {
2672 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2673 m_position = m_input.size();
2675 qsizetype end = match.capturedStart();
2676 t = m_input.mid(m_position, end - m_position);
2677 m_position = match.capturedEnd();
2682void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2689 while (j < str.size()) {
2690 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2691 && paramNo <= args.size()) {
2692 const QString &r = args[paramNo - 1];
2693 str.replace(j, 2, r);
2694 j += qMin(1, r.size());
2702
2703
2704
2705
2706
2707
2710 QString code = untabifyEtc(getUntilEnd(cmd));
2711 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2713 int indent = indentLevel(code);
2714 code = dedent(indent, code);
2717 if (!marker && !m_private->m_topics.isEmpty()
2718 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2719 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2720 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2722 if (marker ==
nullptr)
2723 marker = CodeMarker::markerForCode(code);
2724 return marker->markedUpCode(code,
nullptr, location());
2729 qsizetype i = m_position;
2731 while (i < m_inputLength && m_input[i].isSpace()) {
2732 if (m_input[i] ==
'\n')
2742 qsizetype i = m_position;
2744 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2746 if (m_input[i] ==
'\n')
2750 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2753bool DocParser::isLeftBracketAhead(
int maxNewlines)
2756 qsizetype i = m_position;
2758 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2760 if (m_input[i] ==
'\n')
2764 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2768
2769
2772 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2773 && (m_input[m_position].unicode() !=
'\n'))
2778
2779
2782 qsizetype firstEndl = -1;
2783 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2784 QChar ch = m_input[m_position];
2786 if (firstEndl == -1) {
2787 firstEndl = m_position;
2789 m_position = firstEndl;
2799 while (m_position < m_inputLength && m_input[m_position].isSpace())
2803void DocParser::skipToNextPreprocessorCommand()
2805 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2806 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2807 auto match = rx.match(m_input, m_position + 1);
2809 if (!match.hasMatch())
2810 m_position = m_input.size();
2812 m_position = match.capturedStart();
2865 return cmds[cmd]
.name;
2870 return cmdName(endCmdFor(cmd));
2876 result.reserve(str.size());
2879 for (
const auto &character : str) {
2880 if (character == QLatin1Char(
'\r'))
2882 if (character == QLatin1Char(
'\t')) {
2883 result += &
" "[column % s_tabSize];
2884 column = ((column / s_tabSize) + 1) * s_tabSize;
2887 if (character == QLatin1Char(
'\n')) {
2888 while (result.endsWith(QLatin1Char(
' ')))
2890 result += character;
2894 result += character;
2898 while (result.endsWith(
"\n\n"))
2899 result.truncate(result.size() - 1);
2900 while (result.startsWith(QLatin1Char(
'\n')))
2901 result = result.mid(1);
2908 int minIndent = INT_MAX;
2911 for (
const auto &character : str) {
2912 if (character ==
'\n') {
2915 if (character !=
' ' && column < minIndent)
2931 for (
const auto &character : str) {
2932 if (character == QLatin1Char(
'\n')) {
2936 if (column >= level)
2937 result += character;
2945
2946
2954
2955
2965
2966
2967
2968
2971 CodeMarker *marker = CodeMarker::markerForLanguage(language.toLower());
2973 if (!marker && !s_allowedLanguages.contains(language, Qt::CaseInsensitive))
2974 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(language));
2980
2981
2982
2983
2984
2985
2986
2987
2990 static auto take_while = [](QStringView input,
auto predicate) {
2991 QStringView::size_type end{0};
2993 while (end < input.size() &&
std::invoke(predicate, input[end]))
2996 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2999 static auto peek = [](QStringView input, QChar c) {
3000 return !input.empty() && input.first() == c;
3003 static auto skip_one = [](QStringView input) {
3004 if (input.empty())
return std::make_tuple(QStringView{}, input);
3005 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
3008 static auto enclosed = [](QStringView input, QChar open, QChar close) {
3009 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
3011 auto [opened, without_open] = skip_one(input);
3012 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
3014 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
3016 auto [closed, without_close] = skip_one(remaining);
3018 return std::make_tuple(parsed.trimmed(), without_close);
3021 static auto one_of = [](
auto first,
auto second) {
3022 return [first, second](QStringView input) {
3023 auto [parsed, remaining] =
std::invoke(first, input);
3025 if (parsed.empty())
return std::invoke(second, input);
3026 else return std::make_tuple(parsed, remaining);
3030 static auto collect = [](QStringView input,
auto parser) {
3031 QStringList collected{};
3034 auto [parsed, remaining] =
std::invoke(parser, input);
3036 if (parsed.empty())
break;
3037 collected.append(parsed.toString());
3045 static auto spaces = [](QStringView input) {
3046 return take_while(input, [](QChar c){
return c.isSpace(); });
3049 static auto word = [](QStringView input) {
3050 return take_while(input, [](QChar c){
return !c.isSpace(); });
3053 static auto parse_argument = [](QStringView input) {
3054 auto [_, without_spaces] = spaces(input);
3057 [](QStringView input){
return enclosed(input,
'{',
'}'); },
3062 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
3066 QStringList segments = collect(atom->string(), parse_argument);
3068 QString categoryString;
3069 if (!segments.isEmpty())
3070 categoryString = segments.takeFirst();
3071 auto category = comparisonCategoryFromString(categoryString.toStdString());
3073 if (category == ComparisonCategory::None) {
3074 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
3075 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3079 if (segments.isEmpty()) {
3080 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3081 u"Provide at least one type name, or a list of types separated by spaces."_s);
3086 segments.removeDuplicates();
3087 atom->setString(segments.join(QLatin1Char(
';')));
3091 priv->m_metacommandsUsed.insert(cmd);
3094 const auto end{priv
->extra->m_comparesWithMap.cend()};
3095 priv
->extra->m_comparesWithMap.insert(end, category, text);
#define ATOM_FORMATTING_TELETYPE
#define ATOM_FORMATTING_UNDERLINE
#define ATOM_FORMATTING_NOTRANSLATE
#define ATOM_FORMATTING_SPAN
#define ATOM_FORMATTING_SUBSCRIPT
#define ATOM_FORMATTING_BOLD
#define ATOM_FORMATTING_TRADEMARK
#define ATOM_FORMATTING_ITALIC
#define ATOM_FORMATTING_LINK
#define ATOM_FORMATTING_SUPERSCRIPT
#define ATOM_FORMATTING_INDEX
#define ATOM_FORMATTING_UICONTROL
#define ATOM_FORMATTING_PARAMETER
The Atom class is the fundamental unit for representing documents internally.
AtomType type() const
Return the type of this atom.
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
void chopString()
\also string()
virtual Atom::AtomType atomType() const
The Config class contains the configuration variables for controlling how qdoc produces documentation...
static QStringList s_allowedLanguages
static QStringList s_ignoreWords
void parse(const QString &source, DocPrivate *docPrivate, const QSet< QString > &metaCommandSet, const QSet< QString > &possibleTopics)
Parse the source string to build a Text data structure in docPrivate.
static int endCmdFor(int cmd)
static void initialize(const Config &config, FileResolver &file_resolver)
void addAlso(const Text &also)
CommandMap m_metaCommandMap
static void quoteFromFile(const Location &location, Quoter "er, ResolvedFile resolved_file, CodeMarker *marker=nullptr)
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
The Location class provides a way to mark a location in a file.
static Text subText(const Atom *begin, const Atom *end=nullptr)
Text splitAtFirst(Atom::AtomType start)
Splits the current Text from start to end into a new Text object.
Text & operator=(const Text &text)
#define CONFIG_IGNOREWORDS
#define CONFIG_CODELANGUAGES
#define CONFIG_QUOTINGINFORMATION
std::pair< QString, QString > ArgPair
static void warnAboutEmptyOrPreexistingTarget(const Location &location, const QString &duplicateDefinition, const QString &cmdString, const QString &previousDefinition)
bool is_formatting_command
static void processComparesWithCommand(DocPrivate *priv, const Location &location)
static QString cleanLink(const QString &link)
Returns a cleaned version of the given link text.
Combined button and popup list for selecting options.
QHash_QString_Macro macroHash
QHash_QString_int cmdHash
Simple structure used by the Doc and DocParser classes.