13#include <QtCore/qfile.h>
14#include <QtCore/qregularexpression.h>
15#include <QtCore/qtextstream.h>
23using namespace Qt::StringLiterals;
138} cmds[] = { {
"a",
CMD_A,
true },
140 {
"b",
CMD_B,
true },
145 {
"c",
CMD_C,
true },
153 {
"e",
CMD_E,
true },
178 {
"i",
CMD_I,
true },
250 qsizetype colonPos = link.indexOf(
':');
251 if ((colonPos == -1) || (!link.startsWith(
"file:") && !link.startsWith(
"mailto:")))
253 return link.mid(colonPos + 1).simplified();
262 while (cmds[i]
.name) {
266 Location::internalError(QStringLiteral(
"command %1 missing").arg(i));
272 const auto &outputFormats = config.getOutputFormats();
273 for (
const auto &format : outputFormats)
274 DocParser::s_quoting = DocParser::s_quoting
279 DocParser::file_resolver = &file_resolver;
283
284
285
286
287
288
289
290
292 const QSet<QString> &metaCommandSet,
const QSet<QString> &possibleTopics)
296 m_inputLength = m_input.size();
297 m_cachedLocation = docPrivate->m_start_loc;
298 m_cachedPosition = 0;
299 m_private = docPrivate;
303 m_paragraphState = OutsideParagraph;
304 m_inTableHeader =
false;
305 m_inTableRow =
false;
306 m_inTableItem =
false;
307 m_indexStartedParagraph =
false;
313 m_openedCommands.push(CMD_OMIT);
317 Atom *currentLinkAtom =
nullptr;
319 QStack<
bool> preprocessorSkipping;
320 int numPreprocessorSkipping = 0;
322 while (m_position < m_inputLength) {
323 QChar ch = m_input.at(m_position);
325 switch (ch.unicode()) {
328 m_backslashPosition = m_position;
330 while (m_position < m_inputLength) {
331 ch = m_input.at(m_position);
332 if (ch.isLetterOrNumber()) {
339 m_endPosition = m_position;
340 if (cmdStr.isEmpty()) {
341 if (m_position < m_inputLength) {
343 if (m_input.at(m_position).isSpace()) {
345 appendChar(QLatin1Char(
' '));
347 appendChar(m_input.at(m_position++));
364 m_private->m_params.insert(p1);
368 appendAtom(Atom(Atom::CodeBad,
369 getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr))));
376 location().warning(QStringLiteral(
"'\\bold' is deprecated. Use '\\b'"));
383 enterPara(Atom::BriefLeft, Atom::BriefRight);
387 p1 = untabifyEtc(getArgument(ArgumentParsingOptions::Verbatim));
388 marker = CodeMarker::markerForCode(p1);
389 appendAtom(
Atom(
Atom::C, marker->markedUpCode(p1,
nullptr, location())));
393 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
397 appendAtom(Atom(Atom::Code, getCode(CMD_CODE,
nullptr, getMetaCommandArgument(cmdStr))));
401 appendAtom(Atom(Atom::Qml,
402 getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String(
"QML")),
403 getMetaCommandArgument(cmdStr))));
407 appendAtom(Atom(Atom::DetailsLeft, getArgument()));
408 m_openedCommands.push(cmd);
417 p1 = getArgument(ArgumentParsingOptions::Verbatim);
419 m_openedCommands.push(cmd);
431 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
436 QString arg = getOptionalArgument();
443 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
446 int indent = arg.toInt();
447 for (
int i = 0; i < indent; ++i)
449 appendToCode(
"...\n");
453 if (!preprocessorSkipping.empty()) {
454 if (preprocessorSkipping.top()) {
455 --numPreprocessorSkipping;
457 ++numPreprocessorSkipping;
459 preprocessorSkipping.top() = !preprocessorSkipping.top();
460 (
void)getRestOfLine();
461 if (numPreprocessorSkipping)
462 skipToNextPreprocessorCommand();
465 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ELSE)));
475 if (closeCommand(cmd)) {
481 if (preprocessorSkipping.size() > 0) {
482 if (preprocessorSkipping.pop())
483 --numPreprocessorSkipping;
484 (
void)getRestOfLine();
485 if (numPreprocessorSkipping)
486 skipToNextPreprocessorCommand();
489 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDIF)));
493 if (closeCommand(cmd)) {
499 if (closeCommand(cmd)) {
507 if (closeCommand(cmd)) {
509 if (m_openedLists.top().isStarted()) {
510 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
511 appendAtom(Atom(Atom::ListRight, m_openedLists.top().styleString()));
520 if (closeCommand(cmd)) {
527 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDRAW)));
542 if (closeCommand(cmd)) {
548 if (closeCommand(cmd)) {
554 if (openCommand(cmd)) {
561 if (isLeftBracketAhead())
562 p2 = getBracketedArgument();
565 appendAtom(Atom(Atom::AnnotatedList, getArgument(), p2));
569 appendAtom(Atom(Atom::SinceList, getRestOfLine().simplified()));
573 if (isLeftBracketAhead())
574 p2 = getBracketedArgument();
577 QString arg1 = getArgument();
578 QString arg2 = getOptionalArgument();
581 appendAtom(Atom(Atom::GeneratedList, arg1, p2));
584 if (m_openedCommands.top() == CMD_TABLE) {
587 m_inTableHeader =
true;
589 if (m_openedCommands.contains(CMD_TABLE))
590 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
591 .arg(cmdName(CMD_HEADER),
592 cmdName(m_openedCommands.top())));
595 QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
600 location().warning(QStringLiteral(
601 "'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
611 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
612 if (preprocessorSkipping.top())
613 ++numPreprocessorSkipping;
614 if (numPreprocessorSkipping)
615 skipToNextPreprocessorCommand();
622 enterPara(Atom::ImportantLeft, Atom::ImportantRight);
626 QString fileName = getArgument();
627 QStringList parameters;
629 if (isLeftBraceAhead()) {
630 identifier = getArgument();
631 while (isLeftBraceAhead() && parameters.size() < 9)
632 parameters << getArgument();
634 identifier = getRestOfLine();
636 include(fileName, identifier, parameters);
643 if (m_paragraphState == OutsideParagraph) {
645 m_indexStartedParagraph =
true;
648 if (m_indexStartedParagraph
651 m_indexStartedParagraph =
false;
657 insertKeyword(getRestOfLine());
660 if (m_openedCommands.top() != CMD_TOC) {
661 location().warning(
"Command '\\%1' outside of '\\%2'"_L1
662 .arg(cmdName(cmd), cmdName(CMD_TOC)));
668 if (isLeftBracketAhead())
669 p2 = getBracketedArgument();
673 appendAtom(LinkAtom(p1, p2, location()));
675 if (isLeftBraceAhead()) {
680 appendAtom(Atom(Atom::String, cleanLink(p1)));
689 if (openCommand(cmd))
694 if (openCommand(cmd)) {
699 skipSpacesOrOneEndl();
703 if (openCommand(cmd)) {
705 m_openedLists.push(OpenedList(location(), getOptionalArgument()));
711 m_private->extra->m_metaMap.insert(p1, getArgument());
715 enterPara(Atom::NoteLeft, Atom::NoteRight);
717 case CMD_NOTRANSLATE:
721 location().warning(QStringLiteral(
"'\\o' is deprecated. Use '\\li'"));
725 if (m_openedCommands.top() == CMD_LIST) {
726 if (m_openedLists.top().isStarted())
727 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
729 appendAtom(Atom(Atom::ListLeft, m_openedLists.top().styleString()));
730 m_openedLists.top().next();
731 appendAtom(Atom(Atom::ListItemNumber, m_openedLists.top().numberString()));
732 appendAtom(Atom(Atom::ListItemLeft, m_openedLists.top().styleString()));
734 }
else if (m_openedCommands.top() == CMD_TABLE) {
737 if (isLeftBraceAhead()) {
739 if (isLeftBraceAhead())
743 if (!m_inTableHeader && !m_inTableRow) {
745 QStringLiteral(
"Missing '\\%1' or '\\%2' before '\\%3'")
750 }
else if (m_inTableItem) {
752 m_inTableItem =
false;
755 appendAtom(Atom(Atom::TableItemLeft, p1, p2));
756 m_inTableItem =
true;
759 QStringLiteral(
"Command '\\%1' outside of '\\%2' and '\\%3'")
768 if (!m_private->m_enumItemList.contains(p1))
769 m_private->m_enumItemList.append(p1);
770 if (!m_private->m_omitEnumItemList.contains(p1))
771 m_private->m_omitEnumItemList.append(p1);
772 skipSpacesOrOneEndl();
774 while (m_position < m_inputLength && !isBlankLine()) {
776 if (qsizetype pos = m_position; pos < m_input.size()
777 && m_input.at(pos++).unicode() ==
'\\') {
779 while (pos < m_input.size() && m_input[pos].isLetterOrNumber())
780 nextCmdStr += m_input[pos++];
782 if (nextCmd == cmd || nextCmd ==
CMD_VALUE)
791 p1 = getRestOfLine();
792 if (openCommand(cmd))
796 if (closeCommand(cmd)) {
804 QString rest = getRestOfLine();
809 appendToCode(m_quoter.quoteLine(location(), cmdStr, rest));
814 QString rest = getRestOfLine();
819 appendToCode(m_quoter.quoteTo(location(), cmdStr, rest));
824 QString rest = getRestOfLine();
829 appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest));
833 if (openCommand(cmd)) {
841 QString fileName = getArgument();
842 quoteFromFile(fileName);
847 appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString())));
853 QString arg = getArgument();
863 p1 = getRestOfLine();
865 location().warning(QStringLiteral(
"Missing format name after '\\%1'")
868 appendAtom(Atom(Atom::RawString, untabifyEtc(getUntilEnd(cmd))));
873 if (m_openedCommands.top() == CMD_TABLE) {
875 if (isLeftBraceAhead())
876 p1 = getArgument(ArgumentParsingOptions::Verbatim);
881 if (m_openedCommands.contains(CMD_TABLE))
882 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
883 .arg(cmdName(CMD_ROW),
884 cmdName(m_openedCommands.top())));
886 location().warning(QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
906 if (openCommand(cmd)) {
913 QString rest = getRestOfLine();
918 m_quoter.quoteLine(location(), cmdStr, rest);
923 QString rest = getRestOfLine();
928 m_quoter.quoteTo(location(), cmdStr, rest);
933 QString rest = getRestOfLine();
938 m_quoter.quoteUntil(location(), cmdStr, rest);
943 startFormat(p1, cmd);
947 QString snippet = getArgument();
948 QString identifier = getRestOfLine();
954 marker = CodeMarker::markerForFileName(snippet);
955 quoteFromFile(snippet);
956 appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType());
967 p1 = getOptionalArgument();
968 p2 = getOptionalArgument();
969 if (openCommand(cmd)) {
971 appendAtom(Atom(Atom::TableLeft, p1, p2));
972 m_inTableHeader =
false;
973 m_inTableRow =
false;
974 m_inTableItem =
false;
977 case CMD_TABLEOFCONTENTS:
978 location().report(
"\\%1 is deprecated and will be removed in a future version."_L1.arg(cmdName(cmd)));
979 if (isLeftBraceAhead())
983 if (m_openedCommands.top() == CMD_TABLE && !m_inTableItem) {
984 location().warning(
"Found a \\target command outside table item in a table.\n"
985 "Move the \\target inside the \\li to resolve this warning.");
987 insertTarget(getRestOfLine());
991 if (m_paragraphState != InSingleLineParagraph)
995 if (openCommand(cmd)) {
1001 if (closeCommand(cmd)) {
1019 uint unicodeChar = p1.toUInt(&ok, 0);
1020 if (!ok || (unicodeChar == 0x0000) || (unicodeChar > 0xFFFE))
1022 QStringLiteral(
"Invalid Unicode character '%1' specified with '%2'")
1030 if (m_openedLists.top().style() == OpenedList::Value) {
1033 if (p1.startsWith(QLatin1String(
"[since "))
1034 && p1.endsWith(QLatin1String(
"]"))) {
1035 p2 = p1.mid(7, p1.size() - 8);
1038 if (!m_private->m_enumItemList.contains(p1))
1039 m_private->m_enumItemList.append(p1);
1041 m_openedLists.top().next();
1045 if (!p2.isEmpty()) {
1052 skipSpacesOrOneEndl();
1061 enterPara(Atom::WarningLeft, Atom::WarningRight);
1067 if (metaCommandSet.contains(cmdStr)) {
1069 QString bracketedArg;
1070 m_private->m_metacommandsUsed.insert(cmdStr);
1071 if (isLeftBracketAhead())
1072 bracketedArg = getBracketedArgument();
1075 if (m_position < m_inputLength
1076 && (cmdStr == QLatin1String(
"obsolete")
1077 || cmdStr == QLatin1String(
"deprecated")))
1078 m_input[m_position] =
'\n';
1080 arg = getMetaCommandArgument(cmdStr);
1082 if (possibleTopics.contains(cmdStr)) {
1083 if (!cmdStr.endsWith(QLatin1String(
"propertygroup")))
1086 }
else if (s_utilities
.macroHash.contains(cmdStr)) {
1088 QStringList macroArgs;
1089 int numPendingFi = 0;
1090 int numFormatDefs = 0;
1091 for (
auto it = macro.m_otherDefs.constBegin();
1092 it != macro.m_otherDefs.constEnd(); ++it) {
1093 if (it.key() !=
"match") {
1094 if (numFormatDefs == 0)
1095 macroArgs = getMacroArguments(cmdStr, macro);
1097 expandMacro(*it, macroArgs);
1099 if (it == macro.m_otherDefs.constEnd()) {
1107 while (numPendingFi-- > 0)
1110 if (!macro.m_defaultDef.isEmpty()) {
1111 if (numFormatDefs > 0) {
1112 macro.m_defaultDefLocation.warning(
1113 QStringLiteral(
"Macro cannot have both "
1114 "format-specific and qdoc-"
1115 "syntax definitions"));
1117 QString expanded = expandMacroToString(cmdStr, macro);
1118 m_input.replace(m_backslashPosition,
1119 m_endPosition - m_backslashPosition, expanded);
1120 m_inputLength = m_input.size();
1121 m_position = m_backslashPosition;
1124 }
else if (isAutoLinkString(cmdStr)) {
1127 if (!cmdStr.endsWith(
"propertygroup")) {
1130 location().warning(QStringLiteral(
"Unknown command '\\%1'").arg(cmdStr),
1131 detailsUnknownCommand(metaCommandSet, cmdStr));
1142 qsizetype dashCount = 1;
1146 while ((m_position < m_inputLength) && (m_input.at(m_position) ==
'-')) {
1151 if (dashCount == 3) {
1153 const QChar emDash(8212);
1155 }
else if (dashCount == 2) {
1157 const QChar enDash(8211);
1163 for (qsizetype i = 0; i < dashCount; ++i)
1178 auto format = m_pendingFormats.find(m_braceDepth);
1179 if (format == m_pendingFormats.end()) {
1186 if (m_indexStartedParagraph)
1191 if (currentLinkAtom && currentLinkAtom->string().endsWith(
"::")) {
1195 currentLinkAtom->concatenateString(suffix);
1197 currentLinkAtom =
nullptr;
1201 m_pendingFormats.erase(format);
1207 if (m_position + 2 < m_inputLength)
1208 if (m_input.at(m_position + 1) ==
'/')
1209 if (m_input.at(m_position + 2) ==
'!') {
1212 if (m_input.at(m_position - 1) ==
'\n')
1228 if (m_paragraphState == OutsideParagraph) {
1240 && (m_paragraphState == InSingleLineParagraph || isBlankLine())) {
1253 qsizetype startPos = m_position;
1255 bool autolink = (!m_pendingFormats.isEmpty() &&
1257 false : isAutoLinkString(m_input, m_position);
1258 if (m_position == startPos) {
1259 if (!ch.isSpace()) {
1264 QString word = m_input.mid(startPos, m_position - startPos);
1266 if (s_ignoreWords.contains(word) || word.startsWith(QString(
"__")))
1281 if (m_openedCommands.top() == CMD_LEGALESE) {
1283 m_openedCommands.pop();
1286 if (m_openedCommands.top() != CMD_OMIT) {
1288 QStringLiteral(
"Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
1289 }
else if (preprocessorSkipping.size() > 0) {
1290 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(
CMD_ENDIF)));
1294 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1302
1303
1306 while (!m_openedInputs.isEmpty() && m_openedInputs.top() <= m_position) {
1307 m_cachedLocation.pop();
1308 m_cachedPosition = m_openedInputs.pop();
1310 while (m_cachedPosition < m_position)
1311 m_cachedLocation.advance(m_input.at(m_cachedPosition++));
1312 return m_cachedLocation;
1315QString
DocParser::detailsUnknownCommand(
const QSet<QString> &metaCommandSet,
const QString &str)
1317 QSet<QString> commandSet = metaCommandSet;
1319 while (cmds[i]
.name !=
nullptr) {
1320 commandSet.insert(cmds[i]
.name);
1324 QString best = nearestName(str, commandSet);
1327 return QStringLiteral(
"Maybe you meant '\\%1'?").arg(best);
1331
1332
1333
1334
1335
1336
1337
1339 const QString &cmdString,
const QString &previousDefinition)
1341 if (duplicateDefinition.isEmpty()) {
1342 location.warning(
"Expected an argument for \\%1"_L1.arg(cmdString));
1345 "Duplicate %3 name '%1'. The previous occurrence is here: %2"_L1
1346 .arg(duplicateDefinition, previousDefinition, cmdString));
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364void DocParser::insertTarget(
const QString &target)
1366 if (target.isEmpty() || m_targetMap.contains(target))
1367 return warnAboutEmptyOrPreexistingTarget(location(), target,
1368 s_utilities.cmdHash.key(CMD_TARGET), m_targetMap[target].toString());
1370 m_targetMap.insert(target, location());
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391void DocParser::insertKeyword(
const QString &keyword)
1393 if (keyword.isEmpty() || m_targetMap.contains(keyword))
1394 return warnAboutEmptyOrPreexistingTarget(location(), keyword,
1395 s_utilities.cmdHash.key(CMD_KEYWORD), m_targetMap[keyword].toString());
1397 m_targetMap.insert(keyword, location());
1404void DocParser::include(
const QString &fileName,
const QString &identifier,
const QStringList ¶meters)
1407 location().fatal(QStringLiteral(
"Too many nested '\\%1's").arg(cmdName(
CMD_INCLUDE)));
1408 QString filePath = Config::instance().getIncludeFilePath(fileName);
1409 if (filePath.isEmpty()) {
1410 location().warning(QStringLiteral(
"Cannot find qdoc include file '%1'").arg(fileName));
1412 QFile inFile(filePath);
1413 if (!inFile.open(QFile::ReadOnly)) {
1415 QStringLiteral(
"Cannot open qdoc include file '%1'").arg(filePath));
1417 location().push(fileName);
1418 QTextStream inStream(&inFile);
1419 QString includedContent = inStream.readAll();
1422 if (identifier.isEmpty()) {
1423 expandArgumentsInString(includedContent, parameters);
1424 m_input.insert(m_position, includedContent);
1425 m_inputLength = m_input.size();
1426 m_openedInputs.push(m_position + includedContent.size());
1428 QStringList lineBuffer = includedContent.split(QLatin1Char(
'\n'));
1429 qsizetype bufLen{lineBuffer.size()};
1431 QStringView trimmedLine;
1432 for (i = 0; i < bufLen; ++i) {
1433 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1434 if (trimmedLine.startsWith(QLatin1String(
"//!")) &&
1435 trimmedLine.contains(identifier))
1438 if (i < bufLen - 1) {
1442 QStringLiteral(
"Cannot find '%1' in '%2'").arg(identifier, filePath));
1447 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1448 if (trimmedLine.startsWith(QLatin1String(
"//!")) &&
1449 trimmedLine.contains(identifier))
1452 result += lineBuffer[i] + QLatin1Char(
'\n');
1454 }
while (i < bufLen);
1456 expandArgumentsInString(result, parameters);
1457 if (result.isEmpty()) {
1458 location().warning(QStringLiteral(
"Empty qdoc snippet '%1' in '%2'")
1459 .arg(identifier, filePath));
1461 m_input.insert(m_position, result);
1462 m_inputLength = m_input.size();
1463 m_openedInputs.push(m_position + result.size());
1470void DocParser::startFormat(
const QString &format,
int cmd)
1474 for (
const auto &item : std::as_const(m_pendingFormats)) {
1475 if (item == format) {
1476 location().warning(QStringLiteral(
"Cannot nest '\\%1' commands").arg(cmdName(cmd)));
1483 if (isLeftBraceAhead()) {
1484 skipSpacesOrOneEndl();
1485 m_pendingFormats.insert(m_braceDepth, format);
1489 const auto &arg{getArgument()};
1494 m_indexStartedParagraph =
false;
1503 int outer = m_openedCommands.top();
1506 if ((cmd == CMD_COMPARESWITH || cmd == CMD_TOC)
1507 && m_openedCommands.contains(cmd)) {
1508 location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
1525 m_openedCommands.push(cmd);
1528 QStringLiteral(
"Can't use '\\%1' in '\\%2'").arg(cmdName(cmd), cmdName(outer)));
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
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
1610inline bool DocParser::isAutoLinkString(
const QString &word)
1612 qsizetype start = 0;
1613 return isAutoLinkString(word, start) && (start == word.size());
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637bool DocParser::isAutoLinkString(
const QString &word, qsizetype &curPos)
1639 qsizetype len = word.size();
1640 qsizetype startPos = curPos;
1641 int numUppercase = 0;
1642 int numLowercase = 0;
1643 int numStrangeSymbols = 0;
1645 while (curPos < len) {
1646 unsigned char latin1Ch = word.at(curPos).toLatin1();
1647 if (islower(latin1Ch)) {
1650 }
else if (isupper(latin1Ch)) {
1651 if (curPos > startPos)
1654 }
else if (isdigit(latin1Ch)) {
1655 if (curPos > startPos)
1659 }
else if (latin1Ch ==
'_' || latin1Ch ==
'@') {
1660 ++numStrangeSymbols;
1662 }
else if ((latin1Ch ==
':') && (curPos < len - 1)
1663 && (word.at(curPos + 1) == QLatin1Char(
':'))) {
1664 ++numStrangeSymbols;
1666 }
else if (latin1Ch ==
'(') {
1667 if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(
')'))) {
1668 ++numStrangeSymbols;
1678 return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
1683 if (endCmdFor(m_openedCommands.top()) == endCmd && m_openedCommands.size() > 1) {
1684 m_openedCommands.pop();
1687 bool contains =
false;
1688 QStack<
int> opened2 = m_openedCommands;
1689 while (opened2.size() > 1) {
1698 while (endCmdFor(m_openedCommands.top()) != endCmd && m_openedCommands.size() > 1) {
1700 QStringLiteral(
"Missing '\\%1' before '\\%2'")
1701 .arg(endCmdName(m_openedCommands.top()), cmdName(endCmd)));
1702 m_openedCommands.pop();
1705 location().warning(QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(endCmd)));
1716 m_currentSection =
static_cast<
Doc::
Sections>(unit);
1719 endSection(unit, cmd);
1722 appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
1725 m_private
->extra->m_tableOfContentsLevels.append(unit);
1726 enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit));
1727 m_currentSection = unit;
1733 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1767 const QString imageFileName = getArgument();
1769 bool hasAltTextArgument{
false};
1770 if (isLeftBraceAhead()) {
1771 hasAltTextArgument =
true;
1772 imageText = getArgument();
1774 imageText = getRestOfLine();
1777 if (imageText.length() > 1) {
1778 if (imageText.front() ==
'"' && imageText.back() ==
'"') {
1779 imageText.removeFirst();
1780 imageText.removeLast();
1784 if (!hasAltTextArgument && imageText.isEmpty() && Config::instance().reportMissingAltTextForImages())
1785 location().report(QStringLiteral(
"\\%1 %2 is without a textual description, "
1786 "QDoc will not generate an alt text for the image.")
1788 .arg(imageFileName));
1789 appendAtom(
Atom(imageAtom, imageFileName));
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1808 const QString cmd{
"overload"};
1811 m_private->m_metacommandsUsed.insert(cmd);
1812 QString overloadArgument = isBlankLine() ? getMetaCommandArgument(cmd) : getRestOfLine();
1817 if (overloadArgument.trimmed() ==
"primary")
1818 overloadArgument =
"__qdoc_primary_overload__"_L1;
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1838 auto line_comment = [
this]() ->
bool {
1840 if (m_position + 2 > m_inputLength)
1842 if (m_input[m_position].unicode() ==
'/') {
1843 if (m_input[m_position + 1].unicode() ==
'/') {
1844 if (m_input[m_position + 2].unicode() ==
'!') {
1852 auto skip_everything_until_newline = [
this]() ->
void {
1853 while (m_position < m_inputLength && m_input[m_position] !=
'\n')
1859 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
1862 bool skipMe =
false;
1864 if (m_input[m_position] ==
'{') {
1865 target = getArgument();
1867 if (m_position < m_inputLength && m_input[m_position] ==
'{') {
1868 str = getArgument();
1871 if (target.endsWith(
"::"))
1877 target = getArgument();
1878 str = cleanLink(target);
1879 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1893 skip_everything_until_newline();
1895 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1898 skip_everything_until_newline();
1899 skipSpacesOrOneEndl();
1900 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1901 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1919 if (ch == QLatin1Char(
' ')) {
1920 if (!atom->string().endsWith(QLatin1Char(
' ')))
1921 atom->appendChar(QLatin1Char(
' '));
1923 atom->appendChar(ch);
1926void DocParser::appendWord(
const QString &word)
1934void DocParser::appendToCode(
const QString &markedCode)
1936 if (!isCode(m_lastAtom)) {
1940 m_lastAtom->concatenateString(markedCode);
1945 if (!isCode(m_lastAtom)) {
1946 appendAtom(
Atom(defaultType, markedCode));
1949 m_lastAtom->concatenateString(markedCode);
1955 if (m_paragraphState != OutsideParagraph)
1964 appendAtom(
Atom(leftType, string));
1965 m_indexStartedParagraph =
false;
1966 m_pendingParagraphLeftType = leftType;
1967 m_pendingParagraphRightType = rightType;
1968 m_pendingParagraphString = string;
1970 m_paragraphState = InSingleLineParagraph;
1972 m_paragraphState = InMultiLineParagraph;
1974 skipSpacesOrOneEndl();
1979 if (m_paragraphState == OutsideParagraph)
1982 if (!m_pendingFormats.isEmpty()) {
1983 location().warning(QStringLiteral(
"Missing '}'"));
1984 m_pendingFormats.clear();
1994 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
1996 m_paragraphState = OutsideParagraph;
1997 m_indexStartedParagraph =
false;
1998 m_pendingParagraphRightType =
Atom::Nop;
1999 m_pendingParagraphString.clear();
2005 if (m_openedLists.isEmpty()) {
2006 m_openedLists.push(OpenedList(OpenedList::Value));
2018 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2023 m_openedLists.pop();
2029 if (m_inTableItem) {
2032 m_inTableItem =
false;
2034 if (m_inTableHeader) {
2036 m_inTableHeader =
false;
2040 m_inTableRow =
false;
2044void DocParser::quoteFromFile(
const QString &filename)
2057 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2058 if (!maybe_resolved_file) {
2067 QString details = std::transform_reduce(
2068 (*file_resolver).get_search_directories().cbegin(),
2069 (*file_resolver).get_search_directories().cend(),
2070 u"Searched directories:"_s,
2072 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2075 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2091 CodeMarker *marker = CodeMarker::markerForFileName(QString{});
2092 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2093 }
else Doc::quoteFromFile(location(), m_quoter, *maybe_resolved_file);
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121bool DocParser::expandMacro(ArgumentParsingOptions options)
2123 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2125 if (options == ArgumentParsingOptions::Verbatim)
2129 qsizetype backslashPos = m_position++;
2130 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2131 cmdStr += m_input[m_position++];
2133 m_endPosition = m_position;
2134 if (!cmdStr.isEmpty()) {
2135 if (s_utilities
.macroHash.contains(cmdStr)) {
2137 if (!macro.m_defaultDef.isEmpty()) {
2138 QString expanded = expandMacroToString(cmdStr, macro);
2139 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2140 m_inputLength = m_input.size();
2141 m_position = backslashPos;
2144 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2148 m_position = backslashPos;
2149 if (options != ArgumentParsingOptions::MacroArguments
2151 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2155 }
else if (m_input[m_position].isSpace()) {
2157 }
else if (m_input[m_position].unicode() ==
'\\') {
2159 m_input.remove(m_position--, 1);
2165void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2167 if (args.isEmpty()) {
2172 for (
int j = 0; j < def.size(); ++j) {
2173 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2174 if (!rawString.isEmpty()) {
2180 rawString += def[j];
2183 if (!rawString.isEmpty())
2188QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2190 const QString &def{macro.m_defaultDef};
2194 rawString = macro.m_defaultDef;
2196 QStringList args{getMacroArguments(name, macro)};
2198 for (
int j = 0; j < def.size(); ++j) {
2199 int paramNo = def[j].unicode();
2200 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2203 QString matchExpr{macro.m_otherDefs.value(
"match")};
2204 if (matchExpr.isEmpty())
2208 QRegularExpression re(matchExpr);
2209 int capStart = (re.captureCount() > 0) ? 1 : 0;
2211 QRegularExpressionMatch match;
2212 while ((match = re.match(rawString, i)).hasMatch()) {
2213 for (
int c = capStart; c <= re.captureCount(); ++c)
2214 result += match.captured(c);
2215 i = match.capturedEnd();
2223 QString name = getOptionalArgument();
2225 if (name ==
"section1") {
2227 }
else if (name ==
"section2") {
2229 }
else if (name ==
"section3") {
2231 }
else if (name ==
"section4") {
2233 }
else if (name.isEmpty()) {
2236 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2255 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2257 while (m_position < m_input.size() && delimDepth >= 0) {
2258 switch (m_input[m_position].unicode()) {
2261 arg += QLatin1Char(
'{');
2266 if (delimDepth >= 0)
2267 arg += QLatin1Char(
'}');
2271 if (!expandMacro(options))
2272 arg += m_input[m_position++];
2275 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2278 arg += m_input[m_position];
2283 location().warning(QStringLiteral(
"Missing '}'"));
2285 m_endPosition = m_position;
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305QString
DocParser::getArgument(ArgumentParsingOptions options)
2307 skipSpacesOrOneEndl();
2310 qsizetype startPos = m_position;
2311 QString arg = getBracedArgument(options);
2312 if (arg.isEmpty()) {
2313 while ((m_position < m_input.size())
2314 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2315 switch (m_input[m_position].unicode()) {
2320 arg += m_input[m_position];
2327 if (m_position == startPos || delimDepth >= 0) {
2328 arg += m_input[m_position];
2333 if (!expandMacro(options))
2334 arg += m_input[m_position++];
2337 arg += m_input[m_position];
2341 m_endPosition = m_position;
2342 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2343 && !arg.endsWith(
"...")) {
2344 arg.truncate(arg.size() - 1);
2347 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2348 arg.truncate(arg.size() - 2);
2352 return arg.simplified();
2356
2357
2358
2359
2360
2361QString
DocParser::getBracketedArgument()
2365 skipSpacesOrOneEndl();
2366 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2368 while (m_position < m_input.size() && delimDepth >= 0) {
2369 switch (m_input[m_position].unicode()) {
2372 arg += QLatin1Char(
'[');
2377 if (delimDepth >= 0)
2378 arg += QLatin1Char(
']');
2382 arg += m_input[m_position];
2386 arg += m_input[m_position];
2391 location().warning(QStringLiteral(
"Missing ']'"));
2398
2399
2400
2401
2402
2403QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2407 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2408 args << getArgument(ArgumentParsingOptions::MacroArguments);
2410 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2411 " arguments (expected %2, got %3)")
2422
2423
2424
2425
2428 skipSpacesOrOneEndl();
2429 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2430 && m_input[m_position + 1].isLetterOrNumber()) {
2433 return getArgument();
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2454 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2455 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2456 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2457 trailingBackslash =
true;
2461 trailingBackslash =
false;
2465 return trailingBackslash;
2468 QString rest_of_line;
2470 bool trailing_backslash{
false };
2471 bool return_simplified_string{
false };
2473 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2474 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2476 if (!rest_of_line.isEmpty())
2477 rest_of_line += QLatin1Char(
' ');
2478 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2480 if (trailing_backslash) {
2481 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2482 return_simplified_string =
true;
2485 if (m_position < m_inputLength)
2488 if (!trailing_backslash)
2490 start_position = m_position;
2493 if (return_simplified_string)
2494 return rest_of_line.simplified();
2496 return rest_of_line.trimmed();
2500
2501
2502
2503
2504QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2508 qsizetype begin = m_position;
2511 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2512 if (m_input.at(m_position) ==
'(')
2514 else if (m_input.at(m_position) ==
')')
2516 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2520 if (m_position == m_input.size() && parenDepth > 0) {
2522 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2525 QString t = m_input.mid(begin, m_position - begin).simplified();
2533 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2535 auto match = rx.match(m_input, m_position);
2537 if (!match.hasMatch()) {
2538 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2539 m_position = m_input.size();
2541 qsizetype end = match.capturedStart();
2542 t = m_input.mid(m_position, end - m_position);
2543 m_position = match.capturedEnd();
2548void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2555 while (j < str.size()) {
2556 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2557 && paramNo <= args.size()) {
2558 const QString &r = args[paramNo - 1];
2559 str.replace(j, 2, r);
2560 j += qMin(1, r.size());
2568
2569
2570
2571
2572
2573
2576 QString code = untabifyEtc(getUntilEnd(cmd));
2577 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2579 int indent = indentLevel(code);
2580 code = dedent(indent, code);
2583 if (!marker && !m_private->m_topics.isEmpty()
2584 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2585 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2586 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2588 if (marker ==
nullptr)
2589 marker = CodeMarker::markerForCode(code);
2590 return marker->markedUpCode(code,
nullptr, location());
2595 qsizetype i = m_position;
2597 while (i < m_inputLength && m_input[i].isSpace()) {
2598 if (m_input[i] ==
'\n')
2608 qsizetype i = m_position;
2610 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2612 if (m_input[i] ==
'\n')
2616 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2622 qsizetype i = m_position;
2624 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2626 if (m_input[i] ==
'\n')
2630 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'[';
2634
2635
2638 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2639 && (m_input[m_position].unicode() !=
'\n'))
2644
2645
2648 qsizetype firstEndl = -1;
2649 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2650 QChar ch = m_input[m_position];
2652 if (firstEndl == -1) {
2653 firstEndl = m_position;
2655 m_position = firstEndl;
2665 while (m_position < m_inputLength && m_input[m_position].isSpace())
2669void DocParser::skipToNextPreprocessorCommand()
2671 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2672 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2673 auto match = rx.match(m_input, m_position + 1);
2675 if (!match.hasMatch())
2676 m_position = m_input.size();
2678 m_position = match.capturedStart();
2731 return cmds[cmd]
.name;
2736 return cmdName(endCmdFor(cmd));
2742 result.reserve(str.size());
2745 for (
const auto &character : str) {
2746 if (character == QLatin1Char(
'\r'))
2748 if (character == QLatin1Char(
'\t')) {
2749 result += &
" "[column % s_tabSize];
2750 column = ((column / s_tabSize) + 1) * s_tabSize;
2753 if (character == QLatin1Char(
'\n')) {
2754 while (result.endsWith(QLatin1Char(
' ')))
2756 result += character;
2760 result += character;
2764 while (result.endsWith(
"\n\n"))
2765 result.truncate(result.size() - 1);
2766 while (result.startsWith(QLatin1Char(
'\n')))
2767 result = result.mid(1);
2774 int minIndent = INT_MAX;
2777 for (
const auto &character : str) {
2778 if (character ==
'\n') {
2781 if (character !=
' ' && column < minIndent)
2797 for (
const auto &character : str) {
2798 if (character == QLatin1Char(
'\n')) {
2802 if (column >= level)
2803 result += character;
2811
2812
2820
2821
2831
2832
2833
2834
2835
2836
2837
2838
2841 static auto take_while = [](QStringView input,
auto predicate) {
2842 QStringView::size_type end{0};
2844 while (end < input.size() &&
std::invoke(predicate, input[end]))
2847 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2850 static auto peek = [](QStringView input, QChar c) {
2851 return !input.empty() && input.first() == c;
2854 static auto skip_one = [](QStringView input) {
2855 if (input.empty())
return std::make_tuple(QStringView{}, input);
2856 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
2859 static auto enclosed = [](QStringView input, QChar open, QChar close) {
2860 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
2862 auto [opened, without_open] = skip_one(input);
2863 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
2865 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
2867 auto [closed, without_close] = skip_one(remaining);
2869 return std::make_tuple(parsed.trimmed(), without_close);
2872 static auto one_of = [](
auto first,
auto second) {
2873 return [first, second](QStringView input) {
2874 auto [parsed, remaining] =
std::invoke(first, input);
2876 if (parsed.empty())
return std::invoke(second, input);
2877 else return std::make_tuple(parsed, remaining);
2881 static auto collect = [](QStringView input,
auto parser) {
2882 QStringList collected{};
2885 auto [parsed, remaining] =
std::invoke(parser, input);
2887 if (parsed.empty())
break;
2888 collected.append(parsed.toString());
2896 static auto spaces = [](QStringView input) {
2897 return take_while(input, [](QChar c){
return c.isSpace(); });
2900 static auto word = [](QStringView input) {
2901 return take_while(input, [](QChar c){
return !c.isSpace(); });
2904 static auto parse_argument = [](QStringView input) {
2905 auto [_, without_spaces] = spaces(input);
2908 [](QStringView input){
return enclosed(input,
'{',
'}'); },
2913 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
2917 QStringList segments = collect(atom->string(), parse_argument);
2919 QString categoryString;
2920 if (!segments.isEmpty())
2921 categoryString = segments.takeFirst();
2922 auto category = comparisonCategoryFromString(categoryString.toStdString());
2924 if (category == ComparisonCategory::None) {
2925 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
2926 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
2930 if (segments.isEmpty()) {
2931 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
2932 u"Provide at least one type name, or a list of types separated by spaces."_s);
2937 segments.removeDuplicates();
2938 atom->setString(segments.join(QLatin1Char(
';')));
2942 priv->m_metacommandsUsed.insert(cmd);
2945 const auto end{priv
->extra->m_comparesWithMap.cend()};
2946 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
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.
#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)
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.