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 },
251
252
253
254
257 qsizetype colonPos = link.indexOf(
':');
258 QString cleaned{link};
259 cleaned.replace(
"\\#"_L1,
"#"_L1);
260 if ((colonPos == -1) || (!link.startsWith(
"file:") && !link.startsWith(
"mailto:")))
262 return cleaned.mid(colonPos + 1).simplified();
271 while (cmds[i]
.name) {
275 Location::internalError(QStringLiteral(
"command %1 missing").arg(i));
281 const auto &outputFormats = config.getOutputFormats();
282 for (
const auto &format : outputFormats)
283 DocParser::s_quoting = DocParser::s_quoting
288 DocParser::file_resolver = &file_resolver;
292
293
294
295
296
297
298
299
301 const QSet<QString> &metaCommandSet,
const QSet<QString> &possibleTopics)
305 m_inputLength = m_input.size();
306 m_cachedLocation = docPrivate->m_start_loc;
307 m_cachedPosition = 0;
308 m_private = docPrivate;
312 m_paragraphState = OutsideParagraph;
313 m_inTableHeader =
false;
314 m_inTableRow =
false;
315 m_inTableItem =
false;
316 m_indexStartedParagraph =
false;
322 m_openedCommands.push(CMD_OMIT);
326 Atom *currentLinkAtom =
nullptr;
328 QStack<
bool> preprocessorSkipping;
329 int numPreprocessorSkipping = 0;
331 while (m_position < m_inputLength) {
332 QChar ch = m_input.at(m_position);
334 switch (ch.unicode()) {
337 m_backslashPosition = m_position;
339 while (m_position < m_inputLength) {
340 ch = m_input.at(m_position);
341 if (ch.isLetterOrNumber()) {
348 m_endPosition = m_position;
349 if (cmdStr.isEmpty()) {
350 if (m_position < m_inputLength) {
351 QChar nextCh = m_input.at(m_position);
352 if (nextCh ==
'\\') {
353 appendEscapedIdentifier();
354 }
else if (nextCh.isSpace()) {
357 appendChar(QLatin1Char(
' '));
360 appendChar(m_input.at(m_position++));
377 m_private->m_params.insert(p1);
381 appendAtom(Atom(Atom::CodeBad,
382 getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr))));
389 location().warning(QStringLiteral(
"'\\bold' is deprecated. Use '\\b'"));
396 enterPara(Atom::BriefLeft, Atom::BriefRight);
400 p1 = untabifyEtc(getArgument(ArgumentParsingOptions::Verbatim));
401 marker = CodeMarker::markerForCode(p1);
402 appendAtom(
Atom(
Atom::C, marker->markedUpCode(p1,
nullptr, location())));
406 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
410 if (isLeftBracketAhead(0)) {
411 p1 = getBracketedArgument();
412 marker = CodeMarker::markerForLanguage(p1);
414 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(p1));
422 appendAtom(Atom(Atom::Code, getCode(CMD_CODE, marker, getMetaCommandArgument(cmdStr)), p1.toLower()));
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)) {
829 QString rest = getRestOfLine();
834 appendToCode(m_quoter.quoteLine(location(), cmdStr, rest));
839 QString rest = getRestOfLine();
844 appendToCode(m_quoter.quoteTo(location(), cmdStr, rest));
849 QString rest = getRestOfLine();
854 appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest));
858 if (openCommand(cmd)) {
866 QString fileName = getArgument();
867 quoteFromFile(fileName);
872 appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString())));
878 QString arg = getArgument();
888 p1 = getRestOfLine();
890 location().warning(QStringLiteral(
"Missing format name after '\\%1'")
898 if (m_openedCommands.top() == CMD_TABLE) {
900 if (isLeftBraceAhead())
901 p1 = getArgument(ArgumentParsingOptions::Verbatim);
906 if (m_openedCommands.contains(CMD_TABLE))
907 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
909 cmdName(m_openedCommands.top())));
911 location().warning(QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
931 if (openCommand(cmd)) {
938 QString rest = getRestOfLine();
943 m_quoter.quoteLine(location(), cmdStr, rest);
948 QString rest = getRestOfLine();
953 m_quoter.quoteTo(location(), cmdStr, rest);
958 QString rest = getRestOfLine();
963 m_quoter.quoteUntil(location(), cmdStr, rest);
968 startFormat(p1, cmd);
972 QString snippet = getArgument();
973 QString identifier = getRestOfLine();
979 marker = CodeMarker::markerForFileName(snippet);
980 quoteFromFile(snippet);
981 appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType());
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)
1207 auto format = m_pendingFormats.find(m_braceDepth);
1208 if (format == m_pendingFormats.end()) {
1215 if (m_indexStartedParagraph)
1220 if (currentLinkAtom && currentLinkAtom->string().endsWith(
"::")) {
1224 currentLinkAtom->concatenateString(suffix);
1226 currentLinkAtom =
nullptr;
1230 m_pendingFormats.erase(format);
1236 if (m_position + 2 < m_inputLength)
1237 if (m_input.at(m_position + 1) ==
'/')
1238 if (m_input.at(m_position + 2) ==
'!') {
1241 if (m_input.at(m_position - 1) ==
'\n')
1257 if (m_paragraphState == OutsideParagraph) {
1269 && (m_paragraphState == InSingleLineParagraph || isBlankLine())) {
1282 qsizetype startPos = m_position;
1284 bool autolink = (!m_pendingFormats.isEmpty() &&
1286 || m_paragraphState == InSingleLineParagraph ?
1287 false : isAutoLinkString(m_input, m_position);
1288 if (m_position == startPos) {
1289 if (!ch.isSpace()) {
1294 QString word = m_input.mid(startPos, m_position - startPos);
1296 if (s_ignoreWords.contains(word) || word.startsWith(QString(
"__")))
1311 if (m_openedCommands.top() == CMD_LEGALESE) {
1313 m_openedCommands.pop();
1316 if (m_openedCommands.top() != CMD_OMIT) {
1318 QStringLiteral(
"Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
1319 }
else if (preprocessorSkipping.size() > 0) {
1320 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(
CMD_ENDIF)));
1324 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1332
1333
1336 while (!m_openedInputs.isEmpty() && m_openedInputs.top() <= m_position) {
1337 m_cachedLocation.pop();
1338 m_cachedPosition = m_openedInputs.pop();
1340 while (m_cachedPosition < m_position)
1341 m_cachedLocation.advance(m_input.at(m_cachedPosition++));
1342 return m_cachedLocation;
1345QString
DocParser::detailsUnknownCommand(
const QSet<QString> &metaCommandSet,
const QString &str)
1347 QSet<QString> commandSet = metaCommandSet;
1349 while (cmds[i]
.name !=
nullptr) {
1350 commandSet.insert(cmds[i]
.name);
1354 return "Maybe you meant '\\%1'?"_L1.arg(suggestName(str, commandSet));
1358
1359
1360
1361
1362
1363
1364
1366 const QString &cmdString,
const QString &previousDefinition)
1368 if (duplicateDefinition.isEmpty()) {
1369 location.warning(
"Expected an argument for \\%1"_L1.arg(cmdString));
1372 "Duplicate %3 name '%1'. The previous occurrence is here: %2"_L1
1373 .arg(duplicateDefinition, previousDefinition, cmdString));
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391void DocParser::insertTarget(
const QString &target)
1393 if (target.isEmpty() || m_targetMap.contains(target))
1394 return warnAboutEmptyOrPreexistingTarget(location(), target,
1395 s_utilities.cmdHash.key(CMD_TARGET), m_targetMap[target].toString());
1397 m_targetMap.insert(target, location());
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418void DocParser::insertKeyword(
const QString &keyword)
1420 if (keyword.isEmpty() || m_targetMap.contains(keyword))
1421 return warnAboutEmptyOrPreexistingTarget(location(), keyword,
1422 s_utilities.cmdHash.key(CMD_KEYWORD), m_targetMap[keyword].toString());
1424 m_targetMap.insert(keyword, location());
1431void DocParser::include(
const QString &fileName,
const QString &identifier,
const QStringList ¶meters)
1434 location().fatal(QStringLiteral(
"Too many nested '\\%1's").arg(cmdName(
CMD_INCLUDE)));
1435 QString filePath = Config::instance().getIncludeFilePath(fileName);
1436 if (filePath.isEmpty()) {
1437 location().warning(QStringLiteral(
"Cannot find qdoc include file '%1'").arg(fileName));
1439 QFile inFile(filePath);
1440 if (!inFile.open(QFile::ReadOnly)) {
1442 QStringLiteral(
"Cannot open qdoc include file '%1'").arg(filePath));
1444 location().push(fileName);
1445 QTextStream inStream(&inFile);
1446 QString includedContent = inStream.readAll();
1449 if (identifier.isEmpty()) {
1450 expandArgumentsInString(includedContent, parameters);
1451 m_input.insert(m_position, includedContent);
1452 m_inputLength = m_input.size();
1453 m_openedInputs.push(m_position + includedContent.size());
1455 QStringList lineBuffer = includedContent.split(QLatin1Char(
'\n'));
1456 qsizetype bufLen{lineBuffer.size()};
1458 QStringView trimmedLine;
1459 for (i = 0; i < bufLen; ++i) {
1460 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1461 if (trimmedLine.startsWith(QLatin1String(
"//!")) &&
1462 trimmedLine.contains(identifier))
1465 if (i < bufLen - 1) {
1469 QStringLiteral(
"Cannot find '%1' in '%2'").arg(identifier, filePath));
1474 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1475 if (trimmedLine.startsWith(QLatin1String(
"//!")) &&
1476 trimmedLine.contains(identifier))
1479 result += lineBuffer[i] + QLatin1Char(
'\n');
1481 }
while (i < bufLen);
1483 expandArgumentsInString(result, parameters);
1484 if (result.isEmpty()) {
1485 location().warning(QStringLiteral(
"Empty qdoc snippet '%1' in '%2'")
1486 .arg(identifier, filePath));
1488 m_input.insert(m_position, result);
1489 m_inputLength = m_input.size();
1490 m_openedInputs.push(m_position + result.size());
1497void DocParser::startFormat(
const QString &format,
int cmd)
1501 for (
const auto &item : std::as_const(m_pendingFormats)) {
1502 if (item == format) {
1503 location().warning(QStringLiteral(
"Cannot nest '\\%1' commands").arg(cmdName(cmd)));
1510 if (isLeftBraceAhead()) {
1511 skipSpacesOrOneEndl();
1512 m_pendingFormats.insert(m_braceDepth, format);
1516 const auto &arg{getArgument()};
1521 m_indexStartedParagraph =
false;
1530 int outer = m_openedCommands.top();
1533 if ((cmd == CMD_COMPARESWITH || cmd == CMD_TOC)
1534 && m_openedCommands.contains(cmd)) {
1535 location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
1552 m_openedCommands.push(cmd);
1555 QStringLiteral(
"Can't use '\\%1' in '\\%2'").arg(cmdName(cmd), cmdName(outer)));
1561
1562
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
1637inline bool DocParser::isAutoLinkString(
const QString &word)
1639 qsizetype start = 0;
1640 return isAutoLinkString(word, start) && (start == word.size());
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664bool DocParser::isAutoLinkString(
const QString &word, qsizetype &curPos)
1666 qsizetype len = word.size();
1667 qsizetype startPos = curPos;
1668 int numUppercase = 0;
1669 int numLowercase = 0;
1670 int numStrangeSymbols = 0;
1672 while (curPos < len) {
1673 unsigned char latin1Ch = word.at(curPos).toLatin1();
1674 if (islower(latin1Ch)) {
1677 }
else if (isupper(latin1Ch)) {
1678 if (curPos > startPos)
1681 }
else if (isdigit(latin1Ch)) {
1682 if (curPos > startPos)
1686 }
else if (latin1Ch ==
'_' || latin1Ch ==
'@') {
1687 ++numStrangeSymbols;
1689 }
else if ((latin1Ch ==
':') && (curPos < len - 1)
1690 && (word.at(curPos + 1) == QLatin1Char(
':'))) {
1691 ++numStrangeSymbols;
1693 }
else if (latin1Ch ==
'(') {
1694 if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(
')'))) {
1695 ++numStrangeSymbols;
1705 return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
1710 if (endCmdFor(m_openedCommands.top()) == endCmd && m_openedCommands.size() > 1) {
1711 m_openedCommands.pop();
1714 bool contains =
false;
1715 QStack<
int> opened2 = m_openedCommands;
1716 while (opened2.size() > 1) {
1725 while (endCmdFor(m_openedCommands.top()) != endCmd && m_openedCommands.size() > 1) {
1727 QStringLiteral(
"Missing '\\%1' before '\\%2'")
1728 .arg(endCmdName(m_openedCommands.top()), cmdName(endCmd)));
1729 m_openedCommands.pop();
1732 location().warning(QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(endCmd)));
1743 m_currentSection =
static_cast<
Doc::
Sections>(unit);
1746 endSection(unit, cmd);
1749 appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
1752 m_private
->extra->m_tableOfContentsLevels.append(unit);
1753 enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit));
1754 m_currentSection = unit;
1760 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1794 const QString imageFileName = getArgument();
1796 bool hasAltTextArgument{
false};
1797 if (isLeftBraceAhead()) {
1798 hasAltTextArgument =
true;
1799 imageText = getArgument();
1801 imageText = getRestOfLine();
1804 if (imageText.length() > 1) {
1805 if (imageText.front() ==
'"' && imageText.back() ==
'"') {
1806 imageText.removeFirst();
1807 imageText.removeLast();
1811 if (!hasAltTextArgument && imageText.isEmpty() && Config::instance().reportMissingAltTextForImages())
1812 location().report(QStringLiteral(
"\\%1 %2 is without a textual description, "
1813 "QDoc will not generate an alt text for the image.")
1815 .arg(imageFileName));
1816 appendAtom(
Atom(imageAtom, imageFileName));
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1835 const QString cmd{
"overload"};
1838 m_private->m_metacommandsUsed.insert(cmd);
1839 QString overloadArgument = isBlankLine() ? getMetaCommandArgument(cmd) : getRestOfLine();
1844 if (overloadArgument.trimmed() ==
"primary")
1845 overloadArgument =
"__qdoc_primary_overload__"_L1;
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1865 auto line_comment = [
this]() ->
bool {
1867 if (m_position + 2 > m_inputLength)
1869 if (m_input[m_position].unicode() ==
'/') {
1870 if (m_input[m_position + 1].unicode() ==
'/') {
1871 if (m_input[m_position + 2].unicode() ==
'!') {
1879 auto skip_everything_until_newline = [
this]() ->
void {
1880 while (m_position < m_inputLength && m_input[m_position] !=
'\n')
1886 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
1889 bool skipMe =
false;
1891 if (m_input[m_position] ==
'{') {
1892 target = getArgument();
1893 if (isLeftBraceAhead()) {
1894 str = getArgument();
1897 if (target.endsWith(
"::"))
1903 target = getArgument();
1904 str = cleanLink(target);
1905 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1919 skip_everything_until_newline();
1921 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1924 skip_everything_until_newline();
1925 skipSpacesOrOneEndl();
1926 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1927 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1945 if (ch == QLatin1Char(
' ')) {
1946 if (!atom->string().endsWith(QLatin1Char(
' ')))
1947 atom->appendChar(QLatin1Char(
' '));
1949 atom->appendChar(ch);
1952void DocParser::appendWord(
const QString &word)
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972void DocParser::appendEscapedIdentifier()
1974 Q_ASSERT(m_position < m_inputLength);
1975 Q_ASSERT(m_input.at(m_position) ==
'\\');
1980 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
1981 identifier += m_input.at(m_position);
1986 if (!identifier.isEmpty()) {
1991 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
1994 appendChar(QLatin1Char(
'\\'));
1998void DocParser::appendToCode(
const QString &markedCode)
2000 if (!isCode(m_lastAtom)) {
2004 m_lastAtom->concatenateString(markedCode);
2009 if (!isCode(m_lastAtom)) {
2010 appendAtom(
Atom(defaultType, markedCode));
2013 m_lastAtom->concatenateString(markedCode);
2019 if (m_paragraphState != OutsideParagraph)
2028 appendAtom(
Atom(leftType, string));
2029 m_indexStartedParagraph =
false;
2030 m_pendingParagraphLeftType = leftType;
2031 m_pendingParagraphRightType = rightType;
2032 m_pendingParagraphString = string;
2034 m_paragraphState = InSingleLineParagraph;
2036 m_paragraphState = InMultiLineParagraph;
2038 skipSpacesOrOneEndl();
2043 if (m_paragraphState == OutsideParagraph)
2046 if (!m_pendingFormats.isEmpty()) {
2047 location().warning(QStringLiteral(
"Missing '}'"));
2048 m_pendingFormats.clear();
2063 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2066 m_paragraphState = OutsideParagraph;
2067 m_indexStartedParagraph =
false;
2068 m_pendingParagraphRightType =
Atom::Nop;
2069 m_pendingParagraphString.clear();
2075 if (m_openedLists.isEmpty()) {
2076 m_openedLists.push(OpenedList(OpenedList::Value));
2088 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2093 m_openedLists.pop();
2099 if (m_inTableItem) {
2102 m_inTableItem =
false;
2104 if (m_inTableHeader) {
2106 m_inTableHeader =
false;
2110 m_inTableRow =
false;
2114void DocParser::quoteFromFile(
const QString &filename)
2127 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2128 if (!maybe_resolved_file) {
2137 QString details =
std::transform_reduce(
2138 (*file_resolver).get_search_directories().cbegin(),
2139 (*file_resolver).get_search_directories().cend(),
2140 u"Searched directories:"_s,
2142 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2145 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2161 CodeMarker *marker = CodeMarker::markerForFileName(QString{});
2162 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191bool DocParser::expandMacro(ArgumentParsingOptions options)
2193 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2195 if (options == ArgumentParsingOptions::Verbatim)
2199 qsizetype backslashPos = m_position++;
2200 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2201 cmdStr += m_input[m_position++];
2203 m_endPosition = m_position;
2204 if (!cmdStr.isEmpty()) {
2205 if (s_utilities
.macroHash.contains(cmdStr)) {
2207 if (!macro.m_defaultDef.isEmpty()) {
2208 QString expanded = expandMacroToString(cmdStr, macro);
2209 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2210 m_inputLength = m_input.size();
2211 m_position = backslashPos;
2214 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2218 m_position = backslashPos;
2219 if (options != ArgumentParsingOptions::MacroArguments
2221 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2225 }
else if (m_input[m_position].isSpace()) {
2227 }
else if (m_input[m_position].unicode() ==
'\\') {
2229 m_input.remove(m_position--, 1);
2235void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2237 if (args.isEmpty()) {
2242 for (
int j = 0; j < def.size(); ++j) {
2243 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2244 if (!rawString.isEmpty()) {
2250 rawString += def[j];
2253 if (!rawString.isEmpty())
2258QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2260 const QString &def{macro.m_defaultDef};
2264 rawString = macro.m_defaultDef;
2266 QStringList args{getMacroArguments(name, macro)};
2268 for (
int j = 0; j < def.size(); ++j) {
2269 int paramNo = def[j].unicode();
2270 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2273 QString matchExpr{macro.m_otherDefs.value(
"match")};
2274 if (matchExpr.isEmpty())
2278 QRegularExpression re(matchExpr);
2279 int capStart = (re.captureCount() > 0) ? 1 : 0;
2281 QRegularExpressionMatch match;
2282 while ((match = re.match(rawString, i)).hasMatch()) {
2283 for (
int c = capStart; c <= re.captureCount(); ++c)
2284 result += match.captured(c);
2285 i = match.capturedEnd();
2293 QString name = getOptionalArgument();
2295 if (name ==
"section1") {
2297 }
else if (name ==
"section2") {
2299 }
else if (name ==
"section3") {
2301 }
else if (name ==
"section4") {
2303 }
else if (name.isEmpty()) {
2306 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2325 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2327 while (m_position < m_input.size() && delimDepth >= 0) {
2328 switch (m_input[m_position].unicode()) {
2331 arg += QLatin1Char(
'{');
2336 if (delimDepth >= 0)
2337 arg += QLatin1Char(
'}');
2341 if (!expandMacro(options))
2342 arg += m_input[m_position++];
2345 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2348 arg += m_input[m_position];
2353 location().warning(QStringLiteral(
"Missing '}'"));
2355 m_endPosition = m_position;
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375QString
DocParser::getArgument(ArgumentParsingOptions options)
2377 skipSpacesOrOneEndl();
2380 qsizetype startPos = m_position;
2381 QString arg = getBracedArgument(options);
2382 if (arg.isEmpty()) {
2383 while ((m_position < m_input.size())
2384 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2385 switch (m_input[m_position].unicode()) {
2390 arg += m_input[m_position];
2397 if (m_position == startPos || delimDepth >= 0) {
2398 arg += m_input[m_position];
2403 if (!expandMacro(options))
2404 arg += m_input[m_position++];
2407 arg += m_input[m_position];
2411 m_endPosition = m_position;
2412 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2413 && !arg.endsWith(
"...")) {
2414 arg.truncate(arg.size() - 1);
2417 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2418 arg.truncate(arg.size() - 2);
2422 return arg.simplified();
2426
2427
2428
2429
2430
2431QString
DocParser::getBracketedArgument()
2435 skipSpacesOrOneEndl();
2436 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2438 while (m_position < m_input.size() && delimDepth >= 0) {
2439 switch (m_input[m_position].unicode()) {
2442 arg += QLatin1Char(
'[');
2447 if (delimDepth >= 0)
2448 arg += QLatin1Char(
']');
2452 arg += m_input[m_position];
2456 arg += m_input[m_position];
2461 location().warning(QStringLiteral(
"Missing ']'"));
2468
2469
2470
2471
2472
2473QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2477 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2478 args << getArgument(ArgumentParsingOptions::MacroArguments);
2480 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2481 " arguments (expected %2, got %3)")
2492
2493
2494
2495
2498 skipSpacesOrOneEndl();
2499 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2500 && m_input[m_position + 1].isLetterOrNumber()) {
2503 return getArgument();
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2524 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2525 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2526 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2527 trailingBackslash =
true;
2531 trailingBackslash =
false;
2535 return trailingBackslash;
2538 QString rest_of_line;
2540 bool trailing_backslash{
false };
2541 bool return_simplified_string{
false };
2543 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2544 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2546 if (!rest_of_line.isEmpty())
2547 rest_of_line += QLatin1Char(
' ');
2548 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2550 if (trailing_backslash) {
2551 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2552 return_simplified_string =
true;
2555 if (m_position < m_inputLength)
2558 if (!trailing_backslash)
2560 start_position = m_position;
2563 if (return_simplified_string)
2564 return rest_of_line.simplified();
2566 return rest_of_line.trimmed();
2570
2571
2572
2573
2574QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2578 qsizetype begin = m_position;
2581 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2582 if (m_input.at(m_position) ==
'(')
2584 else if (m_input.at(m_position) ==
')')
2586 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2590 if (m_position == m_input.size() && parenDepth > 0) {
2592 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2595 QString t = m_input.mid(begin, m_position - begin).simplified();
2603 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2605 auto match = rx.match(m_input, m_position);
2607 if (!match.hasMatch()) {
2608 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2609 m_position = m_input.size();
2611 qsizetype end = match.capturedStart();
2612 t = m_input.mid(m_position, end - m_position);
2613 m_position = match.capturedEnd();
2618void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2625 while (j < str.size()) {
2626 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2627 && paramNo <= args.size()) {
2628 const QString &r = args[paramNo - 1];
2629 str.replace(j, 2, r);
2630 j += qMin(1, r.size());
2638
2639
2640
2641
2642
2643
2646 QString code = untabifyEtc(getUntilEnd(cmd));
2647 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2649 int indent = indentLevel(code);
2650 code = dedent(indent, code);
2653 if (!marker && !m_private->m_topics.isEmpty()
2654 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2655 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2656 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2658 if (marker ==
nullptr)
2659 marker = CodeMarker::markerForCode(code);
2660 return marker->markedUpCode(code,
nullptr, location());
2665 qsizetype i = m_position;
2667 while (i < m_inputLength && m_input[i].isSpace()) {
2668 if (m_input[i] ==
'\n')
2678 qsizetype i = m_position;
2680 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2682 if (m_input[i] ==
'\n')
2686 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2689bool DocParser::isLeftBracketAhead(
int maxNewlines)
2692 qsizetype i = m_position;
2694 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2696 if (m_input[i] ==
'\n')
2700 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2704
2705
2708 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2709 && (m_input[m_position].unicode() !=
'\n'))
2714
2715
2718 qsizetype firstEndl = -1;
2719 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2720 QChar ch = m_input[m_position];
2722 if (firstEndl == -1) {
2723 firstEndl = m_position;
2725 m_position = firstEndl;
2735 while (m_position < m_inputLength && m_input[m_position].isSpace())
2739void DocParser::skipToNextPreprocessorCommand()
2741 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2742 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2743 auto match = rx.match(m_input, m_position + 1);
2745 if (!match.hasMatch())
2746 m_position = m_input.size();
2748 m_position = match.capturedStart();
2801 return cmds[cmd]
.name;
2806 return cmdName(endCmdFor(cmd));
2812 result.reserve(str.size());
2815 for (
const auto &character : str) {
2816 if (character == QLatin1Char(
'\r'))
2818 if (character == QLatin1Char(
'\t')) {
2819 result += &
" "[column % s_tabSize];
2820 column = ((column / s_tabSize) + 1) * s_tabSize;
2823 if (character == QLatin1Char(
'\n')) {
2824 while (result.endsWith(QLatin1Char(
' ')))
2826 result += character;
2830 result += character;
2834 while (result.endsWith(
"\n\n"))
2835 result.truncate(result.size() - 1);
2836 while (result.startsWith(QLatin1Char(
'\n')))
2837 result = result.mid(1);
2844 int minIndent = INT_MAX;
2847 for (
const auto &character : str) {
2848 if (character ==
'\n') {
2851 if (character !=
' ' && column < minIndent)
2867 for (
const auto &character : str) {
2868 if (character == QLatin1Char(
'\n')) {
2872 if (column >= level)
2873 result += character;
2881
2882
2890
2891
2901
2902
2903
2904
2905
2906
2907
2908
2911 static auto take_while = [](QStringView input,
auto predicate) {
2912 QStringView::size_type end{0};
2914 while (end < input.size() &&
std::invoke(predicate, input[end]))
2917 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2920 static auto peek = [](QStringView input, QChar c) {
2921 return !input.empty() && input.first() == c;
2924 static auto skip_one = [](QStringView input) {
2925 if (input.empty())
return std::make_tuple(QStringView{}, input);
2926 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
2929 static auto enclosed = [](QStringView input, QChar open, QChar close) {
2930 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
2932 auto [opened, without_open] = skip_one(input);
2933 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
2935 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
2937 auto [closed, without_close] = skip_one(remaining);
2939 return std::make_tuple(parsed.trimmed(), without_close);
2942 static auto one_of = [](
auto first,
auto second) {
2943 return [first, second](QStringView input) {
2944 auto [parsed, remaining] =
std::invoke(first, input);
2946 if (parsed.empty())
return std::invoke(second, input);
2947 else return std::make_tuple(parsed, remaining);
2951 static auto collect = [](QStringView input,
auto parser) {
2952 QStringList collected{};
2955 auto [parsed, remaining] =
std::invoke(parser, input);
2957 if (parsed.empty())
break;
2958 collected.append(parsed.toString());
2966 static auto spaces = [](QStringView input) {
2967 return take_while(input, [](QChar c){
return c.isSpace(); });
2970 static auto word = [](QStringView input) {
2971 return take_while(input, [](QChar c){
return !c.isSpace(); });
2974 static auto parse_argument = [](QStringView input) {
2975 auto [_, without_spaces] = spaces(input);
2978 [](QStringView input){
return enclosed(input,
'{',
'}'); },
2983 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
2987 QStringList segments = collect(atom->string(), parse_argument);
2989 QString categoryString;
2990 if (!segments.isEmpty())
2991 categoryString = segments.takeFirst();
2992 auto category = comparisonCategoryFromString(categoryString.toStdString());
2994 if (category == ComparisonCategory::None) {
2995 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
2996 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3000 if (segments.isEmpty()) {
3001 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3002 u"Provide at least one type name, or a list of types separated by spaces."_s);
3007 segments.removeDuplicates();
3008 atom->setString(segments.join(QLatin1Char(
';')));
3012 priv->m_metacommandsUsed.insert(cmd);
3015 const auto end{priv
->extra->m_comparesWithMap.cend()};
3016 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()
The Config class contains the configuration variables for controlling how qdoc produces documentation...
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)
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_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.