/tools/qdoc3/doc.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 3248 lines · 2907 code · 231 blank · 110 comment · 739 complexity · 317f8885d5055b0a56885d79f64ca479 MD5 · raw file

Large files are truncated click here to view the full file

  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
  4. ** All rights reserved.
  5. ** Contact: Nokia Corporation (qt-info@nokia.com)
  6. **
  7. ** This file is part of the tools applications of the Qt Toolkit.
  8. **
  9. ** $QT_BEGIN_LICENSE:LGPL$
  10. ** GNU Lesser General Public License Usage
  11. ** This file may be used under the terms of the GNU Lesser General Public
  12. ** License version 2.1 as published by the Free Software Foundation and
  13. ** appearing in the file LICENSE.LGPL included in the packaging of this
  14. ** file. Please review the following information to ensure the GNU Lesser
  15. ** General Public License version 2.1 requirements will be met:
  16. ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  17. **
  18. ** In addition, as a special exception, Nokia gives you certain additional
  19. ** rights. These rights are described in the Nokia Qt LGPL Exception
  20. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  21. **
  22. ** GNU General Public License Usage
  23. ** Alternatively, this file may be used under the terms of the GNU General
  24. ** Public License version 3.0 as published by the Free Software Foundation
  25. ** and appearing in the file LICENSE.GPL included in the packaging of this
  26. ** file. Please review the following information to ensure the GNU General
  27. ** Public License version 3.0 requirements will be met:
  28. ** http://www.gnu.org/copyleft/gpl.html.
  29. **
  30. ** Other Usage
  31. ** Alternatively, this file may be used in accordance with the terms and
  32. ** conditions contained in a signed written agreement between you and Nokia.
  33. **
  34. **
  35. **
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "config.h"
  42. #include "doc.h"
  43. #include "codemarker.h"
  44. #include "editdistance.h"
  45. #include "openedlist.h"
  46. #include "quoter.h"
  47. #include "text.h"
  48. #include "tokenizer.h"
  49. #include <qdatetime.h>
  50. #include <qfile.h>
  51. #include <qfileinfo.h>
  52. #include <qhash.h>
  53. #include <qtextstream.h>
  54. #include <qregexp.h>
  55. #include <ctype.h>
  56. #include <limits.h>
  57. #include <qdebug.h>
  58. QT_BEGIN_NAMESPACE
  59. Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
  60. Q_GLOBAL_STATIC(QStringList, null_QStringList)
  61. Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
  62. //Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
  63. Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap)
  64. struct Macro
  65. {
  66. QString defaultDef;
  67. Location defaultDefLocation;
  68. QStringMap otherDefs;
  69. int numParams;
  70. };
  71. enum {
  72. CMD_A,
  73. CMD_ABSTRACT,
  74. CMD_ANNOTATEDLIST,
  75. CMD_BADCODE,
  76. CMD_BASENAME,
  77. CMD_BOLD,
  78. CMD_BRIEF,
  79. CMD_C,
  80. CMD_CAPTION,
  81. CMD_CHAPTER, // 9
  82. CMD_CODE,
  83. CMD_CODELINE,
  84. CMD_DIV,
  85. CMD_DOTS,
  86. CMD_ELSE,
  87. CMD_ENDABSTRACT,
  88. CMD_ENDCHAPTER,
  89. CMD_ENDCODE,
  90. CMD_ENDDIV,
  91. CMD_ENDFOOTNOTE,
  92. CMD_ENDIF,
  93. CMD_ENDLEGALESE,
  94. CMD_ENDLINK,
  95. CMD_ENDLIST,
  96. CMD_ENDOMIT,
  97. CMD_ENDPART,
  98. CMD_ENDQUOTATION,
  99. CMD_ENDRAW,
  100. CMD_ENDSECTION1,
  101. CMD_ENDSECTION2,
  102. CMD_ENDSECTION3,
  103. CMD_ENDSECTION4,
  104. CMD_ENDSIDEBAR,
  105. CMD_ENDTABLE,
  106. CMD_EXPIRE,
  107. CMD_FOOTNOTE,
  108. CMD_GENERATELIST,
  109. CMD_GRANULARITY,
  110. CMD_HEADER,
  111. CMD_I,
  112. CMD_IF,
  113. CMD_IMAGE,
  114. CMD_INCLUDE,
  115. CMD_INLINEIMAGE,
  116. CMD_INDEX,
  117. CMD_KEYWORD,
  118. CMD_L,
  119. CMD_LEGALESE,
  120. CMD_LINK,
  121. CMD_LIST,
  122. CMD_META,
  123. CMD_NEWCODE,
  124. CMD_O,
  125. CMD_OLDCODE,
  126. CMD_OMIT,
  127. CMD_OMITVALUE,
  128. CMD_OVERLOAD,
  129. CMD_PART,
  130. CMD_PRINTLINE,
  131. CMD_PRINTTO,
  132. CMD_PRINTUNTIL,
  133. CMD_QUOTATION,
  134. CMD_QUOTEFILE,
  135. CMD_QUOTEFROMFILE,
  136. CMD_QUOTEFUNCTION,
  137. CMD_RAW,
  138. CMD_ROW,
  139. CMD_SA,
  140. CMD_SECTION1, // 68
  141. CMD_SECTION2, // 69
  142. CMD_SECTION3, // 70
  143. CMD_SECTION4, // 71
  144. CMD_SIDEBAR,
  145. CMD_SINCELIST,
  146. CMD_SKIPLINE,
  147. CMD_SKIPTO,
  148. CMD_SKIPUNTIL,
  149. CMD_SNIPPET,
  150. CMD_SPAN,
  151. CMD_SUB,
  152. CMD_SUP,
  153. CMD_TABLE,
  154. CMD_TABLEOFCONTENTS,
  155. CMD_TARGET,
  156. CMD_TT,
  157. CMD_UNDERLINE,
  158. CMD_UNICODE,
  159. CMD_VALUE,
  160. CMD_WARNING,
  161. CMD_QML,
  162. CMD_ENDQML,
  163. CMD_CPP,
  164. CMD_ENDCPP,
  165. CMD_QMLTEXT,
  166. CMD_ENDQMLTEXT,
  167. CMD_CPPTEXT,
  168. CMD_ENDCPPTEXT,
  169. CMD_JS,
  170. CMD_ENDJS,
  171. NOT_A_CMD
  172. };
  173. static struct {
  174. const char *english;
  175. int no;
  176. QString *alias;
  177. } cmds[] = {
  178. { "a", CMD_A, 0 },
  179. { "abstract", CMD_ABSTRACT, 0 },
  180. { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
  181. { "badcode", CMD_BADCODE, 0 },
  182. { "basename", CMD_BASENAME, 0 }, // ### don't document for now
  183. { "bold", CMD_BOLD, 0 },
  184. { "brief", CMD_BRIEF, 0 },
  185. { "c", CMD_C, 0 },
  186. { "caption", CMD_CAPTION, 0 },
  187. { "chapter", CMD_CHAPTER, 0 },
  188. { "code", CMD_CODE, 0 },
  189. { "codeline", CMD_CODELINE, 0},
  190. { "div", CMD_DIV, 0 },
  191. { "dots", CMD_DOTS, 0 },
  192. { "else", CMD_ELSE, 0 },
  193. { "endabstract", CMD_ENDABSTRACT, 0 },
  194. { "endchapter", CMD_ENDCHAPTER, 0 },
  195. { "endcode", CMD_ENDCODE, 0 },
  196. { "enddiv", CMD_ENDDIV, 0 },
  197. { "endfootnote", CMD_ENDFOOTNOTE, 0 },
  198. { "endif", CMD_ENDIF, 0 },
  199. { "endlegalese", CMD_ENDLEGALESE, 0 },
  200. { "endlink", CMD_ENDLINK, 0 },
  201. { "endlist", CMD_ENDLIST, 0 },
  202. { "endomit", CMD_ENDOMIT, 0 },
  203. { "endpart", CMD_ENDPART, 0 },
  204. { "endquotation", CMD_ENDQUOTATION, 0 },
  205. { "endraw", CMD_ENDRAW, 0 },
  206. { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
  207. { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now
  208. { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now
  209. { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now
  210. { "endsidebar", CMD_ENDSIDEBAR, 0 },
  211. { "endtable", CMD_ENDTABLE, 0 },
  212. { "expire", CMD_EXPIRE, 0 },
  213. { "footnote", CMD_FOOTNOTE, 0 },
  214. { "generatelist", CMD_GENERATELIST, 0 },
  215. { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
  216. { "header", CMD_HEADER, 0 },
  217. { "i", CMD_I, 0 },
  218. { "if", CMD_IF, 0 },
  219. { "image", CMD_IMAGE, 0 },
  220. { "include", CMD_INCLUDE, 0 },
  221. { "inlineimage", CMD_INLINEIMAGE, 0 },
  222. { "index", CMD_INDEX, 0 }, // ### don't document for now
  223. { "keyword", CMD_KEYWORD, 0 },
  224. { "l", CMD_L, 0 },
  225. { "legalese", CMD_LEGALESE, 0 },
  226. { "link", CMD_LINK, 0 },
  227. { "list", CMD_LIST, 0 },
  228. { "meta", CMD_META, 0 },
  229. { "newcode", CMD_NEWCODE, 0 },
  230. { "o", CMD_O, 0 },
  231. { "oldcode", CMD_OLDCODE, 0 },
  232. { "omit", CMD_OMIT, 0 },
  233. { "omitvalue", CMD_OMITVALUE, 0 },
  234. { "overload", CMD_OVERLOAD, 0 },
  235. { "part", CMD_PART, 0 },
  236. { "printline", CMD_PRINTLINE, 0 },
  237. { "printto", CMD_PRINTTO, 0 },
  238. { "printuntil", CMD_PRINTUNTIL, 0 },
  239. { "quotation", CMD_QUOTATION, 0 },
  240. { "quotefile", CMD_QUOTEFILE, 0 },
  241. { "quotefromfile", CMD_QUOTEFROMFILE, 0 },
  242. { "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now
  243. { "raw", CMD_RAW, 0 },
  244. { "row", CMD_ROW, 0 },
  245. { "sa", CMD_SA, 0 },
  246. { "section1", CMD_SECTION1, 0 },
  247. { "section2", CMD_SECTION2, 0 },
  248. { "section3", CMD_SECTION3, 0 },
  249. { "section4", CMD_SECTION4, 0 },
  250. { "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now
  251. { "sincelist", CMD_SINCELIST, 0 },
  252. { "skipline", CMD_SKIPLINE, 0 },
  253. { "skipto", CMD_SKIPTO, 0 },
  254. { "skipuntil", CMD_SKIPUNTIL, 0 },
  255. { "snippet", CMD_SNIPPET, 0 },
  256. { "span", CMD_SPAN, 0 },
  257. { "sub", CMD_SUB, 0 },
  258. { "sup", CMD_SUP, 0 },
  259. { "table", CMD_TABLE, 0 },
  260. { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
  261. { "target", CMD_TARGET, 0 },
  262. { "tt", CMD_TT, 0 },
  263. { "underline", CMD_UNDERLINE, 0 },
  264. { "unicode", CMD_UNICODE, 0 },
  265. { "value", CMD_VALUE, 0 },
  266. { "warning", CMD_WARNING, 0 },
  267. { "qml", CMD_QML, 0 },
  268. { "endqml", CMD_ENDQML, 0 },
  269. { "cpp", CMD_CPP, 0 },
  270. { "endcpp", CMD_ENDCPP, 0 },
  271. { "qmltext", CMD_QMLTEXT, 0 },
  272. { "endqmltext", CMD_ENDQMLTEXT, 0 },
  273. { "cpptext", CMD_CPPTEXT, 0 },
  274. { "endcpptext", CMD_ENDCPPTEXT, 0 },
  275. { "js", CMD_JS, 0 },
  276. { "endjs", CMD_ENDJS, 0 },
  277. { 0, 0, 0 }
  278. };
  279. typedef QHash<QString, int> QHash_QString_int;
  280. typedef QHash<QString, Macro> QHash_QString_Macro;
  281. Q_GLOBAL_STATIC(QStringMap, aliasMap)
  282. Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
  283. Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
  284. class DocPrivateExtra
  285. {
  286. public:
  287. QString baseName;
  288. Doc::Sections granularity;
  289. Doc::Sections section; // ###
  290. QList<Atom*> tableOfContents;
  291. QList<int> tableOfContentsLevels;
  292. QList<Atom*> keywords;
  293. QList<Atom*> targets;
  294. QStringMultiMap metaMap;
  295. DocPrivateExtra()
  296. : granularity(Doc::Part) { }
  297. };
  298. struct Shared // ### get rid of
  299. {
  300. Shared()
  301. : count(1) { }
  302. void ref() { ++count; }
  303. bool deref() { return (--count == 0); }
  304. int count;
  305. };
  306. static QString cleanLink(const QString &link)
  307. {
  308. int colonPos = link.indexOf(':');
  309. if ((colonPos == -1) ||
  310. (!link.startsWith("file:") && !link.startsWith("mailto:")))
  311. return link;
  312. return link.mid(colonPos + 1).simplified();
  313. }
  314. class DocPrivate : public Shared
  315. {
  316. public:
  317. DocPrivate(const Location& start = Location::null,
  318. const Location& end = Location::null,
  319. const QString& source = "");
  320. ~DocPrivate();
  321. void addAlso(const Text& also);
  322. void constructExtra();
  323. bool isEnumDocSimplifiable() const;
  324. // ### move some of this in DocPrivateExtra
  325. Location start_loc;
  326. Location end_loc;
  327. QString src;
  328. Text text;
  329. QSet<QString> params;
  330. QList<Text> alsoList;
  331. QStringList enumItemList;
  332. QStringList omitEnumItemList;
  333. QSet<QString> metacommandsUsed;
  334. QCommandMap metaCommandMap;
  335. bool hasLegalese : 1;
  336. bool hasSectioningUnits : 1;
  337. DocPrivateExtra *extra;
  338. };
  339. DocPrivate::DocPrivate(const Location& start,
  340. const Location& end,
  341. const QString& source)
  342. : start_loc(start),
  343. end_loc(end),
  344. src(source),
  345. hasLegalese(false),
  346. hasSectioningUnits(false),
  347. extra(0)
  348. {
  349. // nothing.
  350. }
  351. DocPrivate::~DocPrivate()
  352. {
  353. delete extra;
  354. }
  355. void DocPrivate::addAlso(const Text& also)
  356. {
  357. alsoList.append(also);
  358. }
  359. void DocPrivate::constructExtra()
  360. {
  361. if (extra == 0)
  362. extra = new DocPrivateExtra;
  363. }
  364. bool DocPrivate::isEnumDocSimplifiable() const
  365. {
  366. bool justMetColon = false;
  367. int numValueTables = 0;
  368. const Atom *atom = text.firstAtom();
  369. while (atom) {
  370. if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
  371. justMetColon = atom->string().endsWith(":");
  372. }
  373. else if ((atom->type() == Atom::ListLeft) &&
  374. (atom->string() == ATOM_LIST_VALUE)) {
  375. if (justMetColon || numValueTables > 0)
  376. return false;
  377. ++numValueTables;
  378. }
  379. atom = atom->next();
  380. }
  381. return true;
  382. }
  383. class DocParser
  384. {
  385. public:
  386. void parse(const QString &source,
  387. DocPrivate *docPrivate,
  388. const QSet<QString> &metaCommandSet);
  389. static int endCmdFor(int cmd);
  390. static QString cmdName(int cmd);
  391. static QString endCmdName(int cmd);
  392. static QString untabifyEtc(const QString& str);
  393. static int indentLevel(const QString& str);
  394. static QString unindent(int level, const QString& str);
  395. static QString slashed(const QString& str);
  396. static int tabSize;
  397. static QStringList exampleFiles;
  398. static QStringList exampleDirs;
  399. static QStringList sourceFiles;
  400. static QStringList sourceDirs;
  401. static bool quoting;
  402. private:
  403. Location& location();
  404. QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
  405. const QString& str);
  406. void checkExpiry(const QString& date);
  407. void insertBaseName(const QString &baseName);
  408. void insertTarget(const QString& target, bool keyword);
  409. void include(const QString& fileName, const QString& identifier);
  410. void startFormat(const QString& format, int cmd);
  411. bool openCommand(int cmd);
  412. bool closeCommand(int endCmd);
  413. void startSection(Doc::Sections unit, int cmd);
  414. void endSection(int unit, int endCmd);
  415. void parseAlso();
  416. void append(Atom::Type type, const QString& string = "");
  417. void append(Atom::Type type, const QString& p1, const QString& p2);
  418. void appendChar(QChar ch);
  419. void appendWord(const QString &word);
  420. void appendToCode(const QString &code);
  421. void appendToCode(const QString &code, Atom::Type defaultType);
  422. void startNewPara();
  423. void enterPara(Atom::Type leftType = Atom::ParaLeft,
  424. Atom::Type rightType = Atom::ParaRight,
  425. const QString& string = "");
  426. void leavePara();
  427. void leaveValue();
  428. void leaveValueList();
  429. void leaveTableRow();
  430. CodeMarker *quoteFromFile();
  431. void expandMacro(const QString& name, const QString& def, int numParams);
  432. QString expandMacroToString(const QString &name, const QString &def, int numParams);
  433. Doc::Sections getSectioningUnit();
  434. QString getArgument(bool verbatim = false);
  435. QString getOptionalArgument();
  436. QString getRestOfLine();
  437. QString getMetaCommandArgument(const QString &cmdStr);
  438. QString getUntilEnd(int cmd);
  439. QString getCode(int cmd, CodeMarker *marker);
  440. QString getUnmarkedCode(int cmd);
  441. bool isBlankLine();
  442. bool isLeftBraceAhead();
  443. void skipSpacesOnLine();
  444. void skipSpacesOrOneEndl();
  445. void skipAllSpaces();
  446. void skipToNextPreprocessorCommand();
  447. QStack<int> openedInputs;
  448. QString in;
  449. int pos;
  450. int len;
  451. Location cachedLoc;
  452. int cachedPos;
  453. DocPrivate* priv;
  454. enum ParagraphState {
  455. OutsideParagraph,
  456. InSingleLineParagraph,
  457. InMultiLineParagraph
  458. };
  459. ParagraphState paraState;
  460. bool inTableHeader;
  461. bool inTableRow;
  462. bool inTableItem;
  463. bool indexStartedPara; // ### rename
  464. Atom::Type pendingParaLeftType;
  465. Atom::Type pendingParaRightType;
  466. QString pendingParaString;
  467. int braceDepth;
  468. int minIndent;
  469. Doc::Sections currentSection;
  470. QMap<QString, Location> targetMap;
  471. QMap<int, QString> pendingFormats;
  472. QStack<int> openedCommands;
  473. QStack<OpenedList> openedLists;
  474. Quoter quoter;
  475. };
  476. int DocParser::tabSize;
  477. QStringList DocParser::exampleFiles;
  478. QStringList DocParser::exampleDirs;
  479. QStringList DocParser::sourceFiles;
  480. QStringList DocParser::sourceDirs;
  481. bool DocParser::quoting;
  482. /*!
  483. Parse the \a source string to build a Text data structure
  484. in \a docPrivate. The Text data structure is a linked list
  485. of Atoms.
  486. \a metaCommandSet is the set of metacommands that may be
  487. found in \a source. These metacommands are not markup text
  488. commands. They are topic commands and related metacommands.
  489. */
  490. void DocParser::parse(const QString& source,
  491. DocPrivate *docPrivate,
  492. const QSet<QString>& metaCommandSet)
  493. {
  494. in = source;
  495. pos = 0;
  496. len = in.length();
  497. cachedLoc = docPrivate->start_loc;
  498. cachedPos = 0;
  499. priv = docPrivate;
  500. priv->text << Atom::Nop;
  501. paraState = OutsideParagraph;
  502. inTableHeader = false;
  503. inTableRow = false;
  504. inTableItem = false;
  505. indexStartedPara = false;
  506. pendingParaLeftType = Atom::Nop;
  507. pendingParaRightType = Atom::Nop;
  508. braceDepth = 0;
  509. minIndent = INT_MAX;
  510. currentSection = Doc::NoSection;
  511. openedCommands.push(CMD_OMIT);
  512. quoter.reset();
  513. CodeMarker *marker = 0;
  514. Atom *currentLinkAtom = 0;
  515. QString p1, p2;
  516. QStack<bool> preprocessorSkipping;
  517. int numPreprocessorSkipping = 0;
  518. while (pos < len) {
  519. QChar ch = in.at(pos);
  520. switch (ch.unicode()) {
  521. case '\\':
  522. {
  523. QString cmdStr;
  524. pos++;
  525. while (pos < len) {
  526. ch = in.at(pos);
  527. if (ch.isLetterOrNumber()) {
  528. cmdStr += ch;
  529. pos++;
  530. }
  531. else {
  532. break;
  533. }
  534. }
  535. if (cmdStr.isEmpty()) {
  536. if (pos < len) {
  537. enterPara();
  538. if (in.at(pos).isSpace()) {
  539. skipAllSpaces();
  540. appendChar(QLatin1Char(' '));
  541. }
  542. else {
  543. appendChar(in.at(pos++));
  544. }
  545. }
  546. }
  547. else {
  548. int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
  549. switch (cmd) {
  550. case CMD_A:
  551. enterPara();
  552. p1 = getArgument();
  553. append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
  554. append(Atom::String, p1);
  555. append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
  556. priv->params.insert(p1);
  557. break;
  558. case CMD_ABSTRACT:
  559. if (openCommand(cmd)) {
  560. leavePara();
  561. append(Atom::AbstractLeft);
  562. }
  563. break;
  564. case CMD_BADCODE:
  565. leavePara();
  566. append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
  567. break;
  568. case CMD_BASENAME:
  569. leavePara();
  570. insertBaseName(getArgument());
  571. break;
  572. case CMD_BOLD:
  573. startFormat(ATOM_FORMATTING_BOLD, cmd);
  574. break;
  575. case CMD_BRIEF:
  576. leavePara();
  577. enterPara(Atom::BriefLeft, Atom::BriefRight);
  578. break;
  579. case CMD_C:
  580. enterPara();
  581. p1 = untabifyEtc(getArgument(true));
  582. marker = CodeMarker::markerForCode(p1);
  583. append(Atom::C, marker->markedUpCode(p1, 0, location()));
  584. break;
  585. case CMD_CAPTION:
  586. leavePara();
  587. enterPara(Atom::CaptionLeft, Atom::CaptionRight);
  588. break;
  589. case CMD_CHAPTER:
  590. startSection(Doc::Chapter, cmd);
  591. break;
  592. case CMD_CODE:
  593. leavePara();
  594. append(Atom::Code, getCode(CMD_CODE, 0));
  595. break;
  596. case CMD_QML:
  597. leavePara();
  598. append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
  599. break;
  600. case CMD_QMLTEXT:
  601. append(Atom::QmlText);
  602. break;
  603. case CMD_JS:
  604. leavePara();
  605. append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
  606. break;
  607. case CMD_DIV:
  608. leavePara();
  609. p1 = getArgument(true);
  610. append(Atom::DivLeft, p1);
  611. openedCommands.push(cmd);
  612. break;
  613. case CMD_ENDDIV:
  614. leavePara();
  615. append(Atom::DivRight);
  616. closeCommand(cmd);
  617. break;
  618. case CMD_CODELINE:
  619. {
  620. if (!quoting) {
  621. if (priv->text.lastAtom()->type() == Atom::Code
  622. && priv->text.lastAtom()->string().endsWith("\n\n"))
  623. priv->text.lastAtom()->chopString();
  624. appendToCode("\n");
  625. }
  626. else {
  627. append(Atom::CodeQuoteCommand, cmdStr);
  628. append(Atom::CodeQuoteArgument, " ");
  629. }
  630. }
  631. break;
  632. case CMD_DOTS:
  633. {
  634. if (!quoting) {
  635. if (priv->text.lastAtom()->type() == Atom::Code
  636. && priv->text.lastAtom()->string().endsWith("\n\n"))
  637. priv->text.lastAtom()->chopString();
  638. QString arg = getOptionalArgument();
  639. int indent = 4;
  640. if (!arg.isEmpty())
  641. indent = arg.toInt();
  642. for (int i = 0; i < indent; ++i)
  643. appendToCode(" ");
  644. appendToCode("...\n");
  645. }
  646. else {
  647. append(Atom::CodeQuoteCommand, cmdStr);
  648. QString arg = getOptionalArgument();
  649. if (arg.isEmpty())
  650. arg = "4";
  651. append(Atom::CodeQuoteArgument, arg);
  652. }
  653. }
  654. break;
  655. case CMD_ELSE:
  656. if (preprocessorSkipping.size() > 0) {
  657. if (preprocessorSkipping.top()) {
  658. --numPreprocessorSkipping;
  659. }
  660. else {
  661. ++numPreprocessorSkipping;
  662. }
  663. preprocessorSkipping.top() = !preprocessorSkipping.top();
  664. (void)getRestOfLine(); // ### should ensure that it's empty
  665. if (numPreprocessorSkipping)
  666. skipToNextPreprocessorCommand();
  667. }
  668. else {
  669. location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
  670. }
  671. break;
  672. case CMD_ENDABSTRACT:
  673. if (closeCommand(cmd)) {
  674. leavePara();
  675. append(Atom::AbstractRight);
  676. }
  677. break;
  678. case CMD_ENDCHAPTER:
  679. endSection(Doc::Chapter, cmd);
  680. break;
  681. case CMD_ENDCODE:
  682. closeCommand(cmd);
  683. break;
  684. case CMD_ENDQML:
  685. closeCommand(cmd);
  686. break;
  687. case CMD_ENDQMLTEXT:
  688. append(Atom::EndQmlText);
  689. break;
  690. case CMD_ENDJS:
  691. closeCommand(cmd);
  692. break;
  693. case CMD_ENDFOOTNOTE:
  694. if (closeCommand(cmd)) {
  695. leavePara();
  696. append(Atom::FootnoteRight);
  697. paraState = InMultiLineParagraph; // ###
  698. }
  699. break;
  700. case CMD_ENDIF:
  701. if (preprocessorSkipping.count() > 0) {
  702. if (preprocessorSkipping.pop())
  703. --numPreprocessorSkipping;
  704. (void)getRestOfLine(); // ### should ensure that it's empty
  705. if (numPreprocessorSkipping)
  706. skipToNextPreprocessorCommand();
  707. }
  708. else {
  709. location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
  710. }
  711. break;
  712. case CMD_ENDLEGALESE:
  713. if (closeCommand(cmd)) {
  714. leavePara();
  715. append(Atom::LegaleseRight);
  716. }
  717. break;
  718. case CMD_ENDLINK:
  719. if (closeCommand(cmd)) {
  720. if (priv->text.lastAtom()->type() == Atom::String
  721. && priv->text.lastAtom()->string().endsWith(" "))
  722. priv->text.lastAtom()->chopString();
  723. append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
  724. }
  725. break;
  726. case CMD_ENDLIST:
  727. if (closeCommand(cmd)) {
  728. leavePara();
  729. if (openedLists.top().isStarted()) {
  730. append(Atom::ListItemRight,
  731. openedLists.top().styleString());
  732. append(Atom::ListRight,
  733. openedLists.top().styleString());
  734. }
  735. openedLists.pop();
  736. }
  737. break;
  738. case CMD_ENDOMIT:
  739. closeCommand(cmd);
  740. break;
  741. case CMD_ENDPART:
  742. endSection(Doc::Part, cmd);
  743. break;
  744. case CMD_ENDQUOTATION:
  745. if (closeCommand(cmd)) {
  746. leavePara();
  747. append(Atom::QuotationRight);
  748. }
  749. break;
  750. case CMD_ENDRAW:
  751. location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
  752. break;
  753. case CMD_ENDSECTION1:
  754. endSection(Doc::Section1, cmd);
  755. break;
  756. case CMD_ENDSECTION2:
  757. endSection(Doc::Section2, cmd);
  758. break;
  759. case CMD_ENDSECTION3:
  760. endSection(Doc::Section3, cmd);
  761. break;
  762. case CMD_ENDSECTION4:
  763. endSection(Doc::Section4, cmd);
  764. break;
  765. case CMD_ENDSIDEBAR:
  766. if (closeCommand(cmd)) {
  767. leavePara();
  768. append(Atom::SidebarRight);
  769. }
  770. break;
  771. case CMD_ENDTABLE:
  772. if (closeCommand(cmd)) {
  773. leaveTableRow();
  774. append(Atom::TableRight);
  775. }
  776. break;
  777. case CMD_EXPIRE:
  778. checkExpiry(getArgument());
  779. break;
  780. case CMD_FOOTNOTE:
  781. if (openCommand(cmd)) {
  782. enterPara();
  783. append(Atom::FootnoteLeft);
  784. paraState = OutsideParagraph; // ###
  785. }
  786. break;
  787. case CMD_ANNOTATEDLIST:
  788. append(Atom::AnnotatedList, getArgument());
  789. break;
  790. case CMD_SINCELIST:
  791. append(Atom::SinceList, getRestOfLine().simplified());
  792. break;
  793. case CMD_GENERATELIST:
  794. append(Atom::GeneratedList, getArgument());
  795. break;
  796. case CMD_GRANULARITY:
  797. priv->constructExtra();
  798. priv->extra->granularity = getSectioningUnit();
  799. break;
  800. case CMD_HEADER:
  801. if (openedCommands.top() == CMD_TABLE) {
  802. leaveTableRow();
  803. append(Atom::TableHeaderLeft);
  804. inTableHeader = true;
  805. }
  806. else {
  807. if (openedCommands.contains(CMD_TABLE)) {
  808. location().warning(tr("Cannot use '\\%1' within '\\%2'")
  809. .arg(cmdName(CMD_HEADER))
  810. .arg(cmdName(openedCommands.top())));
  811. }
  812. else {
  813. location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
  814. .arg(cmdName(CMD_HEADER))
  815. .arg(cmdName(CMD_TABLE)));
  816. }
  817. }
  818. break;
  819. case CMD_I:
  820. startFormat(ATOM_FORMATTING_ITALIC, cmd);
  821. break;
  822. case CMD_IF:
  823. preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
  824. if (preprocessorSkipping.top())
  825. ++numPreprocessorSkipping;
  826. if (numPreprocessorSkipping)
  827. skipToNextPreprocessorCommand();
  828. break;
  829. case CMD_IMAGE:
  830. leaveValueList();
  831. append(Atom::Image, getArgument());
  832. append(Atom::ImageText, getRestOfLine());
  833. break;
  834. case CMD_INCLUDE:
  835. {
  836. QString fileName = getArgument();
  837. QString identifier = getRestOfLine();
  838. include(fileName, identifier);
  839. }
  840. break;
  841. case CMD_INLINEIMAGE:
  842. enterPara();
  843. append(Atom::InlineImage, getArgument());
  844. append(Atom::ImageText, getRestOfLine());
  845. append(Atom::String, " ");
  846. break;
  847. case CMD_INDEX:
  848. if (paraState == OutsideParagraph) {
  849. enterPara();
  850. indexStartedPara = true;
  851. }
  852. else {
  853. const Atom *last = priv->text.lastAtom();
  854. if (indexStartedPara &&
  855. (last->type() != Atom::FormattingRight ||
  856. last->string() != ATOM_FORMATTING_INDEX))
  857. indexStartedPara = false;
  858. }
  859. startFormat(ATOM_FORMATTING_INDEX, cmd);
  860. break;
  861. case CMD_KEYWORD:
  862. insertTarget(getRestOfLine(),true);
  863. break;
  864. case CMD_L:
  865. enterPara();
  866. if (isLeftBraceAhead()) {
  867. p1 = getArgument();
  868. append(Atom::Link, p1);
  869. if (isLeftBraceAhead()) {
  870. currentLinkAtom = priv->text.lastAtom();
  871. startFormat(ATOM_FORMATTING_LINK, cmd);
  872. }
  873. else {
  874. append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
  875. append(Atom::String, cleanLink(p1));
  876. append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
  877. }
  878. }
  879. else {
  880. p1 = getArgument();
  881. append(Atom::Link, p1);
  882. append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
  883. append(Atom::String, cleanLink(p1));
  884. append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
  885. }
  886. break;
  887. case CMD_LEGALESE:
  888. leavePara();
  889. if (openCommand(cmd))
  890. append(Atom::LegaleseLeft);
  891. docPrivate->hasLegalese = true;
  892. break;
  893. case CMD_LINK:
  894. if (openCommand(cmd)) {
  895. enterPara();
  896. p1 = getArgument();
  897. append(Atom::Link, p1);
  898. append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
  899. skipSpacesOrOneEndl();
  900. }
  901. break;
  902. case CMD_LIST:
  903. if (openCommand(cmd)) {
  904. leavePara();
  905. openedLists.push(OpenedList(location(),
  906. getOptionalArgument()));
  907. }
  908. break;
  909. case CMD_META:
  910. priv->constructExtra();
  911. p1 = getArgument();
  912. priv->extra->metaMap.insert(p1, getArgument());
  913. break;
  914. case CMD_NEWCODE:
  915. location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
  916. break;
  917. case CMD_O:
  918. leavePara();
  919. if (openedCommands.top() == CMD_LIST) {
  920. if (openedLists.top().isStarted()) {
  921. append(Atom::ListItemRight,
  922. openedLists.top().styleString());
  923. }
  924. else {
  925. append(Atom::ListLeft,
  926. openedLists.top().styleString());
  927. }
  928. openedLists.top().next();
  929. append(Atom::ListItemNumber,
  930. openedLists.top().numberString());
  931. append(Atom::ListItemLeft,
  932. openedLists.top().styleString());
  933. enterPara();
  934. }
  935. else if (openedCommands.top() == CMD_TABLE) {
  936. p1 = "1,1";
  937. if (isLeftBraceAhead()) {
  938. p1 = getArgument();
  939. if (isLeftBraceAhead()) {
  940. p2 = getArgument();
  941. }
  942. }
  943. if (!inTableHeader && !inTableRow) {
  944. location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
  945. .arg(cmdName(CMD_HEADER))
  946. .arg(cmdName(CMD_ROW))
  947. .arg(cmdName(CMD_O)));
  948. append(Atom::TableRowLeft);
  949. inTableRow = true;
  950. }
  951. else if (inTableItem) {
  952. append(Atom::TableItemRight);
  953. inTableItem = false;
  954. }
  955. append(Atom::TableItemLeft, p1, p2);
  956. inTableItem = true;
  957. }
  958. else {
  959. location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
  960. .arg(cmdName(cmd))
  961. .arg(cmdName(CMD_LIST))
  962. .arg(cmdName(CMD_TABLE)));
  963. }
  964. break;
  965. case CMD_OLDCODE:
  966. leavePara();
  967. append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
  968. append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
  969. break;
  970. case CMD_OMIT:
  971. getUntilEnd(cmd);
  972. break;
  973. case CMD_OMITVALUE:
  974. p1 = getArgument();
  975. if (!priv->enumItemList.contains(p1))
  976. priv->enumItemList.append(p1);
  977. if (!priv->omitEnumItemList.contains(p1))
  978. priv->omitEnumItemList.append(p1);
  979. break;
  980. case CMD_PART:
  981. startSection(Doc::Part, cmd);
  982. break;
  983. case CMD_PRINTLINE:
  984. leavePara();
  985. if (!quoting)
  986. appendToCode(quoter.quoteLine(location(), cmdStr,
  987. getRestOfLine()));
  988. else {
  989. append(Atom::CodeQuoteCommand, cmdStr);
  990. append(Atom::CodeQuoteArgument, getRestOfLine());
  991. }
  992. break;
  993. case CMD_PRINTTO:
  994. leavePara();
  995. if (!quoting)
  996. appendToCode(quoter.quoteTo(location(), cmdStr,
  997. getRestOfLine()));
  998. else {
  999. append(Atom::CodeQuoteCommand, cmdStr);
  1000. append(Atom::CodeQuoteArgument, getRestOfLine());
  1001. }
  1002. break;
  1003. case CMD_PRINTUNTIL:
  1004. leavePara();
  1005. if (!quoting)
  1006. appendToCode(quoter.quoteUntil(location(), cmdStr,
  1007. getRestOfLine()));
  1008. else {
  1009. append(Atom::CodeQuoteCommand, cmdStr);
  1010. append(Atom::CodeQuoteArgument, getRestOfLine());
  1011. }
  1012. break;
  1013. case CMD_QUOTATION:
  1014. if (openCommand(cmd)) {
  1015. leavePara();
  1016. append(Atom::QuotationLeft);
  1017. }
  1018. break;
  1019. case CMD_QUOTEFILE:
  1020. {
  1021. leavePara();
  1022. QString fileName = getArgument();
  1023. Doc::quoteFromFile(location(), quoter, fileName);
  1024. if (!quoting) {
  1025. append(Atom::Code,
  1026. quoter.quoteTo(location(), cmdStr, ""));
  1027. quoter.reset();
  1028. }
  1029. else {
  1030. append(Atom::CodeQuoteCommand, cmdStr);
  1031. append(Atom::CodeQuoteArgument, fileName);
  1032. }
  1033. break;
  1034. }
  1035. case CMD_QUOTEFROMFILE:
  1036. leavePara();
  1037. if (!quoting)
  1038. quoteFromFile();
  1039. else {
  1040. append(Atom::CodeQuoteCommand, cmdStr);
  1041. append(Atom::CodeQuoteArgument, getArgument());
  1042. }
  1043. break;
  1044. case CMD_QUOTEFUNCTION:
  1045. leavePara();
  1046. marker = quoteFromFile();
  1047. p1 = getRestOfLine();
  1048. if (!quoting) {
  1049. quoter.quoteTo(location(), cmdStr,
  1050. slashed(marker->functionBeginRegExp(p1)));
  1051. append(Atom::Code,
  1052. quoter.quoteUntil(location(), cmdStr,
  1053. slashed(marker->functionEndRegExp(p1))));
  1054. quoter.reset();
  1055. }
  1056. else {
  1057. append(Atom::CodeQuoteCommand, cmdStr);
  1058. append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
  1059. }
  1060. break;
  1061. case CMD_RAW:
  1062. leavePara();
  1063. p1 = getRestOfLine();
  1064. if (p1.isEmpty())
  1065. location().warning(tr("Missing format name after '\\%1")
  1066. .arg(cmdName(CMD_RAW)));
  1067. append(Atom::FormatIf, p1);
  1068. append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
  1069. append(Atom::FormatElse);
  1070. append(Atom::FormatEndif);
  1071. break;
  1072. case CMD_ROW:
  1073. if (openedCommands.top() == CMD_TABLE) {
  1074. p1.clear();
  1075. if (isLeftBraceAhead())
  1076. p1 = getArgument(true);
  1077. leaveTableRow();
  1078. append(Atom::TableRowLeft,p1);
  1079. inTableRow = true;
  1080. }
  1081. else {
  1082. if (openedCommands.contains(CMD_TABLE)) {
  1083. location().warning(tr("Cannot use '\\%1' within '\\%2'")
  1084. .arg(cmdName(CMD_ROW))
  1085. .arg(cmdName(openedCommands.top())));
  1086. }
  1087. else {
  1088. location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
  1089. .arg(cmdName(CMD_ROW))
  1090. .arg(cmdName(CMD_TABLE)));
  1091. }
  1092. }
  1093. break;
  1094. case CMD_SA:
  1095. parseAlso();
  1096. break;
  1097. case CMD_SECTION1:
  1098. startSection(Doc::Section1, cmd);
  1099. break;
  1100. case CMD_SECTION2:
  1101. startSection(Doc::Section2, cmd);
  1102. break;
  1103. case CMD_SECTION3:
  1104. startSection(Doc::Section3, cmd);
  1105. break;
  1106. case CMD_SECTION4:
  1107. startSection(Doc::Section4, cmd);
  1108. break;
  1109. case CMD_SIDEBAR:
  1110. if (openCommand(cmd)) {
  1111. leavePara();
  1112. append(Atom::SidebarLeft);
  1113. }
  1114. break;
  1115. case CMD_SKIPLINE:
  1116. leavePara();
  1117. if (!quoting)
  1118. quoter.quoteLine(location(),
  1119. cmdStr,
  1120. getRestOfLine());
  1121. else {
  1122. append(Atom::CodeQuoteCommand, cmdStr);
  1123. append(Atom::CodeQuoteArgument, getRestOfLine());
  1124. }
  1125. break;
  1126. case CMD_SKIPTO:
  1127. leavePara();
  1128. if (!quoting)
  1129. quoter.quoteTo(location(),
  1130. cmdStr,
  1131. getRestOfLine());
  1132. else {
  1133. append(Atom::CodeQuoteCommand, cmdStr);
  1134. append(Atom::CodeQuoteArgument, getRestOfLine());
  1135. }
  1136. break;
  1137. case CMD_SKIPUNTIL:
  1138. leavePara();
  1139. if (!quoting)
  1140. quoter.quoteUntil(location(),
  1141. cmdStr,
  1142. getRestOfLine());
  1143. else {
  1144. append(Atom::CodeQuoteCommand, cmdStr);
  1145. append(Atom::CodeQuoteArgument, getRestOfLine());
  1146. }
  1147. break;
  1148. case CMD_SPAN:
  1149. p1 = ATOM_FORMATTING_SPAN + getArgument(true);
  1150. startFormat(p1, cmd);
  1151. break;
  1152. case CMD_SNIPPET:
  1153. leavePara();
  1154. {
  1155. QString snippet = getArgument();
  1156. QString identifier = getRestOfLine();
  1157. if (quoting) {
  1158. append(Atom::SnippetCommand, cmdStr);
  1159. append(Atom::SnippetLocation, snippet);
  1160. append(Atom::SnippetIdentifier, identifier);
  1161. }
  1162. else {
  1163. marker = Doc::quoteFromFile(location(),quoter,snippet);
  1164. appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
  1165. }
  1166. }
  1167. break;
  1168. case CMD_SUB:
  1169. startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
  1170. break;
  1171. case CMD_SUP:
  1172. startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
  1173. break;
  1174. case CMD_TABLE:
  1175. p1 = getRestOfLine();
  1176. if (openCommand(cmd)) {
  1177. leavePara();
  1178. append(Atom::TableLeft, p1);
  1179. inTableHeader = false;
  1180. inTableRow = false;
  1181. inTableItem = false;
  1182. }
  1183. break;
  1184. case CMD_TABLEOFCONTENTS:
  1185. p1 = "1";
  1186. if (isLeftBraceAhead())
  1187. p1 = getArgument();
  1188. p1 += ",";
  1189. p1 += QString::number((int)getSectioningUnit());
  1190. append(Atom::TableOfContents, p1);
  1191. break;
  1192. case CMD_TARGET:
  1193. insertTarget(getRestOfLine(),false);
  1194. break;
  1195. case CMD_TT:
  1196. startFormat(ATOM_FORMATTING_TELETYPE, cmd);
  1197. break;
  1198. case CMD_UNDERLINE:
  1199. startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
  1200. break;
  1201. case CMD_UNICODE:
  1202. enterPara();
  1203. p1 = getArgument();
  1204. {
  1205. bool ok;
  1206. uint unicodeChar = p1.toUInt(&ok, 0);
  1207. if (!ok ||
  1208. (unicodeChar == 0x0000) ||
  1209. (unicodeChar > 0xFFFE)) {
  1210. location().warning(tr("Invalid Unicode character '%1' specified "
  1211. "with '%2'")
  1212. .arg(p1, cmdName(CMD_UNICODE)));
  1213. }
  1214. else {
  1215. append(Atom::String, QChar(unicodeChar));
  1216. }
  1217. }
  1218. break;
  1219. case CMD_VALUE:
  1220. leaveValue();
  1221. if (openedLists.top().style() == OpenedList::Value) {
  1222. p1 = getArgument();
  1223. if (!priv->enumItemList.contains(p1))
  1224. priv->enumItemList.append(p1);
  1225. openedLists.top().next();
  1226. append(Atom::ListTagLeft, ATOM_LIST_VALUE);
  1227. append(Atom::String, p1);
  1228. append(Atom::ListTagRight, ATOM_LIST_VALUE);
  1229. append(Atom::ListItemLeft, ATOM_LIST_VALUE);
  1230. skipSpacesOrOneEndl();
  1231. if (isBlankLine())
  1232. append(Atom::Nop);
  1233. }
  1234. else {
  1235. // ### problems
  1236. }
  1237. break;
  1238. case CMD_WARNING:
  1239. leavePara();
  1240. enterPara();
  1241. append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
  1242. app