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)) {
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(
"."))
1936 skip_everything_until_newline();
1938 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1941 skip_everything_until_newline();
1942 skipSpacesOrOneEndl();
1943 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1944 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1962 if (ch == QLatin1Char(
' ')) {
1963 if (!atom->string().endsWith(QLatin1Char(
' ')))
1964 atom->appendChar(QLatin1Char(
' '));
1966 atom->appendChar(ch);
1969void DocParser::appendWord(
const QString &word)
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989void DocParser::appendEscapedIdentifier()
1991 Q_ASSERT(m_position < m_inputLength);
1992 Q_ASSERT(m_input.at(m_position) ==
'\\');
1997 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
1998 identifier += m_input.at(m_position);
2003 if (!identifier.isEmpty()) {
2008 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
2011 appendChar(QLatin1Char(
'\\'));
2015void DocParser::appendToCode(
const QString &markedCode)
2017 if (!isCode(m_lastAtom)) {
2021 m_lastAtom->concatenateString(markedCode);
2025
2026
2027
2028
2029
2030void DocParser::appendToCode(
const QString &markedCode,
Atom::
AtomType defaultType,
const QString &language)
2032 if (!isCode(m_lastAtom)) {
2033 appendAtom(
Atom(defaultType, markedCode, language));
2036 m_lastAtom->concatenateString(markedCode);
2042 if (m_paragraphState != OutsideParagraph)
2051 appendAtom(
Atom(leftType, string));
2052 m_indexStartedParagraph =
false;
2053 m_pendingParagraphLeftType = leftType;
2054 m_pendingParagraphRightType = rightType;
2055 m_pendingParagraphString = string;
2057 m_paragraphState = InSingleLineParagraph;
2059 m_paragraphState = InBraceDelimitedParagraph;
2061 m_paragraphState = InMultiLineParagraph;
2063 skipSpacesOrOneEndl();
2068 if (m_paragraphState == OutsideParagraph)
2070 if (!m_pendingFormats.isEmpty() || (m_paragraphState == InBraceDelimitedParagraph && m_braceDepth > 0)) {
2071 location().warning(u"Missing '}'"_s);
2072 m_pendingFormats.clear();
2088 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2091 m_paragraphState = OutsideParagraph;
2092 m_indexStartedParagraph =
false;
2093 m_pendingParagraphRightType =
Atom::Nop;
2094 m_pendingParagraphString.clear();
2100 if (m_openedLists.isEmpty()) {
2101 m_openedLists.push(OpenedList(OpenedList::Value));
2113 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2118 m_openedLists.pop();
2124 if (m_inTableItem) {
2127 m_inTableItem =
false;
2129 if (m_inTableHeader) {
2131 m_inTableHeader =
false;
2135 m_inTableRow =
false;
2140
2141
2142
2143
2157 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2158 if (!maybe_resolved_file) {
2167 QString details =
std::transform_reduce(
2168 (*file_resolver).get_search_directories().cbegin(),
2169 (*file_resolver).get_search_directories().cend(),
2170 u"Searched directories:"_s,
2172 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2175 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2192 marker = CodeMarker::markerForFileName(QString{});
2193 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223bool DocParser::expandMacro(ArgumentParsingOptions options)
2225 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2227 if (options == ArgumentParsingOptions::Verbatim)
2231 qsizetype backslashPos = m_position++;
2232 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2233 cmdStr += m_input[m_position++];
2235 m_endPosition = m_position;
2236 if (!cmdStr.isEmpty()) {
2237 if (s_utilities
.macroHash.contains(cmdStr)) {
2239 if (!macro.m_defaultDef.isEmpty()) {
2240 QString expanded = expandMacroToString(cmdStr, macro);
2241 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2242 m_inputLength = m_input.size();
2243 m_position = backslashPos;
2246 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2250 m_position = backslashPos;
2251 if (options != ArgumentParsingOptions::MacroArguments
2253 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2257 }
else if (m_input[m_position].isSpace()) {
2259 }
else if (m_input[m_position].unicode() ==
'\\') {
2261 m_input.remove(m_position--, 1);
2267void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2269 if (args.isEmpty()) {
2274 for (
int j = 0; j < def.size(); ++j) {
2275 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2276 if (!rawString.isEmpty()) {
2282 rawString += def[j];
2285 if (!rawString.isEmpty())
2290QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2292 const QString &def{macro.m_defaultDef};
2296 rawString = macro.m_defaultDef;
2298 QStringList args{getMacroArguments(name, macro)};
2300 for (
int j = 0; j < def.size(); ++j) {
2301 int paramNo = def[j].unicode();
2302 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2305 QString matchExpr{macro.m_otherDefs.value(
"match")};
2306 if (matchExpr.isEmpty())
2310 QRegularExpression re(matchExpr);
2311 int capStart = (re.captureCount() > 0) ? 1 : 0;
2313 QRegularExpressionMatch match;
2314 while ((match = re.match(rawString, i)).hasMatch()) {
2315 for (
int c = capStart; c <= re.captureCount(); ++c)
2316 result += match.captured(c);
2317 i = match.capturedEnd();
2325 QString name = getOptionalArgument();
2327 if (name ==
"section1") {
2329 }
else if (name ==
"section2") {
2331 }
else if (name ==
"section3") {
2333 }
else if (name ==
"section4") {
2335 }
else if (name.isEmpty()) {
2338 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2357 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2359 while (m_position < m_input.size() && delimDepth >= 0) {
2360 switch (m_input[m_position].unicode()) {
2363 arg += QLatin1Char(
'{');
2368 if (delimDepth >= 0)
2369 arg += QLatin1Char(
'}');
2373 if (!expandMacro(options))
2374 arg += m_input[m_position++];
2377 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2380 arg += m_input[m_position];
2385 location().warning(QStringLiteral(
"Missing '}'"));
2387 m_endPosition = m_position;
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407QString
DocParser::getArgument(ArgumentParsingOptions options)
2409 skipSpacesOrOneEndl();
2412 qsizetype startPos = m_position;
2413 QString arg = getBracedArgument(options);
2414 if (arg.isEmpty()) {
2415 while ((m_position < m_input.size())
2416 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2417 switch (m_input[m_position].unicode()) {
2422 arg += m_input[m_position];
2429 if (m_position == startPos || delimDepth >= 0) {
2430 arg += m_input[m_position];
2435 if (!expandMacro(options))
2436 arg += m_input[m_position++];
2439 arg += m_input[m_position];
2443 m_endPosition = m_position;
2444 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2445 && !arg.endsWith(
"...")) {
2446 arg.truncate(arg.size() - 1);
2449 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2450 arg.truncate(arg.size() - 2);
2454 return arg.simplified();
2458
2459
2460
2461
2462
2463QString
DocParser::getBracketedArgument()
2467 skipSpacesOrOneEndl();
2468 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2470 while (m_position < m_input.size() && delimDepth >= 0) {
2471 switch (m_input[m_position].unicode()) {
2474 arg += QLatin1Char(
'[');
2479 if (delimDepth >= 0)
2480 arg += QLatin1Char(
']');
2484 arg += m_input[m_position];
2488 arg += m_input[m_position];
2493 location().warning(QStringLiteral(
"Missing ']'"));
2499
2500
2501
2502
2503
2504
2505
2509 if (isLeftBracketAhead(0)) {
2510 value = getBracketedArgument();
2511 *marker = markerForLanguage(value);
2516 return value.toLower();
2520
2521
2522
2523
2524
2525QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2529 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2530 args << getArgument(ArgumentParsingOptions::MacroArguments);
2532 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2533 " arguments (expected %2, got %3)")
2544
2545
2546
2547
2550 skipSpacesOrOneEndl();
2551 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2552 && m_input[m_position + 1].isLetterOrNumber()) {
2555 return getArgument();
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2576 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2577 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2578 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2579 trailingBackslash =
true;
2583 trailingBackslash =
false;
2587 return trailingBackslash;
2590 QString rest_of_line;
2592 bool trailing_backslash{
false };
2593 bool return_simplified_string{
false };
2595 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2596 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2598 if (!rest_of_line.isEmpty())
2599 rest_of_line += QLatin1Char(
' ');
2600 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2602 if (trailing_backslash) {
2603 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2604 return_simplified_string =
true;
2607 if (m_position < m_inputLength)
2610 if (!trailing_backslash)
2612 start_position = m_position;
2615 if (return_simplified_string)
2616 return rest_of_line.simplified();
2618 return rest_of_line.trimmed();
2622
2623
2624
2625
2626QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2630 qsizetype begin = m_position;
2633 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2634 if (m_input.at(m_position) ==
'(')
2636 else if (m_input.at(m_position) ==
')')
2638 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2642 if (m_position == m_input.size() && parenDepth > 0) {
2644 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2647 QString t = m_input.mid(begin, m_position - begin).simplified();
2655 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2657 auto match = rx.match(m_input, m_position);
2659 if (!match.hasMatch()) {
2660 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2661 m_position = m_input.size();
2663 qsizetype end = match.capturedStart();
2664 t = m_input.mid(m_position, end - m_position);
2665 m_position = match.capturedEnd();
2670void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2677 while (j < str.size()) {
2678 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2679 && paramNo <= args.size()) {
2680 const QString &r = args[paramNo - 1];
2681 str.replace(j, 2, r);
2682 j += qMin(1, r.size());
2690
2691
2692
2693
2694
2695
2698 QString code = untabifyEtc(getUntilEnd(cmd));
2699 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2701 int indent = indentLevel(code);
2702 code = dedent(indent, code);
2705 if (!marker && !m_private->m_topics.isEmpty()
2706 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2707 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2708 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2710 if (marker ==
nullptr)
2711 marker = CodeMarker::markerForCode(code);
2712 return marker->markedUpCode(code,
nullptr, location());
2717 qsizetype i = m_position;
2719 while (i < m_inputLength && m_input[i].isSpace()) {
2720 if (m_input[i] ==
'\n')
2730 qsizetype i = m_position;
2732 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2734 if (m_input[i] ==
'\n')
2738 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2741bool DocParser::isLeftBracketAhead(
int maxNewlines)
2744 qsizetype i = m_position;
2746 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2748 if (m_input[i] ==
'\n')
2752 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2756
2757
2760 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2761 && (m_input[m_position].unicode() !=
'\n'))
2766
2767
2770 qsizetype firstEndl = -1;
2771 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2772 QChar ch = m_input[m_position];
2774 if (firstEndl == -1) {
2775 firstEndl = m_position;
2777 m_position = firstEndl;
2787 while (m_position < m_inputLength && m_input[m_position].isSpace())
2791void DocParser::skipToNextPreprocessorCommand()
2793 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2794 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2795 auto match = rx.match(m_input, m_position + 1);
2797 if (!match.hasMatch())
2798 m_position = m_input.size();
2800 m_position = match.capturedStart();
2853 return cmds[cmd]
.name;
2858 return cmdName(endCmdFor(cmd));
2864 result.reserve(str.size());
2867 for (
const auto &character : str) {
2868 if (character == QLatin1Char(
'\r'))
2870 if (character == QLatin1Char(
'\t')) {
2871 result += &
" "[column % s_tabSize];
2872 column = ((column / s_tabSize) + 1) * s_tabSize;
2875 if (character == QLatin1Char(
'\n')) {
2876 while (result.endsWith(QLatin1Char(
' ')))
2878 result += character;
2882 result += character;
2886 while (result.endsWith(
"\n\n"))
2887 result.truncate(result.size() - 1);
2888 while (result.startsWith(QLatin1Char(
'\n')))
2889 result = result.mid(1);
2896 int minIndent = INT_MAX;
2899 for (
const auto &character : str) {
2900 if (character ==
'\n') {
2903 if (character !=
' ' && column < minIndent)
2919 for (
const auto &character : str) {
2920 if (character == QLatin1Char(
'\n')) {
2924 if (column >= level)
2925 result += character;
2933
2934
2942
2943
2953
2954
2955
2956
2959 CodeMarker *marker = CodeMarker::markerForLanguage(language.toLower());
2961 if (!marker && !s_allowedLanguages.contains(language, Qt::CaseInsensitive))
2962 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(language));
2968
2969
2970
2971
2972
2973
2974
2975
2978 static auto take_while = [](QStringView input,
auto predicate) {
2979 QStringView::size_type end{0};
2981 while (end < input.size() &&
std::invoke(predicate, input[end]))
2984 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2987 static auto peek = [](QStringView input, QChar c) {
2988 return !input.empty() && input.first() == c;
2991 static auto skip_one = [](QStringView input) {
2992 if (input.empty())
return std::make_tuple(QStringView{}, input);
2993 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
2996 static auto enclosed = [](QStringView input, QChar open, QChar close) {
2997 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
2999 auto [opened, without_open] = skip_one(input);
3000 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
3002 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
3004 auto [closed, without_close] = skip_one(remaining);
3006 return std::make_tuple(parsed.trimmed(), without_close);
3009 static auto one_of = [](
auto first,
auto second) {
3010 return [first, second](QStringView input) {
3011 auto [parsed, remaining] =
std::invoke(first, input);
3013 if (parsed.empty())
return std::invoke(second, input);
3014 else return std::make_tuple(parsed, remaining);
3018 static auto collect = [](QStringView input,
auto parser) {
3019 QStringList collected{};
3022 auto [parsed, remaining] =
std::invoke(parser, input);
3024 if (parsed.empty())
break;
3025 collected.append(parsed.toString());
3033 static auto spaces = [](QStringView input) {
3034 return take_while(input, [](QChar c){
return c.isSpace(); });
3037 static auto word = [](QStringView input) {
3038 return take_while(input, [](QChar c){
return !c.isSpace(); });
3041 static auto parse_argument = [](QStringView input) {
3042 auto [_, without_spaces] = spaces(input);
3045 [](QStringView input){
return enclosed(input,
'{',
'}'); },
3050 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
3054 QStringList segments = collect(atom->string(), parse_argument);
3056 QString categoryString;
3057 if (!segments.isEmpty())
3058 categoryString = segments.takeFirst();
3059 auto category = comparisonCategoryFromString(categoryString.toStdString());
3061 if (category == ComparisonCategory::None) {
3062 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
3063 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3067 if (segments.isEmpty()) {
3068 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3069 u"Provide at least one type name, or a list of types separated by spaces."_s);
3074 segments.removeDuplicates();
3075 atom->setString(segments.join(QLatin1Char(
';')));
3079 priv->m_metacommandsUsed.insert(cmd);
3082 const auto end{priv
->extra->m_comparesWithMap.cend()};
3083 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.