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))));
432 appendAtom(Atom(Atom::DetailsLeft, getArgument()));
433 m_openedCommands.push(cmd);
442 p1 = getArgument(ArgumentParsingOptions::Verbatim);
444 m_openedCommands.push(cmd);
456 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
461 QString arg = getOptionalArgument();
468 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
471 int indent = arg.toInt();
472 for (
int i = 0; i < indent; ++i)
474 appendToCode(
"...\n");
478 if (!preprocessorSkipping.empty()) {
479 if (preprocessorSkipping.top()) {
480 --numPreprocessorSkipping;
482 ++numPreprocessorSkipping;
484 preprocessorSkipping.top() = !preprocessorSkipping.top();
485 (
void)getRestOfLine();
486 if (numPreprocessorSkipping)
487 skipToNextPreprocessorCommand();
490 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ELSE)));
500 if (closeCommand(cmd)) {
506 if (preprocessorSkipping.size() > 0) {
507 if (preprocessorSkipping.pop())
508 --numPreprocessorSkipping;
509 (
void)getRestOfLine();
510 if (numPreprocessorSkipping)
511 skipToNextPreprocessorCommand();
514 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDIF)));
518 if (closeCommand(cmd)) {
524 if (closeCommand(cmd)) {
532 if (closeCommand(cmd)) {
534 if (m_openedLists.top().isStarted()) {
535 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
536 appendAtom(Atom(Atom::ListRight, m_openedLists.top().styleString()));
545 if (closeCommand(cmd)) {
552 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDRAW)));
567 if (closeCommand(cmd)) {
573 if (closeCommand(cmd)) {
579 if (openCommand(cmd)) {
586 if (isLeftBracketAhead())
587 p2 = getBracketedArgument();
590 appendAtom(Atom(Atom::AnnotatedList, getArgument(), p2));
594 appendAtom(Atom(Atom::SinceList, getRestOfLine().simplified()));
598 if (isLeftBracketAhead())
599 p2 = getBracketedArgument();
602 QString arg1 = getArgument();
603 QString arg2 = getOptionalArgument();
606 appendAtom(Atom(Atom::GeneratedList, arg1, p2));
609 if (m_openedCommands.top() == CMD_TABLE) {
612 m_inTableHeader =
true;
614 if (m_openedCommands.contains(CMD_TABLE))
615 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
617 cmdName(m_openedCommands.top())));
620 QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
625 location().warning(QStringLiteral(
626 "'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
636 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
637 if (preprocessorSkipping.top())
638 ++numPreprocessorSkipping;
639 if (numPreprocessorSkipping)
640 skipToNextPreprocessorCommand();
647 enterPara(Atom::ImportantLeft, Atom::ImportantRight);
651 QString fileName = getArgument();
652 QStringList parameters;
654 if (isLeftBraceAhead()) {
655 identifier = getArgument();
656 while (isLeftBraceAhead() && parameters.size() < 9)
657 parameters << getArgument();
659 identifier = getRestOfLine();
661 include(fileName, identifier, parameters);
668 if (m_paragraphState == OutsideParagraph) {
670 m_indexStartedParagraph =
true;
673 if (m_indexStartedParagraph
676 m_indexStartedParagraph =
false;
682 insertKeyword(getRestOfLine());
685 if (m_openedCommands.top() != CMD_TOC) {
686 location().warning(
"Command '\\%1' outside of '\\%2'"_L1
687 .arg(cmdName(cmd), cmdName(CMD_TOC)));
693 if (isLeftBracketAhead())
694 p2 = getBracketedArgument();
698 appendAtom(LinkAtom(p1, p2, location()));
700 if (isLeftBraceAhead()) {
705 appendAtom(Atom(Atom::String, cleanLink(p1)));
714 if (openCommand(cmd))
719 if (openCommand(cmd)) {
724 skipSpacesOrOneEndl();
728 if (openCommand(cmd)) {
730 m_openedLists.push(OpenedList(location(), getOptionalArgument()));
740 enterPara(Atom::NoteLeft, Atom::NoteRight);
742 case CMD_NOTRANSLATE:
746 location().warning(QStringLiteral(
"'\\o' is deprecated. Use '\\li'"));
750 if (m_openedCommands.top() == CMD_LIST) {
751 if (m_openedLists.top().isStarted())
752 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
754 appendAtom(Atom(Atom::ListLeft, m_openedLists.top().styleString()));
755 m_openedLists.top().next();
756 appendAtom(Atom(Atom::ListItemNumber, m_openedLists.top().numberString()));
757 appendAtom(Atom(Atom::ListItemLeft, m_openedLists.top().styleString()));
759 }
else if (m_openedCommands.top() == CMD_TABLE) {
762 if (isLeftBraceAhead()) {
764 if (isLeftBraceAhead())
768 if (!m_inTableHeader && !m_inTableRow) {
770 QStringLiteral(
"Missing '\\%1' or '\\%2' before '\\%3'")
775 }
else if (m_inTableItem) {
777 m_inTableItem =
false;
780 appendAtom(Atom(Atom::TableItemLeft, p1, p2));
781 m_inTableItem =
true;
784 QStringLiteral(
"Command '\\%1' outside of '\\%2' and '\\%3'")
793 if (!m_private->m_enumItemList.contains(p1))
794 m_private->m_enumItemList.append(p1);
795 if (!m_private->m_omitEnumItemList.contains(p1))
796 m_private->m_omitEnumItemList.append(p1);
797 skipSpacesOrOneEndl();
799 while (m_position < m_inputLength && !isBlankLine()) {
801 if (qsizetype pos = m_position; pos < m_input.size()
802 && m_input.at(pos++).unicode() ==
'\\') {
804 while (pos < m_input.size() && m_input[pos].isLetterOrNumber())
805 nextCmdStr += m_input[pos++];
807 if (nextCmd == cmd || nextCmd ==
CMD_VALUE)
816 p1 = getRestOfLine();
817 if (openCommand(cmd))
821 if (closeCommand(cmd)) {
833 QString rest = getRestOfLine();
838 if (cmd == CMD_PRINTLINE)
839 appendToCode(m_quoter.quoteLine(location(), cmdStr, rest), atomType, codeLanguage);
840 else if (cmd == CMD_PRINTTO)
841 appendToCode(m_quoter.quoteTo(location(), cmdStr, rest), atomType, codeLanguage);
843 appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest), atomType, codeLanguage);
847 if (openCommand(cmd)) {
855 codeLanguage = getLanguageArgument(&marker);
858 QString fileName = getArgument();
864 marker = CodeMarker::markerForFileName(fileName);
866 quoteFromFile(fileName, marker);
868 appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString()), codeLanguage));
875 p1 = getRestOfLine();
877 location().warning(QStringLiteral(
"Missing format name after '\\%1'")
885 if (m_openedCommands.top() == CMD_TABLE) {
887 if (isLeftBraceAhead())
888 p1 = getArgument(ArgumentParsingOptions::Verbatim);
893 if (m_openedCommands.contains(CMD_TABLE))
894 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
896 cmdName(m_openedCommands.top())));
898 location().warning(QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
918 if (openCommand(cmd)) {
925 QString rest = getRestOfLine();
930 m_quoter.quoteLine(location(), cmdStr, rest);
935 QString rest = getRestOfLine();
940 m_quoter.quoteTo(location(), cmdStr, rest);
945 QString rest = getRestOfLine();
950 m_quoter.quoteUntil(location(), cmdStr, rest);
955 startFormat(p1, cmd);
959 codeLanguage = getLanguageArgument(&marker);
962 QString snippet = getArgument();
963 QString identifier = getRestOfLine();
970 marker = CodeMarker::markerForFileName(snippet);
972 quoteFromFile(snippet, marker);
973 appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType(), codeLanguage);
984 p1 = getOptionalArgument();
985 p2 = getOptionalArgument();
986 if (openCommand(cmd)) {
988 appendAtom(Atom(Atom::TableLeft, p1, p2));
989 m_inTableHeader =
false;
990 m_inTableRow =
false;
991 m_inTableItem =
false;
995 location().report(
"\\%1 is deprecated and will be removed in a future version."_L1.arg(cmdName(cmd)));
996 if (isLeftBraceAhead())
1000 if (m_openedCommands.top() == CMD_TABLE && !m_inTableItem) {
1001 location().warning(
"Found a \\target command outside table item in a table.\n"
1002 "Move the \\target inside the \\li to resolve this warning.");
1004 insertTarget(getRestOfLine());
1008 enterPara(Atom::TitleLeft, Atom::TitleRight);
1012 if (m_paragraphState != InSingleLineParagraph)
1016 if (openCommand(cmd)) {
1022 if (closeCommand(cmd)) {
1040 uint unicodeChar = p1.toUInt(&ok, 0);
1041 if (!ok || (unicodeChar == 0x0000) || (unicodeChar > 0xFFFE))
1043 QStringLiteral(
"Invalid Unicode character '%1' specified with '%2'")
1051 if (m_openedLists.top().style() == OpenedList::Value) {
1054 if (p1.startsWith(QLatin1String(
"[since "))
1055 && p1.endsWith(QLatin1String(
"]"))) {
1056 p2 = p1.mid(7, p1.size() - 8);
1059 if (!m_private->m_enumItemList.contains(p1))
1060 m_private->m_enumItemList.append(p1);
1062 m_openedLists.top().next();
1066 if (!p2.isEmpty()) {
1073 skipSpacesOrOneEndl();
1082 enterPara(Atom::WarningLeft, Atom::WarningRight);
1088 if (metaCommandSet.contains(cmdStr)) {
1090 QString bracketedArg;
1091 m_private->m_metacommandsUsed.insert(cmdStr);
1092 if (isLeftBracketAhead())
1093 bracketedArg = getBracketedArgument();
1096 if (m_position < m_inputLength
1097 && (cmdStr == QLatin1String(
"obsolete")
1098 || cmdStr == QLatin1String(
"deprecated")))
1099 m_input[m_position] =
'\n';
1101 arg = getMetaCommandArgument(cmdStr);
1103 if (possibleTopics.contains(cmdStr)) {
1104 if (!cmdStr.endsWith(QLatin1String(
"propertygroup")))
1107 }
else if (s_utilities
.macroHash.contains(cmdStr)) {
1109 QStringList macroArgs;
1110 int numPendingFi = 0;
1111 int numFormatDefs = 0;
1112 for (
auto it = macro.m_otherDefs.constBegin();
1113 it != macro.m_otherDefs.constEnd(); ++it) {
1114 if (it.key() !=
"match") {
1115 if (numFormatDefs == 0)
1116 macroArgs = getMacroArguments(cmdStr, macro);
1118 expandMacro(*it, macroArgs);
1120 if (it == macro.m_otherDefs.constEnd()) {
1128 while (numPendingFi-- > 0)
1131 if (!macro.m_defaultDef.isEmpty()) {
1132 if (numFormatDefs > 0) {
1133 macro.m_defaultDefLocation.warning(
1134 QStringLiteral(
"Macro cannot have both "
1135 "format-specific and qdoc-"
1136 "syntax definitions"));
1138 QString expanded = expandMacroToString(cmdStr, macro);
1139 m_input.replace(m_backslashPosition,
1140 m_endPosition - m_backslashPosition, expanded);
1141 m_inputLength = m_input.size();
1142 m_position = m_backslashPosition;
1145 }
else if (isAutoLinkString(cmdStr)) {
1148 if (!cmdStr.endsWith(
"propertygroup")) {
1151 location().warning(QStringLiteral(
"Unknown command '\\%1'").arg(cmdStr),
1152 detailsUnknownCommand(metaCommandSet, cmdStr));
1163 qsizetype dashCount = 1;
1167 while ((m_position < m_inputLength) && (m_input.at(m_position) ==
'-')) {
1172 if (dashCount == 3) {
1174 const QChar emDash(8212);
1176 }
else if (dashCount == 2) {
1178 const QChar enDash(8211);
1184 for (qsizetype i = 0; i < dashCount; ++i)
1199 auto format = m_pendingFormats.find(m_braceDepth);
1200 if (format == m_pendingFormats.end()) {
1207 if (m_indexStartedParagraph)
1212 if (currentLinkAtom && currentLinkAtom->string().endsWith(
"::")) {
1216 currentLinkAtom->concatenateString(suffix);
1218 currentLinkAtom =
nullptr;
1222 m_pendingFormats.erase(format);
1228 if (m_position + 2 < m_inputLength)
1229 if (m_input.at(m_position + 1) ==
'/')
1230 if (m_input.at(m_position + 2) ==
'!') {
1233 if (m_input.at(m_position - 1) ==
'\n')
1249 if (m_paragraphState == OutsideParagraph) {
1261 && (m_paragraphState == InSingleLineParagraph || isBlankLine())) {
1274 qsizetype startPos = m_position;
1276 bool autolink = (!m_pendingFormats.isEmpty() &&
1278 || m_paragraphState == InSingleLineParagraph ?
1279 false : isAutoLinkString(m_input, m_position);
1280 if (m_position == startPos) {
1281 if (!ch.isSpace()) {
1286 QString word = m_input.mid(startPos, m_position - startPos);
1288 if (s_ignoreWords.contains(word) || word.startsWith(QString(
"__")))
1303 if (m_openedCommands.top() == CMD_LEGALESE) {
1305 m_openedCommands.pop();
1308 if (m_openedCommands.top() != CMD_OMIT) {
1310 QStringLiteral(
"Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
1311 }
else if (preprocessorSkipping.size() > 0) {
1312 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(
CMD_ENDIF)));
1316 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1324
1325
1328 while (!m_openedInputs.isEmpty() && m_openedInputs.top() <= m_position) {
1329 m_cachedLocation.pop();
1330 m_cachedPosition = m_openedInputs.pop();
1332 while (m_cachedPosition < m_position)
1333 m_cachedLocation.advance(m_input.at(m_cachedPosition++));
1334 return m_cachedLocation;
1337QString
DocParser::detailsUnknownCommand(
const QSet<QString> &metaCommandSet,
const QString &str)
1339 QSet<QString> commandSet = metaCommandSet;
1341 while (cmds[i]
.name !=
nullptr) {
1342 commandSet.insert(cmds[i]
.name);
1346 return "Maybe you meant '\\%1'?"_L1.arg(suggestName(str, commandSet));
1350
1351
1352
1353
1354
1355
1356
1358 const QString &cmdString,
const QString &previousDefinition)
1360 if (duplicateDefinition.isEmpty()) {
1361 location.warning(
"Expected an argument for \\%1"_L1.arg(cmdString));
1364 "Duplicate %3 name '%1'. The previous occurrence is here: %2"_L1
1365 .arg(duplicateDefinition, previousDefinition, cmdString));
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383void DocParser::insertTarget(
const QString &target)
1385 if (target.isEmpty() || m_targetMap.contains(target))
1386 return warnAboutEmptyOrPreexistingTarget(location(), target,
1387 s_utilities.cmdHash.key(CMD_TARGET), m_targetMap[target].toString());
1389 m_targetMap.insert(target, location());
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410void DocParser::insertKeyword(
const QString &keyword)
1412 if (keyword.isEmpty() || m_targetMap.contains(keyword))
1413 return warnAboutEmptyOrPreexistingTarget(location(), keyword,
1414 s_utilities.cmdHash.key(CMD_KEYWORD), m_targetMap[keyword].toString());
1416 m_targetMap.insert(keyword, location());
1423void DocParser::include(
const QString &fileName,
const QString &identifier,
const QStringList ¶meters)
1426 location().fatal(QStringLiteral(
"Too many nested '\\%1's").arg(cmdName(
CMD_INCLUDE)));
1427 QString filePath = Config::instance().getIncludeFilePath(fileName);
1428 if (filePath.isEmpty()) {
1429 location().warning(QStringLiteral(
"Cannot find qdoc include file '%1'").arg(fileName));
1431 QFile inFile(filePath);
1432 if (!inFile.open(QFile::ReadOnly)) {
1434 QStringLiteral(
"Cannot open qdoc include file '%1'").arg(filePath));
1436 location().push(fileName);
1437 QTextStream inStream(&inFile);
1438 QString includedContent = inStream.readAll();
1441 if (identifier.isEmpty()) {
1442 expandArgumentsInString(includedContent, parameters);
1443 m_input.insert(m_position, includedContent);
1444 m_inputLength = m_input.size();
1445 m_openedInputs.push(m_position + includedContent.size());
1447 auto isSnippetMarker = [&identifier](QStringView trimmedLine) ->
bool {
1448 if (!trimmedLine.startsWith(QLatin1String(
"//!")))
1450 auto bracketStart = trimmedLine.indexOf(QLatin1Char(
'['));
1451 auto bracketEnd = trimmedLine.indexOf(QLatin1Char(
']'));
1452 if (bracketStart < 0 || bracketEnd <= bracketStart)
1454 auto name = trimmedLine.mid(bracketStart + 1, bracketEnd - bracketStart - 1)
1456 return name == identifier;
1459 QStringList lineBuffer = includedContent.split(QLatin1Char(
'\n'));
1460 qsizetype bufLen{lineBuffer.size()};
1462 QStringView trimmedLine;
1463 for (i = 0; i < bufLen; ++i) {
1464 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1465 if (isSnippetMarker(trimmedLine))
1468 if (i < bufLen - 1) {
1472 QStringLiteral(
"Cannot find '%1' in '%2'").arg(identifier, filePath));
1477 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1478 if (isSnippetMarker(trimmedLine))
1481 result += lineBuffer[i] + QLatin1Char(
'\n');
1483 }
while (i < bufLen);
1485 expandArgumentsInString(result, parameters);
1486 if (result.isEmpty()) {
1487 location().warning(QStringLiteral(
"Empty qdoc snippet '%1' in '%2'")
1488 .arg(identifier, filePath));
1490 m_input.insert(m_position, result);
1491 m_inputLength = m_input.size();
1492 m_openedInputs.push(m_position + result.size());
1499void DocParser::startFormat(
const QString &format,
int cmd)
1503 for (
const auto &item : std::as_const(m_pendingFormats)) {
1504 if (item == format) {
1505 location().warning(QStringLiteral(
"Cannot nest '\\%1' commands").arg(cmdName(cmd)));
1512 if (isLeftBraceAhead()) {
1513 skipSpacesOrOneEndl();
1514 m_pendingFormats.insert(m_braceDepth, format);
1518 const auto &arg{getArgument()};
1523 m_indexStartedParagraph =
false;
1532 int outer = m_openedCommands.top();
1535 if ((cmd == CMD_COMPARESWITH || cmd == CMD_TOC)
1536 && m_openedCommands.contains(cmd)) {
1537 location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
1554 m_openedCommands.push(cmd);
1557 QStringLiteral(
"Can't use '\\%1' in '\\%2'").arg(cmdName(cmd), cmdName(outer)));
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
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
1639inline bool DocParser::isAutoLinkString(
const QString &word)
1641 qsizetype start = 0;
1642 return isAutoLinkString(word, start) && (start == word.size());
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666bool DocParser::isAutoLinkString(
const QString &word, qsizetype &curPos)
1668 qsizetype len = word.size();
1669 qsizetype startPos = curPos;
1670 int numUppercase = 0;
1671 int numLowercase = 0;
1672 int numStrangeSymbols = 0;
1674 while (curPos < len) {
1675 unsigned char latin1Ch = word.at(curPos).toLatin1();
1676 if (islower(latin1Ch)) {
1679 }
else if (isupper(latin1Ch)) {
1680 if (curPos > startPos)
1683 }
else if (isdigit(latin1Ch)) {
1684 if (curPos > startPos)
1688 }
else if (latin1Ch ==
'_' || latin1Ch ==
'@') {
1689 ++numStrangeSymbols;
1691 }
else if ((latin1Ch ==
':') && (curPos < len - 1)
1692 && (word.at(curPos + 1) == QLatin1Char(
':'))) {
1693 ++numStrangeSymbols;
1695 }
else if (latin1Ch ==
'(') {
1696 if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(
')'))) {
1697 ++numStrangeSymbols;
1707 return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
1712 if (endCmdFor(m_openedCommands.top()) == endCmd && m_openedCommands.size() > 1) {
1713 m_openedCommands.pop();
1716 bool contains =
false;
1717 QStack<
int> opened2 = m_openedCommands;
1718 while (opened2.size() > 1) {
1727 while (endCmdFor(m_openedCommands.top()) != endCmd && m_openedCommands.size() > 1) {
1729 QStringLiteral(
"Missing '\\%1' before '\\%2'")
1730 .arg(endCmdName(m_openedCommands.top()), cmdName(endCmd)));
1731 m_openedCommands.pop();
1734 location().warning(QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(endCmd)));
1745 m_currentSection =
static_cast<
Doc::
Sections>(unit);
1748 endSection(unit, cmd);
1751 appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
1754 m_private
->extra->m_tableOfContentsLevels.append(unit);
1755 enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit));
1756 m_currentSection = unit;
1762 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1796 const QString imageFileName = getArgument();
1798 bool hasAltTextArgument{
false};
1799 if (isLeftBraceAhead()) {
1800 hasAltTextArgument =
true;
1801 imageText = getArgument();
1803 imageText = getRestOfLine();
1806 if (imageText.length() > 1) {
1807 if (imageText.front() ==
'"' && imageText.back() ==
'"') {
1808 imageText.removeFirst();
1809 imageText.removeLast();
1813 if (!hasAltTextArgument && imageText.isEmpty() && Config::instance().reportMissingAltTextForImages())
1814 location().report(QStringLiteral(
"\\%1 %2 is without a textual description, "
1815 "QDoc will not generate an alt text for the image.")
1817 .arg(imageFileName));
1818 appendAtom(
Atom(imageAtom, imageFileName));
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1837 const QString cmd{
"overload"};
1840 m_private->m_metacommandsUsed.insert(cmd);
1841 QString overloadArgument = isBlankLine() ? getMetaCommandArgument(cmd) : getRestOfLine();
1846 if (overloadArgument.trimmed() ==
"primary")
1847 overloadArgument =
"__qdoc_primary_overload__"_L1;
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1867 auto line_comment = [
this]() ->
bool {
1869 if (m_position + 2 > m_inputLength)
1871 if (m_input[m_position].unicode() ==
'/') {
1872 if (m_input[m_position + 1].unicode() ==
'/') {
1873 if (m_input[m_position + 2].unicode() ==
'!') {
1881 auto skip_everything_until_newline = [
this]() ->
void {
1882 while (m_position < m_inputLength && m_input[m_position] !=
'\n')
1888 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
1891 bool skipMe =
false;
1893 if (m_input[m_position] ==
'{') {
1894 target = getArgument();
1895 if (isLeftBraceAhead()) {
1896 str = getArgument();
1899 if (target.endsWith(
"::"))
1905 target = getArgument();
1906 str = cleanLink(target);
1907 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1921 skip_everything_until_newline();
1923 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1926 skip_everything_until_newline();
1927 skipSpacesOrOneEndl();
1928 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1929 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1947 if (ch == QLatin1Char(
' ')) {
1948 if (!atom->string().endsWith(QLatin1Char(
' ')))
1949 atom->appendChar(QLatin1Char(
' '));
1951 atom->appendChar(ch);
1954void DocParser::appendWord(
const QString &word)
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974void DocParser::appendEscapedIdentifier()
1976 Q_ASSERT(m_position < m_inputLength);
1977 Q_ASSERT(m_input.at(m_position) ==
'\\');
1982 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
1983 identifier += m_input.at(m_position);
1988 if (!identifier.isEmpty()) {
1993 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
1996 appendChar(QLatin1Char(
'\\'));
2000void DocParser::appendToCode(
const QString &markedCode)
2002 if (!isCode(m_lastAtom)) {
2006 m_lastAtom->concatenateString(markedCode);
2010
2011
2012
2013
2014
2015void DocParser::appendToCode(
const QString &markedCode,
Atom::
AtomType defaultType,
const QString &language)
2017 if (!isCode(m_lastAtom)) {
2018 appendAtom(
Atom(defaultType, markedCode, language));
2021 m_lastAtom->concatenateString(markedCode);
2027 if (m_paragraphState != OutsideParagraph)
2036 appendAtom(
Atom(leftType, string));
2037 m_indexStartedParagraph =
false;
2038 m_pendingParagraphLeftType = leftType;
2039 m_pendingParagraphRightType = rightType;
2040 m_pendingParagraphString = string;
2042 m_paragraphState = InSingleLineParagraph;
2044 m_paragraphState = InMultiLineParagraph;
2046 skipSpacesOrOneEndl();
2051 if (m_paragraphState == OutsideParagraph)
2054 if (!m_pendingFormats.isEmpty()) {
2055 location().warning(QStringLiteral(
"Missing '}'"));
2056 m_pendingFormats.clear();
2071 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2074 m_paragraphState = OutsideParagraph;
2075 m_indexStartedParagraph =
false;
2076 m_pendingParagraphRightType =
Atom::Nop;
2077 m_pendingParagraphString.clear();
2083 if (m_openedLists.isEmpty()) {
2084 m_openedLists.push(OpenedList(OpenedList::Value));
2096 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2101 m_openedLists.pop();
2107 if (m_inTableItem) {
2110 m_inTableItem =
false;
2112 if (m_inTableHeader) {
2114 m_inTableHeader =
false;
2118 m_inTableRow =
false;
2123
2124
2125
2126
2140 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2141 if (!maybe_resolved_file) {
2150 QString details =
std::transform_reduce(
2151 (*file_resolver).get_search_directories().cbegin(),
2152 (*file_resolver).get_search_directories().cend(),
2153 u"Searched directories:"_s,
2155 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2158 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2175 marker = CodeMarker::markerForFileName(QString{});
2176 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206bool DocParser::expandMacro(ArgumentParsingOptions options)
2208 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2210 if (options == ArgumentParsingOptions::Verbatim)
2214 qsizetype backslashPos = m_position++;
2215 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2216 cmdStr += m_input[m_position++];
2218 m_endPosition = m_position;
2219 if (!cmdStr.isEmpty()) {
2220 if (s_utilities
.macroHash.contains(cmdStr)) {
2222 if (!macro.m_defaultDef.isEmpty()) {
2223 QString expanded = expandMacroToString(cmdStr, macro);
2224 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2225 m_inputLength = m_input.size();
2226 m_position = backslashPos;
2229 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2233 m_position = backslashPos;
2234 if (options != ArgumentParsingOptions::MacroArguments
2236 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2240 }
else if (m_input[m_position].isSpace()) {
2242 }
else if (m_input[m_position].unicode() ==
'\\') {
2244 m_input.remove(m_position--, 1);
2250void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2252 if (args.isEmpty()) {
2257 for (
int j = 0; j < def.size(); ++j) {
2258 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2259 if (!rawString.isEmpty()) {
2265 rawString += def[j];
2268 if (!rawString.isEmpty())
2273QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2275 const QString &def{macro.m_defaultDef};
2279 rawString = macro.m_defaultDef;
2281 QStringList args{getMacroArguments(name, macro)};
2283 for (
int j = 0; j < def.size(); ++j) {
2284 int paramNo = def[j].unicode();
2285 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2288 QString matchExpr{macro.m_otherDefs.value(
"match")};
2289 if (matchExpr.isEmpty())
2293 QRegularExpression re(matchExpr);
2294 int capStart = (re.captureCount() > 0) ? 1 : 0;
2296 QRegularExpressionMatch match;
2297 while ((match = re.match(rawString, i)).hasMatch()) {
2298 for (
int c = capStart; c <= re.captureCount(); ++c)
2299 result += match.captured(c);
2300 i = match.capturedEnd();
2308 QString name = getOptionalArgument();
2310 if (name ==
"section1") {
2312 }
else if (name ==
"section2") {
2314 }
else if (name ==
"section3") {
2316 }
else if (name ==
"section4") {
2318 }
else if (name.isEmpty()) {
2321 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2340 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2342 while (m_position < m_input.size() && delimDepth >= 0) {
2343 switch (m_input[m_position].unicode()) {
2346 arg += QLatin1Char(
'{');
2351 if (delimDepth >= 0)
2352 arg += QLatin1Char(
'}');
2356 if (!expandMacro(options))
2357 arg += m_input[m_position++];
2360 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2363 arg += m_input[m_position];
2368 location().warning(QStringLiteral(
"Missing '}'"));
2370 m_endPosition = m_position;
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390QString
DocParser::getArgument(ArgumentParsingOptions options)
2392 skipSpacesOrOneEndl();
2395 qsizetype startPos = m_position;
2396 QString arg = getBracedArgument(options);
2397 if (arg.isEmpty()) {
2398 while ((m_position < m_input.size())
2399 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2400 switch (m_input[m_position].unicode()) {
2405 arg += m_input[m_position];
2412 if (m_position == startPos || delimDepth >= 0) {
2413 arg += m_input[m_position];
2418 if (!expandMacro(options))
2419 arg += m_input[m_position++];
2422 arg += m_input[m_position];
2426 m_endPosition = m_position;
2427 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2428 && !arg.endsWith(
"...")) {
2429 arg.truncate(arg.size() - 1);
2432 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2433 arg.truncate(arg.size() - 2);
2437 return arg.simplified();
2441
2442
2443
2444
2445
2446QString
DocParser::getBracketedArgument()
2450 skipSpacesOrOneEndl();
2451 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2453 while (m_position < m_input.size() && delimDepth >= 0) {
2454 switch (m_input[m_position].unicode()) {
2457 arg += QLatin1Char(
'[');
2462 if (delimDepth >= 0)
2463 arg += QLatin1Char(
']');
2467 arg += m_input[m_position];
2471 arg += m_input[m_position];
2476 location().warning(QStringLiteral(
"Missing ']'"));
2482
2483
2484
2485
2486
2487
2488
2492 if (isLeftBracketAhead(0)) {
2493 value = getBracketedArgument();
2494 *marker = markerForLanguage(value);
2499 return value.toLower();
2503
2504
2505
2506
2507
2508QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2512 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2513 args << getArgument(ArgumentParsingOptions::MacroArguments);
2515 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2516 " arguments (expected %2, got %3)")
2527
2528
2529
2530
2533 skipSpacesOrOneEndl();
2534 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2535 && m_input[m_position + 1].isLetterOrNumber()) {
2538 return getArgument();
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2559 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2560 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2561 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2562 trailingBackslash =
true;
2566 trailingBackslash =
false;
2570 return trailingBackslash;
2573 QString rest_of_line;
2575 bool trailing_backslash{
false };
2576 bool return_simplified_string{
false };
2578 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2579 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2581 if (!rest_of_line.isEmpty())
2582 rest_of_line += QLatin1Char(
' ');
2583 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2585 if (trailing_backslash) {
2586 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2587 return_simplified_string =
true;
2590 if (m_position < m_inputLength)
2593 if (!trailing_backslash)
2595 start_position = m_position;
2598 if (return_simplified_string)
2599 return rest_of_line.simplified();
2601 return rest_of_line.trimmed();
2605
2606
2607
2608
2609QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2613 qsizetype begin = m_position;
2616 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2617 if (m_input.at(m_position) ==
'(')
2619 else if (m_input.at(m_position) ==
')')
2621 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2625 if (m_position == m_input.size() && parenDepth > 0) {
2627 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2630 QString t = m_input.mid(begin, m_position - begin).simplified();
2638 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2640 auto match = rx.match(m_input, m_position);
2642 if (!match.hasMatch()) {
2643 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2644 m_position = m_input.size();
2646 qsizetype end = match.capturedStart();
2647 t = m_input.mid(m_position, end - m_position);
2648 m_position = match.capturedEnd();
2653void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2660 while (j < str.size()) {
2661 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2662 && paramNo <= args.size()) {
2663 const QString &r = args[paramNo - 1];
2664 str.replace(j, 2, r);
2665 j += qMin(1, r.size());
2673
2674
2675
2676
2677
2678
2681 QString code = untabifyEtc(getUntilEnd(cmd));
2682 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2684 int indent = indentLevel(code);
2685 code = dedent(indent, code);
2688 if (!marker && !m_private->m_topics.isEmpty()
2689 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2690 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2691 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2693 if (marker ==
nullptr)
2694 marker = CodeMarker::markerForCode(code);
2695 return marker->markedUpCode(code,
nullptr, location());
2700 qsizetype i = m_position;
2702 while (i < m_inputLength && m_input[i].isSpace()) {
2703 if (m_input[i] ==
'\n')
2713 qsizetype i = m_position;
2715 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2717 if (m_input[i] ==
'\n')
2721 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2724bool DocParser::isLeftBracketAhead(
int maxNewlines)
2727 qsizetype i = m_position;
2729 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2731 if (m_input[i] ==
'\n')
2735 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2739
2740
2743 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2744 && (m_input[m_position].unicode() !=
'\n'))
2749
2750
2753 qsizetype firstEndl = -1;
2754 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2755 QChar ch = m_input[m_position];
2757 if (firstEndl == -1) {
2758 firstEndl = m_position;
2760 m_position = firstEndl;
2770 while (m_position < m_inputLength && m_input[m_position].isSpace())
2774void DocParser::skipToNextPreprocessorCommand()
2776 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2777 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2778 auto match = rx.match(m_input, m_position + 1);
2780 if (!match.hasMatch())
2781 m_position = m_input.size();
2783 m_position = match.capturedStart();
2836 return cmds[cmd]
.name;
2841 return cmdName(endCmdFor(cmd));
2847 result.reserve(str.size());
2850 for (
const auto &character : str) {
2851 if (character == QLatin1Char(
'\r'))
2853 if (character == QLatin1Char(
'\t')) {
2854 result += &
" "[column % s_tabSize];
2855 column = ((column / s_tabSize) + 1) * s_tabSize;
2858 if (character == QLatin1Char(
'\n')) {
2859 while (result.endsWith(QLatin1Char(
' ')))
2861 result += character;
2865 result += character;
2869 while (result.endsWith(
"\n\n"))
2870 result.truncate(result.size() - 1);
2871 while (result.startsWith(QLatin1Char(
'\n')))
2872 result = result.mid(1);
2879 int minIndent = INT_MAX;
2882 for (
const auto &character : str) {
2883 if (character ==
'\n') {
2886 if (character !=
' ' && column < minIndent)
2902 for (
const auto &character : str) {
2903 if (character == QLatin1Char(
'\n')) {
2907 if (column >= level)
2908 result += character;
2916
2917
2925
2926
2936
2937
2938
2939
2942 CodeMarker *marker = CodeMarker::markerForLanguage(language.toLower());
2944 if (!marker && !s_allowedLanguages.contains(language, Qt::CaseInsensitive))
2945 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(language));
2951
2952
2953
2954
2955
2956
2957
2958
2961 static auto take_while = [](QStringView input,
auto predicate) {
2962 QStringView::size_type end{0};
2964 while (end < input.size() &&
std::invoke(predicate, input[end]))
2967 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2970 static auto peek = [](QStringView input, QChar c) {
2971 return !input.empty() && input.first() == c;
2974 static auto skip_one = [](QStringView input) {
2975 if (input.empty())
return std::make_tuple(QStringView{}, input);
2976 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
2979 static auto enclosed = [](QStringView input, QChar open, QChar close) {
2980 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
2982 auto [opened, without_open] = skip_one(input);
2983 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
2985 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
2987 auto [closed, without_close] = skip_one(remaining);
2989 return std::make_tuple(parsed.trimmed(), without_close);
2992 static auto one_of = [](
auto first,
auto second) {
2993 return [first, second](QStringView input) {
2994 auto [parsed, remaining] =
std::invoke(first, input);
2996 if (parsed.empty())
return std::invoke(second, input);
2997 else return std::make_tuple(parsed, remaining);
3001 static auto collect = [](QStringView input,
auto parser) {
3002 QStringList collected{};
3005 auto [parsed, remaining] =
std::invoke(parser, input);
3007 if (parsed.empty())
break;
3008 collected.append(parsed.toString());
3016 static auto spaces = [](QStringView input) {
3017 return take_while(input, [](QChar c){
return c.isSpace(); });
3020 static auto word = [](QStringView input) {
3021 return take_while(input, [](QChar c){
return !c.isSpace(); });
3024 static auto parse_argument = [](QStringView input) {
3025 auto [_, without_spaces] = spaces(input);
3028 [](QStringView input){
return enclosed(input,
'{',
'}'); },
3033 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
3037 QStringList segments = collect(atom->string(), parse_argument);
3039 QString categoryString;
3040 if (!segments.isEmpty())
3041 categoryString = segments.takeFirst();
3042 auto category = comparisonCategoryFromString(categoryString.toStdString());
3044 if (category == ComparisonCategory::None) {
3045 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
3046 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3050 if (segments.isEmpty()) {
3051 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3052 u"Provide at least one type name, or a list of types separated by spaces."_s);
3057 segments.removeDuplicates();
3058 atom->setString(segments.join(QLatin1Char(
';')));
3062 priv->m_metacommandsUsed.insert(cmd);
3065 const auto end{priv
->extra->m_comparesWithMap.cend()};
3066 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.