Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "config.h"
43#include "doc.h"
44#include "codemarker.h"
45#include "editdistance.h"
46#include "openedlist.h"
47#include "quoter.h"
48#include "text.h"
49#include "tokenizer.h"
50#include <qdatetime.h>
51#include <qfile.h>
52#include <qfileinfo.h>
53#include <qhash.h>
54#include <qtextstream.h>
55#include <qregexp.h>
56#include <ctype.h>
57#include <limits.h>
58#include <qdebug.h>
59
60QT_BEGIN_NAMESPACE
61
62Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
63Q_GLOBAL_STATIC(QStringList, null_QStringList)
64Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
65//Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
66Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap)
67
68struct Macro
69{
70 QString defaultDef;
71 Location defaultDefLocation;
72 QStringMap otherDefs;
73 int numParams;
74};
75
76enum {
77 CMD_A,
78 CMD_ABSTRACT,
79 CMD_ANNOTATEDLIST,
80 CMD_BADCODE,
81 CMD_BASENAME,
82 CMD_BOLD,
83 CMD_BRIEF,
84 CMD_C,
85 CMD_CAPTION,
86 CMD_CHAPTER, // 9
87 CMD_CODE,
88 CMD_CODELINE,
89 CMD_DIV,
90 CMD_DOTS,
91 CMD_ELSE,
92 CMD_ENDABSTRACT,
93 CMD_ENDCHAPTER,
94 CMD_ENDCODE,
95 CMD_ENDDIV,
96 CMD_ENDFOOTNOTE,
97 CMD_ENDIF,
98 CMD_ENDLEGALESE,
99 CMD_ENDLINK,
100 CMD_ENDLIST,
101 CMD_ENDOMIT,
102 CMD_ENDPART,
103 CMD_ENDQUOTATION,
104 CMD_ENDRAW,
105 CMD_ENDSECTION1,
106 CMD_ENDSECTION2,
107 CMD_ENDSECTION3,
108 CMD_ENDSECTION4,
109 CMD_ENDSIDEBAR,
110 CMD_ENDTABLE,
111 CMD_EXPIRE,
112 CMD_FOOTNOTE,
113 CMD_GENERATELIST,
114 CMD_GRANULARITY,
115 CMD_HEADER,
116 CMD_I,
117 CMD_IF,
118 CMD_IMAGE,
119 CMD_INCLUDE,
120 CMD_INLINEIMAGE,
121 CMD_INDEX,
122 CMD_KEYWORD,
123 CMD_L,
124 CMD_LEGALESE,
125 CMD_LINK,
126 CMD_LIST,
127 CMD_META,
128 CMD_NEWCODE,
129 CMD_O,
130 CMD_OLDCODE,
131 CMD_OMIT,
132 CMD_OMITVALUE,
133 CMD_OVERLOAD,
134 CMD_PART,
135 CMD_PRINTLINE,
136 CMD_PRINTTO,
137 CMD_PRINTUNTIL,
138 CMD_QUOTATION,
139 CMD_QUOTEFILE,
140 CMD_QUOTEFROMFILE,
141 CMD_QUOTEFUNCTION,
142 CMD_RAW,
143 CMD_ROW,
144 CMD_SA,
145 CMD_SECTION1, // 68
146 CMD_SECTION2, // 69
147 CMD_SECTION3, // 70
148 CMD_SECTION4, // 71
149 CMD_SIDEBAR,
150 CMD_SINCELIST,
151 CMD_SKIPLINE,
152 CMD_SKIPTO,
153 CMD_SKIPUNTIL,
154 CMD_SNIPPET,
155 CMD_SPAN,
156 CMD_SUB,
157 CMD_SUP,
158 CMD_TABLE,
159 CMD_TABLEOFCONTENTS,
160 CMD_TARGET,
161 CMD_TT,
162 CMD_UNDERLINE,
163 CMD_UNICODE,
164 CMD_VALUE,
165 CMD_WARNING,
166 CMD_QML,
167 CMD_ENDQML,
168 CMD_CPP,
169 CMD_ENDCPP,
170 CMD_QMLTEXT,
171 CMD_ENDQMLTEXT,
172 CMD_CPPTEXT,
173 CMD_ENDCPPTEXT,
174 CMD_JS,
175 CMD_ENDJS,
176 NOT_A_CMD
177};
178
179static struct {
180 const char *english;
181 int no;
182 QString *alias;
183} cmds[] = {
184 { "a", CMD_A, 0 },
185 { "abstract", CMD_ABSTRACT, 0 },
186 { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
187 { "badcode", CMD_BADCODE, 0 },
188 { "basename", CMD_BASENAME, 0 }, // ### don't document for now
189 { "bold", CMD_BOLD, 0 },
190 { "brief", CMD_BRIEF, 0 },
191 { "c", CMD_C, 0 },
192 { "caption", CMD_CAPTION, 0 },
193 { "chapter", CMD_CHAPTER, 0 },
194 { "code", CMD_CODE, 0 },
195 { "codeline", CMD_CODELINE, 0},
196 { "div", CMD_DIV, 0 },
197 { "dots", CMD_DOTS, 0 },
198 { "else", CMD_ELSE, 0 },
199 { "endabstract", CMD_ENDABSTRACT, 0 },
200 { "endchapter", CMD_ENDCHAPTER, 0 },
201 { "endcode", CMD_ENDCODE, 0 },
202 { "enddiv", CMD_ENDDIV, 0 },
203 { "endfootnote", CMD_ENDFOOTNOTE, 0 },
204 { "endif", CMD_ENDIF, 0 },
205 { "endlegalese", CMD_ENDLEGALESE, 0 },
206 { "endlink", CMD_ENDLINK, 0 },
207 { "endlist", CMD_ENDLIST, 0 },
208 { "endomit", CMD_ENDOMIT, 0 },
209 { "endpart", CMD_ENDPART, 0 },
210 { "endquotation", CMD_ENDQUOTATION, 0 },
211 { "endraw", CMD_ENDRAW, 0 },
212 { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
213 { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now
214 { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now
215 { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now
216 { "endsidebar", CMD_ENDSIDEBAR, 0 },
217 { "endtable", CMD_ENDTABLE, 0 },
218 { "expire", CMD_EXPIRE, 0 },
219 { "footnote", CMD_FOOTNOTE, 0 },
220 { "generatelist", CMD_GENERATELIST, 0 },
221 { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
222 { "header", CMD_HEADER, 0 },
223 { "i", CMD_I, 0 },
224 { "if", CMD_IF, 0 },
225 { "image", CMD_IMAGE, 0 },
226 { "include", CMD_INCLUDE, 0 },
227 { "inlineimage", CMD_INLINEIMAGE, 0 },
228 { "index", CMD_INDEX, 0 }, // ### don't document for now
229 { "keyword", CMD_KEYWORD, 0 },
230 { "l", CMD_L, 0 },
231 { "legalese", CMD_LEGALESE, 0 },
232 { "link", CMD_LINK, 0 },
233 { "list", CMD_LIST, 0 },
234 { "meta", CMD_META, 0 },
235 { "newcode", CMD_NEWCODE, 0 },
236 { "o", CMD_O, 0 },
237 { "oldcode", CMD_OLDCODE, 0 },
238 { "omit", CMD_OMIT, 0 },
239 { "omitvalue", CMD_OMITVALUE, 0 },
240 { "overload", CMD_OVERLOAD, 0 },
241 { "part", CMD_PART, 0 },
242 { "printline", CMD_PRINTLINE, 0 },
243 { "printto", CMD_PRINTTO, 0 },
244 { "printuntil", CMD_PRINTUNTIL, 0 },
245 { "quotation", CMD_QUOTATION, 0 },
246 { "quotefile", CMD_QUOTEFILE, 0 },
247 { "quotefromfile", CMD_QUOTEFROMFILE, 0 },
248 { "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now
249 { "raw", CMD_RAW, 0 },
250 { "row", CMD_ROW, 0 },
251 { "sa", CMD_SA, 0 },
252 { "section1", CMD_SECTION1, 0 },
253 { "section2", CMD_SECTION2, 0 },
254 { "section3", CMD_SECTION3, 0 },
255 { "section4", CMD_SECTION4, 0 },
256 { "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now
257 { "sincelist", CMD_SINCELIST, 0 },
258 { "skipline", CMD_SKIPLINE, 0 },
259 { "skipto", CMD_SKIPTO, 0 },
260 { "skipuntil", CMD_SKIPUNTIL, 0 },
261 { "snippet", CMD_SNIPPET, 0 },
262 { "span", CMD_SPAN, 0 },
263 { "sub", CMD_SUB, 0 },
264 { "sup", CMD_SUP, 0 },
265 { "table", CMD_TABLE, 0 },
266 { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
267 { "target", CMD_TARGET, 0 },
268 { "tt", CMD_TT, 0 },
269 { "underline", CMD_UNDERLINE, 0 },
270 { "unicode", CMD_UNICODE, 0 },
271 { "value", CMD_VALUE, 0 },
272 { "warning", CMD_WARNING, 0 },
273 { "qml", CMD_QML, 0 },
274 { "endqml", CMD_ENDQML, 0 },
275 { "cpp", CMD_CPP, 0 },
276 { "endcpp", CMD_ENDCPP, 0 },
277 { "qmltext", CMD_QMLTEXT, 0 },
278 { "endqmltext", CMD_ENDQMLTEXT, 0 },
279 { "cpptext", CMD_CPPTEXT, 0 },
280 { "endcpptext", CMD_ENDCPPTEXT, 0 },
281 { "js", CMD_JS, 0 },
282 { "endjs", CMD_ENDJS, 0 },
283 { 0, 0, 0 }
284};
285
286typedef QHash<QString, int> QHash_QString_int;
287typedef QHash<QString, Macro> QHash_QString_Macro;
288
289Q_GLOBAL_STATIC(QStringMap, aliasMap)
290Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
291Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
292
293class DocPrivateExtra
294{
295 public:
296 QString baseName;
297 Doc::Sections granularity;
298 Doc::Sections section; // ###
299 QList<Atom*> tableOfContents;
300 QList<int> tableOfContentsLevels;
301 QList<Atom*> keywords;
302 QList<Atom*> targets;
303 QStringMultiMap metaMap;
304
305 DocPrivateExtra()
306 : granularity(Doc::Part) { }
307};
308
309struct Shared // ### get rid of
310{
311 Shared()
312 : count(1) { }
313 void ref() { ++count; }
314 bool deref() { return (--count == 0); }
315
316 int count;
317};
318
319static QString cleanLink(const QString &link)
320{
321 int colonPos = link.indexOf(':');
322 if ((colonPos == -1) ||
323 (!link.startsWith("file:") && !link.startsWith("mailto:")))
324 return link;
325 return link.mid(colonPos + 1).simplified();
326}
327
328class DocPrivate : public Shared
329{
330 public:
331 DocPrivate(const Location& start = Location::null,
332 const Location& end = Location::null,
333 const QString& source = "");
334 ~DocPrivate();
335
336 void addAlso(const Text& also);
337 void constructExtra();
338 bool isEnumDocSimplifiable() const;
339
340 // ### move some of this in DocPrivateExtra
341 Location start_loc;
342 Location end_loc;
343 QString src;
344 Text text;
345 QSet<QString> params;
346 QList<Text> alsoList;
347 QStringList enumItemList;
348 QStringList omitEnumItemList;
349 QSet<QString> metacommandsUsed;
350 QCommandMap metaCommandMap;
351 bool hasLegalese : 1;
352 bool hasSectioningUnits : 1;
353 DocPrivateExtra *extra;
354};
355
356DocPrivate::DocPrivate(const Location& start,
357 const Location& end,
358 const QString& source)
359 : start_loc(start),
360 end_loc(end),
361 src(source),
362 hasLegalese(false),
363 hasSectioningUnits(false),
364 extra(0)
365{
366 // nothing.
367}
368
369DocPrivate::~DocPrivate()
370{
371 delete extra;
372}
373
374void DocPrivate::addAlso(const Text& also)
375{
376 alsoList.append(also);
377}
378
379void DocPrivate::constructExtra()
380{
381 if (extra == 0)
382 extra = new DocPrivateExtra;
383}
384
385bool DocPrivate::isEnumDocSimplifiable() const
386{
387 bool justMetColon = false;
388 int numValueTables = 0;
389
390 const Atom *atom = text.firstAtom();
391 while (atom) {
392 if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
393 justMetColon = atom->string().endsWith(":");
394 }
395 else if ((atom->type() == Atom::ListLeft) &&
396 (atom->string() == ATOM_LIST_VALUE)) {
397 if (justMetColon || numValueTables > 0)
398 return false;
399 ++numValueTables;
400 }
401 atom = atom->next();
402 }
403 return true;
404}
405
406class DocParser
407{
408 public:
409 void parse(const QString &source,
410 DocPrivate *docPrivate,
411 const QSet<QString> &metaCommandSet);
412
413 static int endCmdFor(int cmd);
414 static QString cmdName(int cmd);
415 static QString endCmdName(int cmd);
416 static QString untabifyEtc(const QString& str);
417 static int indentLevel(const QString& str);
418 static QString unindent(int level, const QString& str);
419 static QString slashed(const QString& str);
420
421 static int tabSize;
422 static QStringList exampleFiles;
423 static QStringList exampleDirs;
424 static QStringList sourceFiles;
425 static QStringList sourceDirs;
426 static bool quoting;
427
428 private:
429 Location& location();
430 QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
431 const QString& str);
432 void checkExpiry(const QString& date);
433 void insertBaseName(const QString &baseName);
434 void insertTarget(const QString& target, bool keyword);
435 void include(const QString& fileName, const QString& identifier);
436 void startFormat(const QString& format, int cmd);
437 bool openCommand(int cmd);
438 bool closeCommand(int endCmd);
439 void startSection(Doc::Sections unit, int cmd);
440 void endSection(int unit, int endCmd);
441 void parseAlso();
442 void append(Atom::Type type, const QString& string = "");
443 void append(Atom::Type type, const QString& p1, const QString& p2);
444 void appendChar(QChar ch);
445 void appendWord(const QString &word);
446 void appendToCode(const QString &code);
447 void appendToCode(const QString &code, Atom::Type defaultType);
448 void startNewPara();
449 void enterPara(Atom::Type leftType = Atom::ParaLeft,
450 Atom::Type rightType = Atom::ParaRight,
451 const QString& string = "");
452 void leavePara();
453 void leaveValue();
454 void leaveValueList();
455 void leaveTableRow();
456 CodeMarker *quoteFromFile();
457 void expandMacro(const QString& name, const QString& def, int numParams);
458 QString expandMacroToString(const QString &name, const QString &def, int numParams);
459 Doc::Sections getSectioningUnit();
460 QString getArgument(bool verbatim = false);
461 QString getOptionalArgument();
462 QString getRestOfLine();
463 QString getMetaCommandArgument(const QString &cmdStr);
464 QString getUntilEnd(int cmd);
465 QString getCode(int cmd, CodeMarker *marker);
466 QString getUnmarkedCode(int cmd);
467
468 bool isBlankLine();
469 bool isLeftBraceAhead();
470 void skipSpacesOnLine();
471 void skipSpacesOrOneEndl();
472 void skipAllSpaces();
473 void skipToNextPreprocessorCommand();
474
475 QStack<int> openedInputs;
476
477 QString in;
478 int pos;
479 int len;
480 Location cachedLoc;
481 int cachedPos;
482
483 DocPrivate* priv;
484 enum ParagraphState {
485 OutsideParagraph,
486 InSingleLineParagraph,
487 InMultiLineParagraph
488 };
489 ParagraphState paraState;
490 bool inTableHeader;
491 bool inTableRow;
492 bool inTableItem;
493 bool indexStartedPara; // ### rename
494 Atom::Type pendingParaLeftType;
495 Atom::Type pendingParaRightType;
496 QString pendingParaString;
497
498 int braceDepth;
499 int minIndent;
500 Doc::Sections currentSection;
501 QMap<QString, Location> targetMap;
502 QMap<int, QString> pendingFormats;
503 QStack<int> openedCommands;
504 QStack<OpenedList> openedLists;
505 Quoter quoter;
506};
507
508int DocParser::tabSize;
509QStringList DocParser::exampleFiles;
510QStringList DocParser::exampleDirs;
511QStringList DocParser::sourceFiles;
512QStringList DocParser::sourceDirs;
513bool DocParser::quoting;
514
515/*!
516 Parse the \a source string to build a Text data structure
517 in \a docPrivate. The Text data structure is a linked list
518 of Atoms.
519
520 \a metaCommandSet is the set of metacommands that may be
521 found in \a source. These metacommands are not markup text
522 commands. They are topic commands and related metacommands.
523 */
524void DocParser::parse(const QString& source,
525 DocPrivate *docPrivate,
526 const QSet<QString>& metaCommandSet)
527{
528 in = source;
529 pos = 0;
530 len = in.length();
531 cachedLoc = docPrivate->start_loc;
532 cachedPos = 0;
533 priv = docPrivate;
534 priv->text << Atom::Nop;
535
536 paraState = OutsideParagraph;
537 inTableHeader = false;
538 inTableRow = false;
539 inTableItem = false;
540 indexStartedPara = false;
541 pendingParaLeftType = Atom::Nop;
542 pendingParaRightType = Atom::Nop;
543
544 braceDepth = 0;
545 minIndent = INT_MAX;
546 currentSection = Doc::NoSection;
547 openedCommands.push(CMD_OMIT);
548 quoter.reset();
549
550 CodeMarker *marker = 0;
551 Atom *currentLinkAtom = 0;
552 QString p1, p2;
553 QStack<bool> preprocessorSkipping;
554 int numPreprocessorSkipping = 0;
555
556 while (pos < len) {
557 QChar ch = in.at(pos);
558
559 switch (ch.unicode()) {
560 case '\\':
561 {
562 QString cmdStr;
563 pos++;
564 while (pos < len) {
565 ch = in.at(pos);
566 if (ch.isLetterOrNumber()) {
567 cmdStr += ch;
568 pos++;
569 }
570 else {
571 break;
572 }
573 }
574 if (cmdStr.isEmpty()) {
575 if (pos < len) {
576 enterPara();
577 if (in.at(pos).isSpace()) {
578 skipAllSpaces();
579 appendChar(QLatin1Char(' '));
580 }
581 else {
582 appendChar(in.at(pos++));
583 }
584 }
585 }
586 else {
587 int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
588 switch (cmd) {
589 case CMD_A:
590 enterPara();
591 p1 = getArgument();
592 append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
593 append(Atom::String, p1);
594 append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
595 priv->params.insert(p1);
596 break;
597 case CMD_ABSTRACT:
598 if (openCommand(cmd)) {
599 leavePara();
600 append(Atom::AbstractLeft);
601 }
602 break;
603 case CMD_BADCODE:
604 leavePara();
605 append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
606 break;
607 case CMD_BASENAME:
608 leavePara();
609 insertBaseName(getArgument());
610 break;
611 case CMD_BOLD:
612 startFormat(ATOM_FORMATTING_BOLD, cmd);
613 break;
614 case CMD_BRIEF:
615 leavePara();
616 enterPara(Atom::BriefLeft, Atom::BriefRight);
617 break;
618 case CMD_C:
619 enterPara();
620 p1 = untabifyEtc(getArgument(true));
621 marker = CodeMarker::markerForCode(p1);
622 append(Atom::C, marker->markedUpCode(p1, 0, location()));
623 break;
624 case CMD_CAPTION:
625 leavePara();
626 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
627 break;
628 case CMD_CHAPTER:
629 startSection(Doc::Chapter, cmd);
630 break;
631 case CMD_CODE:
632 leavePara();
633 append(Atom::Code, getCode(CMD_CODE, 0));
634 break;
635 case CMD_QML:
636 leavePara();
637 append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
638 break;
639 case CMD_QMLTEXT:
640 append(Atom::QmlText);
641 break;
642 case CMD_JS:
643 leavePara();
644 append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
645 break;
646 case CMD_DIV:
647 leavePara();
648 p1 = getArgument(true);
649 append(Atom::DivLeft, p1);
650 openedCommands.push(cmd);
651 break;
652 case CMD_ENDDIV:
653 leavePara();
654 append(Atom::DivRight);
655 closeCommand(cmd);
656 break;
657 case CMD_CODELINE:
658 {
659 if (!quoting) {
660 if (priv->text.lastAtom()->type() == Atom::Code
661 && priv->text.lastAtom()->string().endsWith("\n\n"))
662 priv->text.lastAtom()->chopString();
663 appendToCode("\n");
664 }
665 else {
666 append(Atom::CodeQuoteCommand, cmdStr);
667 append(Atom::CodeQuoteArgument, " ");
668 }
669 }
670 break;
671 case CMD_DOTS:
672 {
673 if (!quoting) {
674 if (priv->text.lastAtom()->type() == Atom::Code
675 && priv->text.lastAtom()->string().endsWith("\n\n"))
676 priv->text.lastAtom()->chopString();
677
678 QString arg = getOptionalArgument();
679 int indent = 4;
680 if (!arg.isEmpty())
681 indent = arg.toInt();
682 for (int i = 0; i < indent; ++i)
683 appendToCode(" ");
684 appendToCode("...\n");
685 }
686 else {
687 append(Atom::CodeQuoteCommand, cmdStr);
688 QString arg = getOptionalArgument();
689 if (arg.isEmpty())
690 arg = "4";
691 append(Atom::CodeQuoteArgument, arg);
692 }
693 }
694 break;
695 case CMD_ELSE:
696 if (preprocessorSkipping.size() > 0) {
697 if (preprocessorSkipping.top()) {
698 --numPreprocessorSkipping;
699 }
700 else {
701 ++numPreprocessorSkipping;
702 }
703 preprocessorSkipping.top() = !preprocessorSkipping.top();
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_ELSE)));
710 }
711 break;
712 case CMD_ENDABSTRACT:
713 if (closeCommand(cmd)) {
714 leavePara();
715 append(Atom::AbstractRight);
716 }
717 break;
718 case CMD_ENDCHAPTER:
719 endSection(Doc::Chapter, cmd);
720 break;
721 case CMD_ENDCODE:
722 closeCommand(cmd);
723 break;
724 case CMD_ENDQML:
725 closeCommand(cmd);
726 break;
727 case CMD_ENDQMLTEXT:
728 append(Atom::EndQmlText);
729 break;
730 case CMD_ENDJS:
731 closeCommand(cmd);
732 break;
733 case CMD_ENDFOOTNOTE:
734 if (closeCommand(cmd)) {
735 leavePara();
736 append(Atom::FootnoteRight);
737 paraState = InMultiLineParagraph; // ###
738 }
739 break;
740 case CMD_ENDIF:
741 if (preprocessorSkipping.count() > 0) {
742 if (preprocessorSkipping.pop())
743 --numPreprocessorSkipping;
744 (void)getRestOfLine(); // ### should ensure that it's empty
745 if (numPreprocessorSkipping)
746 skipToNextPreprocessorCommand();
747 }
748 else {
749 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
750 }
751 break;
752 case CMD_ENDLEGALESE:
753 if (closeCommand(cmd)) {
754 leavePara();
755 append(Atom::LegaleseRight);
756 }
757 break;
758 case CMD_ENDLINK:
759 if (closeCommand(cmd)) {
760 if (priv->text.lastAtom()->type() == Atom::String
761 && priv->text.lastAtom()->string().endsWith(" "))
762 priv->text.lastAtom()->chopString();
763 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
764 }
765 break;
766 case CMD_ENDLIST:
767 if (closeCommand(cmd)) {
768 leavePara();
769 if (openedLists.top().isStarted()) {
770 append(Atom::ListItemRight,
771 openedLists.top().styleString());
772 append(Atom::ListRight,
773 openedLists.top().styleString());
774 }
775 openedLists.pop();
776 }
777 break;
778 case CMD_ENDOMIT:
779 closeCommand(cmd);
780 break;
781 case CMD_ENDPART:
782 endSection(Doc::Part, cmd);
783 break;
784 case CMD_ENDQUOTATION:
785 if (closeCommand(cmd)) {
786 leavePara();
787 append(Atom::QuotationRight);
788 }
789 break;
790 case CMD_ENDRAW:
791 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
792 break;
793 case CMD_ENDSECTION1:
794 endSection(Doc::Section1, cmd);
795 break;
796 case CMD_ENDSECTION2:
797 endSection(Doc::Section2, cmd);
798 break;
799 case CMD_ENDSECTION3:
800 endSection(Doc::Section3, cmd);
801 break;
802 case CMD_ENDSECTION4:
803 endSection(Doc::Section4, cmd);
804 break;
805 case CMD_ENDSIDEBAR:
806 if (closeCommand(cmd)) {
807 leavePara();
808 append(Atom::SidebarRight);
809 }
810 break;
811 case CMD_ENDTABLE:
812 if (closeCommand(cmd)) {
813 leaveTableRow();
814 append(Atom::TableRight);
815 }
816 break;
817 case CMD_EXPIRE:
818 checkExpiry(getArgument());
819 break;
820 case CMD_FOOTNOTE:
821 if (openCommand(cmd)) {
822 enterPara();
823 append(Atom::FootnoteLeft);
824 paraState = OutsideParagraph; // ###
825 }
826 break;
827 case CMD_ANNOTATEDLIST:
828 append(Atom::AnnotatedList, getArgument());
829 break;
830 case CMD_SINCELIST:
831 append(Atom::SinceList, getRestOfLine().simplified());
832 break;
833 case CMD_GENERATELIST:
834 append(Atom::GeneratedList, getArgument());
835 break;
836 case CMD_GRANULARITY:
837 priv->constructExtra();
838 priv->extra->granularity = getSectioningUnit();
839 break;
840 case CMD_HEADER:
841 if (openedCommands.top() == CMD_TABLE) {
842 leaveTableRow();
843 append(Atom::TableHeaderLeft);
844 inTableHeader = true;
845 }
846 else {
847 if (openedCommands.contains(CMD_TABLE)) {
848 location().warning(tr("Cannot use '\\%1' within '\\%2'")
849 .arg(cmdName(CMD_HEADER))
850 .arg(cmdName(openedCommands.top())));
851 }
852 else {
853 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
854 .arg(cmdName(CMD_HEADER))
855 .arg(cmdName(CMD_TABLE)));
856 }
857 }
858 break;
859 case CMD_I:
860 startFormat(ATOM_FORMATTING_ITALIC, cmd);
861 break;
862 case CMD_IF:
863 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
864 if (preprocessorSkipping.top())
865 ++numPreprocessorSkipping;
866 if (numPreprocessorSkipping)
867 skipToNextPreprocessorCommand();
868 break;
869 case CMD_IMAGE:
870 leaveValueList();
871 append(Atom::Image, getArgument());
872 append(Atom::ImageText, getRestOfLine());
873 break;
874 case CMD_INCLUDE:
875 {
876 QString fileName = getArgument();
877 QString identifier = getRestOfLine();
878 include(fileName, identifier);
879 }
880 break;
881 case CMD_INLINEIMAGE:
882 enterPara();
883 append(Atom::InlineImage, getArgument());
884 append(Atom::ImageText, getRestOfLine());
885 append(Atom::String, " ");
886 break;
887 case CMD_INDEX:
888 if (paraState == OutsideParagraph) {
889 enterPara();
890 indexStartedPara = true;
891 }
892 else {
893 const Atom *last = priv->text.lastAtom();
894 if (indexStartedPara &&
895 (last->type() != Atom::FormattingRight ||
896 last->string() != ATOM_FORMATTING_INDEX))
897 indexStartedPara = false;
898 }
899 startFormat(ATOM_FORMATTING_INDEX, cmd);
900 break;
901 case CMD_KEYWORD:
902 insertTarget(getRestOfLine(),true);
903 break;
904 case CMD_L:
905 enterPara();
906 if (isLeftBraceAhead()) {
907 p1 = getArgument();
908 append(Atom::Link, p1);
909 if (isLeftBraceAhead()) {
910 currentLinkAtom = priv->text.lastAtom();
911 startFormat(ATOM_FORMATTING_LINK, cmd);
912 }
913 else {
914 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
915 append(Atom::String, cleanLink(p1));
916 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
917 }
918 }
919 else {
920 p1 = getArgument();
921 append(Atom::Link, p1);
922 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
923 append(Atom::String, cleanLink(p1));
924 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
925 }
926 break;
927 case CMD_LEGALESE:
928 leavePara();
929 if (openCommand(cmd))
930 append(Atom::LegaleseLeft);
931 docPrivate->hasLegalese = true;
932 break;
933 case CMD_LINK:
934 if (openCommand(cmd)) {
935 enterPara();
936 p1 = getArgument();
937 append(Atom::Link, p1);
938 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
939 skipSpacesOrOneEndl();
940 }
941 break;
942 case CMD_LIST:
943 if (openCommand(cmd)) {
944 leavePara();
945 openedLists.push(OpenedList(location(),
946 getOptionalArgument()));
947 }
948 break;
949 case CMD_META:
950 priv->constructExtra();
951 p1 = getArgument();
952 priv->extra->metaMap.insert(p1, getArgument());
953 break;
954 case CMD_NEWCODE:
955 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
956 break;
957 case CMD_O:
958 leavePara();
959 if (openedCommands.top() == CMD_LIST) {
960 if (openedLists.top().isStarted()) {
961 append(Atom::ListItemRight,
962 openedLists.top().styleString());
963 }
964 else {
965 append(Atom::ListLeft,
966 openedLists.top().styleString());
967 }
968 openedLists.top().next();
969 append(Atom::ListItemNumber,
970 openedLists.top().numberString());
971 append(Atom::ListItemLeft,
972 openedLists.top().styleString());
973 enterPara();
974 }
975 else if (openedCommands.top() == CMD_TABLE) {
976 p1 = "1,1";
977 if (isLeftBraceAhead()) {
978 p1 = getArgument();
979 if (isLeftBraceAhead()) {
980 p2 = getArgument();
981 }
982 }
983
984 if (!inTableHeader && !inTableRow) {
985 location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
986 .arg(cmdName(CMD_HEADER))
987 .arg(cmdName(CMD_ROW))
988 .arg(cmdName(CMD_O)));
989 append(Atom::TableRowLeft);
990 inTableRow = true;
991 }
992 else if (inTableItem) {
993 append(Atom::TableItemRight);
994 inTableItem = false;
995 }
996
997 append(Atom::TableItemLeft, p1, p2);
998 inTableItem = true;
999 }
1000 else {
1001 location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
1002 .arg(cmdName(cmd))
1003 .arg(cmdName(CMD_LIST))
1004 .arg(cmdName(CMD_TABLE)));
1005 }
1006 break;
1007 case CMD_OLDCODE:
1008 leavePara();
1009 append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
1010 append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
1011 break;
1012 case CMD_OMIT:
1013 getUntilEnd(cmd);
1014 break;
1015 case CMD_OMITVALUE:
1016 p1 = getArgument();
1017 if (!priv->enumItemList.contains(p1))
1018 priv->enumItemList.append(p1);
1019 if (!priv->omitEnumItemList.contains(p1))
1020 priv->omitEnumItemList.append(p1);
1021 break;
1022 case CMD_PART:
1023 startSection(Doc::Part, cmd);
1024 break;
1025 case CMD_PRINTLINE:
1026 leavePara();
1027 if (!quoting)
1028 appendToCode(quoter.quoteLine(location(), cmdStr,
1029 getRestOfLine()));
1030 else {
1031 append(Atom::CodeQuoteCommand, cmdStr);
1032 append(Atom::CodeQuoteArgument, getRestOfLine());
1033 }
1034 break;
1035 case CMD_PRINTTO:
1036 leavePara();
1037 if (!quoting)
1038 appendToCode(quoter.quoteTo(location(), cmdStr,
1039 getRestOfLine()));
1040 else {
1041 append(Atom::CodeQuoteCommand, cmdStr);
1042 append(Atom::CodeQuoteArgument, getRestOfLine());
1043 }
1044 break;
1045 case CMD_PRINTUNTIL:
1046 leavePara();
1047 if (!quoting)
1048 appendToCode(quoter.quoteUntil(location(), cmdStr,
1049 getRestOfLine()));
1050 else {
1051 append(Atom::CodeQuoteCommand, cmdStr);
1052 append(Atom::CodeQuoteArgument, getRestOfLine());
1053 }
1054 break;
1055 case CMD_QUOTATION:
1056 if (openCommand(cmd)) {
1057 leavePara();
1058 append(Atom::QuotationLeft);
1059 }
1060 break;
1061 case CMD_QUOTEFILE:
1062 {
1063 leavePara();
1064 QString fileName = getArgument();
1065 Doc::quoteFromFile(location(), quoter, fileName);
1066 if (!quoting) {
1067 append(Atom::Code,
1068 quoter.quoteTo(location(), cmdStr, ""));
1069 quoter.reset();
1070 }
1071 else {
1072 append(Atom::CodeQuoteCommand, cmdStr);
1073 append(Atom::CodeQuoteArgument, fileName);
1074 }
1075 break;
1076 }
1077 case CMD_QUOTEFROMFILE:
1078 leavePara();
1079 if (!quoting)
1080 quoteFromFile();
1081 else {
1082 append(Atom::CodeQuoteCommand, cmdStr);
1083 append(Atom::CodeQuoteArgument, getArgument());
1084 }
1085 break;
1086 case CMD_QUOTEFUNCTION:
1087 leavePara();
1088 marker = quoteFromFile();
1089 p1 = getRestOfLine();
1090 if (!quoting) {
1091 quoter.quoteTo(location(), cmdStr,
1092 slashed(marker->functionBeginRegExp(p1)));
1093 append(Atom::Code,
1094 quoter.quoteUntil(location(), cmdStr,
1095 slashed(marker->functionEndRegExp(p1))));
1096 quoter.reset();
1097 }
1098 else {
1099 append(Atom::CodeQuoteCommand, cmdStr);
1100 append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
1101 }
1102 break;
1103 case CMD_RAW:
1104 leavePara();
1105 p1 = getRestOfLine();
1106 if (p1.isEmpty())
1107 location().warning(tr("Missing format name after '\\%1")
1108 .arg(cmdName(CMD_RAW)));
1109 append(Atom::FormatIf, p1);
1110 append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
1111 append(Atom::FormatElse);
1112 append(Atom::FormatEndif);
1113 break;
1114 case CMD_ROW:
1115 if (openedCommands.top() == CMD_TABLE) {
1116 p1.clear();
1117 if (isLeftBraceAhead())
1118 p1 = getArgument(true);
1119 leaveTableRow();
1120 append(Atom::TableRowLeft,p1);
1121 inTableRow = true;
1122 }
1123 else {
1124 if (openedCommands.contains(CMD_TABLE)) {
1125 location().warning(tr("Cannot use '\\%1' within '\\%2'")
1126 .arg(cmdName(CMD_ROW))
1127 .arg(cmdName(openedCommands.top())));
1128 }
1129 else {
1130 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
1131 .arg(cmdName(CMD_ROW))
1132 .arg(cmdName(CMD_TABLE)));
1133 }
1134 }
1135 break;
1136 case CMD_SA:
1137 parseAlso();
1138 break;
1139 case CMD_SECTION1:
1140 startSection(Doc::Section1, cmd);
1141 break;
1142 case CMD_SECTION2:
1143 startSection(Doc::Section2, cmd);
1144 break;
1145 case CMD_SECTION3:
1146 startSection(Doc::Section3, cmd);
1147 break;
1148 case CMD_SECTION4:
1149 startSection(Doc::Section4, cmd);
1150 break;
1151 case CMD_SIDEBAR:
1152 if (openCommand(cmd)) {
1153 leavePara();
1154 append(Atom::SidebarLeft);
1155 }
1156 break;
1157 case CMD_SKIPLINE:
1158 leavePara();
1159 if (!quoting)
1160 quoter.quoteLine(location(),
1161 cmdStr,
1162 getRestOfLine());
1163 else {
1164 append(Atom::CodeQuoteCommand, cmdStr);
1165 append(Atom::CodeQuoteArgument, getRestOfLine());
1166 }
1167 break;
1168 case CMD_SKIPTO:
1169 leavePara();
1170 if (!quoting)
1171 quoter.quoteTo(location(),
1172 cmdStr,
1173 getRestOfLine());
1174 else {
1175 append(Atom::CodeQuoteCommand, cmdStr);
1176 append(Atom::CodeQuoteArgument, getRestOfLine());
1177 }
1178 break;
1179 case CMD_SKIPUNTIL:
1180 leavePara();
1181 if (!quoting)
1182 quoter.quoteUntil(location(),
1183 cmdStr,
1184 getRestOfLine());
1185 else {
1186 append(Atom::CodeQuoteCommand, cmdStr);
1187 append(Atom::CodeQuoteArgument, getRestOfLine());
1188 }
1189 break;
1190 case CMD_SPAN:
1191 p1 = ATOM_FORMATTING_SPAN + getArgument(true);
1192 startFormat(p1, cmd);
1193 break;
1194 case CMD_SNIPPET:
1195 leavePara();
1196 {
1197 QString snippet = getArgument();
1198 QString identifier = getRestOfLine();
1199 if (quoting) {
1200 append(Atom::SnippetCommand, cmdStr);
1201 append(Atom::SnippetLocation, snippet);
1202 append(Atom::SnippetIdentifier, identifier);
1203 }
1204 else {
1205 marker = Doc::quoteFromFile(location(),quoter,snippet);
1206 appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
1207 }
1208 }
1209 break;
1210 case CMD_SUB:
1211 startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
1212 break;
1213 case CMD_SUP:
1214 startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
1215 break;
1216 case CMD_TABLE:
1217 p1 = getRestOfLine();
1218 if (openCommand(cmd)) {
1219 leavePara();
1220 append(Atom::TableLeft, p1);
1221 inTableHeader = false;
1222 inTableRow = false;
1223 inTableItem = false;
1224 }
1225 break;
1226 case CMD_TABLEOFCONTENTS:
1227 p1 = "1";
1228 if (isLeftBraceAhead())
1229 p1 = getArgument();
1230 p1 += ",";
1231 p1 += QString::number((int)getSectioningUnit());
1232 append(Atom::TableOfContents, p1);
1233 break;
1234 case CMD_TARGET:
1235 insertTarget(getRestOfLine(),false);
1236 break;
1237 case CMD_TT:
1238 startFormat(ATOM_FORMATTING_TELETYPE, cmd);
1239 break;
1240 case CMD_UNDERLINE:
1241 startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
1242 break;
1243 case CMD_UNICODE:
1244 enterPara();
1245 p1 = getArgument();
1246 {
1247 bool ok;
1248 uint unicodeChar = p1.toUInt(&ok, 0);
1249 if (!ok ||
1250 (unicodeChar == 0x0000) ||
1251 (unicodeChar > 0xFFFE)) {
1252 location().warning(tr("Invalid Unicode character '%1' specified "
1253 "with '%2'")
1254 .arg(p1, cmdName(CMD_UNICODE)));
1255 }
1256 else {
1257 append(Atom::String, QChar(unicodeChar));
1258 }
1259 }
1260 break;
1261 case CMD_VALUE:
1262 leaveValue();
1263 if (openedLists.top().style() == OpenedList::Value) {
1264 p1 = getArgument();
1265 if (!priv->enumItemList.contains(p1))
1266 priv->enumItemList.append(p1);
1267
1268 openedLists.top().next();
1269 append(Atom::ListTagLeft, ATOM_LIST_VALUE);
1270 append(Atom::String, p1);
1271 append(Atom::ListTagRight, ATOM_LIST_VALUE);
1272 append(Atom::ListItemLeft, ATOM_LIST_VALUE);
1273
1274 skipSpacesOrOneEndl();
1275 if (isBlankLine())
1276 append(Atom::Nop);
1277 }
1278 else {
1279 // ### problems
1280 }
1281 break;
1282 case CMD_WARNING:
1283 leavePara();
1284 enterPara();
1285 append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
1286 append(Atom::String, "Warning:");
1287 append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
1288 append(Atom::String, " ");
1289 break;
1290 case CMD_OVERLOAD:
1291 priv->metacommandsUsed.insert(cmdStr);
1292 p1.clear();
1293 if (!isBlankLine())
1294 p1 = getRestOfLine();
1295 if (!p1.isEmpty()) {
1296 append(Atom::ParaLeft);
1297 append(Atom::String, "This function overloads ");
1298 append(Atom::AutoLink,p1);
1299 append(Atom::String, ".");
1300 append(Atom::ParaRight);
1301 }
1302 else {
1303 append(Atom::ParaLeft);
1304 append(Atom::String,"This is an overloaded function.");
1305 append(Atom::ParaRight);
1306 p1 = getMetaCommandArgument(cmdStr);
1307 }
1308 priv->metaCommandMap[cmdStr].append(p1);
1309 break;
1310 case NOT_A_CMD:
1311 if (metaCommandSet.contains(cmdStr)) {
1312 priv->metacommandsUsed.insert(cmdStr);
1313 QString xxx = getMetaCommandArgument(cmdStr);
1314 priv->metaCommandMap[cmdStr].append(xxx);
1315 }
1316 else if (macroHash()->contains(cmdStr)) {
1317 const Macro &macro = macroHash()->value(cmdStr);
1318 int numPendingFi = 0;
1319 QStringMap::ConstIterator d;
1320 d = macro.otherDefs.begin();
1321 while (d != macro.otherDefs.end()) {
1322 append(Atom::FormatIf, d.key());
1323 expandMacro(cmdStr, *d, macro.numParams);
1324 ++d;
1325
1326 if (d == macro.otherDefs.end()) {
1327 append(Atom::FormatEndif);
1328 }
1329 else {
1330 append(Atom::FormatElse);
1331 numPendingFi++;
1332 }
1333 }
1334 while (numPendingFi-- > 0)
1335 append(Atom::FormatEndif);
1336
1337 if (!macro.defaultDef.isEmpty()) {
1338 if (!macro.otherDefs.isEmpty()) {
1339 macro.defaultDefLocation.warning(
1340 tr("Macro cannot have both "
1341 "format-specific and qdoc- "
1342 "syntax definitions"));
1343 }
1344 else {
1345 location().push(macro.defaultDefLocation.filePath());
1346 in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
1347 len = in.length();
1348 openedInputs.push(pos + macro.defaultDef.length());
1349 }
1350 }
1351 }
1352 else {
1353 location().warning(
1354 tr("Unknown command '\\%1'").arg(cmdStr),
1355 detailsUnknownCommand(metaCommandSet,cmdStr));
1356 enterPara();
1357 append(Atom::UnknownCommand, cmdStr);
1358 }
1359 }
1360 }
1361 }
1362 break;
1363 case '{':
1364 enterPara();
1365 appendChar('{');
1366 braceDepth++;
1367 pos++;
1368 break;
1369 case '}':
1370 {
1371 braceDepth--;
1372 pos++;
1373
1374 QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth);
1375 if (f == pendingFormats.end()) {
1376 enterPara();
1377 appendChar('}');
1378 }
1379 else {
1380 append(Atom::FormattingRight, *f);
1381 if (*f == ATOM_FORMATTING_INDEX) {
1382 if (indexStartedPara)
1383 skipAllSpaces();
1384 }
1385 else if (*f == ATOM_FORMATTING_LINK) {
1386 // hack for C++ to support links like
1387 // \l{QString::}{count()}
1388 if (currentLinkAtom &&
1389 currentLinkAtom->string().endsWith("::")) {
1390 QString suffix = Text::subText(currentLinkAtom,
1391 priv->text.lastAtom()).toString();
1392 currentLinkAtom->appendString(suffix);
1393 }
1394 currentLinkAtom = 0;
1395 }
1396 pendingFormats.erase(f);
1397 }
1398 }
1399 break;
1400 default:
1401 {
1402 bool newWord;
1403 switch (priv->text.lastAtom()->type()) {
1404 case Atom::ParaLeft:
1405 newWord = true;
1406 break;
1407 default:
1408 newWord = false;
1409 }
1410
1411 if (paraState == OutsideParagraph) {
1412 if (ch.isSpace()) {
1413 ++pos;
1414 newWord = false;
1415 }
1416 else {
1417 enterPara();
1418 newWord = true;
1419 }
1420 }
1421 else {
1422 if (ch.isSpace()) {
1423 ++pos;
1424 if ((ch == '\n') &&
1425 (paraState == InSingleLineParagraph ||
1426 isBlankLine())) {
1427 leavePara();
1428 newWord = false;
1429 }
1430 else {
1431 appendChar(' ');
1432 newWord = true;
1433 }
1434 }
1435 else {
1436 newWord = true;
1437 }
1438 }
1439
1440 if (newWord) {
1441 int startPos = pos;
1442 int numInternalUppercase = 0;
1443 int numLowercase = 0;
1444 int numStrangeSymbols = 0;
1445
1446 while (pos < len) {
1447 unsigned char latin1Ch = in.at(pos).toLatin1();
1448 if (islower(latin1Ch)) {
1449 ++numLowercase;
1450 ++pos;
1451 }
1452 else if (isupper(latin1Ch)) {
1453 if (pos > startPos)
1454 ++numInternalUppercase;
1455 ++pos;
1456 }
1457 else if (isdigit(latin1Ch)) {
1458 if (pos > startPos) {
1459 ++pos;
1460 }
1461 else {
1462 break;
1463 }
1464 }
1465 else if (latin1Ch == '_' || latin1Ch == '@') {
1466 ++numStrangeSymbols;
1467 ++pos;
1468 }
1469 else if (latin1Ch == ':' && pos < len - 1
1470 && in.at(pos + 1) == QLatin1Char(':')) {
1471 ++numStrangeSymbols;
1472 pos += 2;
1473 }
1474 else if (latin1Ch == '(') {
1475 if (pos > startPos) {
1476 if (pos < len - 1 &&
1477 in.at(pos + 1) == QLatin1Char(')')) {
1478 ++numStrangeSymbols;
1479 pos += 2;
1480 break;
1481 }
1482 else {
1483 // ### handle functions with signatures
1484 // and function calls
1485 break;
1486 }
1487 }
1488 else {
1489 break;
1490 }
1491 }
1492 else {
1493 break;
1494 }
1495 }
1496
1497 if (pos == startPos) {
1498 if (!ch.isSpace()) {
1499 appendChar(ch);
1500 ++pos;
1501 }
1502 }
1503 else {
1504 QString word = in.mid(startPos, pos - startPos);
1505 // is word a C++ symbol or an English word?
1506 if ((numInternalUppercase >= 1 && numLowercase >= 2)
1507 || numStrangeSymbols >= 1) {
1508 append(Atom::AutoLink, word);
1509 }
1510 else {
1511 appendWord(word);
1512 }
1513 }
1514 }
1515 }
1516 }
1517 }
1518 leaveValueList();
1519
1520 // for compatibility
1521 if (openedCommands.top() == CMD_LEGALESE) {
1522 append(Atom::LegaleseRight);
1523 openedCommands.pop();
1524 }
1525
1526 if (openedCommands.top() != CMD_OMIT) {
1527 location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
1528 }
1529 else if (preprocessorSkipping.count() > 0) {
1530 location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
1531 }
1532
1533 if (currentSection > Doc::NoSection) {
1534 append(Atom::SectionRight, QString::number(currentSection));
1535 currentSection = Doc::NoSection;
1536 }
1537
1538 if (priv->extra && priv->extra->granularity < priv->extra->section)
1539 priv->extra->granularity = priv->extra->section;
1540 priv->text.stripFirstAtom();
1541}
1542
1543Location &DocParser::location()
1544{
1545 while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
1546 cachedLoc.pop();
1547 cachedPos = openedInputs.pop();
1548 }
1549 while (cachedPos < pos)
1550 cachedLoc.advance(in.at(cachedPos++));
1551 return cachedLoc;
1552}
1553
1554QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
1555 const QString &str)
1556{
1557 QSet<QString> commandSet = metaCommandSet;
1558 int i = 0;
1559 while (cmds[i].english != 0) {
1560 commandSet.insert(*cmds[i].alias);
1561 i++;
1562 }
1563
1564 if (aliasMap()->contains(str))
1565 return tr("The command '\\%1' was renamed '\\%2' by the configuration"
1566 " file. Use the new name.")
1567 .arg(str).arg((*aliasMap())[str]);
1568
1569 QString best = nearestName(str, commandSet);
1570 if (best.isEmpty())
1571 return QString();
1572 return tr("Maybe you meant '\\%1'?").arg(best);
1573}
1574
1575void DocParser::checkExpiry(const QString& date)
1576{
1577 QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))");
1578
1579 if (ymd.exactMatch(date)) {
1580 int y = ymd.cap(1).toInt();
1581 int m = ymd.cap(2).toInt();
1582 int d = ymd.cap(3).toInt();
1583
1584 if (m == 0)
1585 m = 1;
1586 if (d == 0)
1587 d = 1;
1588 QDate expiryDate(y, m, d);
1589 if (expiryDate.isValid()) {
1590 int days = expiryDate.daysTo(QDate::currentDate());
1591 if (days == 0) {
1592 location().warning(tr("Documentation expires today"));
1593 }
1594 else if (days == 1) {
1595 location().warning(tr("Documentation expired yesterday"));
1596 }
1597 else if (days >= 2) {
1598 location().warning(tr("Documentation expired %1 days ago")
1599 .arg(days));
1600 }
1601 }
1602 else {
1603 location().warning(tr("Date '%1' invalid").arg(date));
1604 }
1605 }
1606 else {
1607 location().warning(tr("Date '%1' not in YYYY-MM-DD format")
1608 .arg(date));
1609 }
1610}
1611
1612void DocParser::insertBaseName(const QString &baseName)
1613{
1614 priv->constructExtra();
1615 if (currentSection == priv->extra->section) {
1616 priv->extra->baseName = baseName;
1617 }
1618 else {
1619 Atom *atom = priv->text.firstAtom();
1620 Atom *sectionLeft = 0;
1621
1622 int delta = currentSection - priv->extra->section;
1623
1624 while (atom != 0) {
1625 if (atom->type() == Atom::SectionLeft &&
1626 atom->string().toInt() == delta)
1627 sectionLeft = atom;
1628 atom = atom->next();
1629 }
1630 if (sectionLeft != 0)
1631 (void) new Atom(sectionLeft, Atom::BaseName, baseName);
1632 }
1633}
1634
1635void DocParser::insertTarget(const QString &target, bool keyword)
1636{
1637 if (targetMap.contains(target)) {
1638 location().warning(tr("Duplicate target name '%1'").arg(target));
1639 targetMap[target].warning(tr("(The previous occurrence is here)"));
1640 }
1641 else {
1642 targetMap.insert(target, location());
1643 append(Atom::Target, target);
1644 priv->constructExtra();
1645 if (keyword)
1646 priv->extra->keywords.append(priv->text.lastAtom());
1647 else
1648 priv->extra->targets.append(priv->text.lastAtom());
1649 }
1650}
1651
1652void DocParser::include(const QString& fileName, const QString& identifier)
1653{
1654 if (location().depth() > 16)
1655 location().fatal(tr("Too many nested '\\%1's")
1656 .arg(cmdName(CMD_INCLUDE)));
1657
1658 QString userFriendlyFilePath;
1659 // ### use current directory?
1660 QString filePath = Config::findFile(location(),
1661 sourceFiles,
1662 sourceDirs,
1663 fileName,
1664 userFriendlyFilePath);
1665 if (filePath.isEmpty()) {
1666 location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName));
1667 }
1668 else {
1669 QFile inFile(filePath);
1670 if (!inFile.open(QFile::ReadOnly)) {
1671 location().warning(tr("Cannot open qdoc include file '%1'")
1672 .arg(userFriendlyFilePath));
1673 }
1674 else {
1675 location().push(userFriendlyFilePath);
1676
1677 QTextStream inStream(&inFile);
1678 QString includedStuff = inStream.readAll();
1679 inFile.close();
1680
1681 if (identifier.isEmpty()) {
1682 in.insert(pos, includedStuff);
1683 len = in.length();
1684 openedInputs.push(pos + includedStuff.length());
1685 }
1686 else {
1687 QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
1688 int i = 0;
1689 int startLine = -1;
1690 while (i < lineBuffer.size()) {
1691 if (lineBuffer[i].startsWith("//!")) {
1692 if (lineBuffer[i].contains(identifier)) {
1693 startLine = i+1;
1694 break;
1695 }
1696 }
1697 ++i;
1698 }
1699 if (startLine < 0) {
1700 location().warning(tr("Cannot find '%1' in '%2'")
1701 .arg(identifier)
1702 .arg(userFriendlyFilePath));
1703 return;
1704
1705 }
1706 QString result;
1707 i = startLine;
1708 do {
1709 if (lineBuffer[i].startsWith("//!")) {
1710 if (i<lineBuffer.size()) {
1711 if (lineBuffer[i].contains(identifier)) {
1712 break;
1713 }
1714 }
1715 }
1716 else
1717 result += lineBuffer[i] + "\n";
1718 ++i;
1719 } while (i < lineBuffer.size());
1720 if (result.isEmpty()) {
1721 location().warning(tr("Empty qdoc snippet '%1' in '%2'")
1722 .arg(identifier)
1723 .arg(userFriendlyFilePath));
1724 }
1725 else {
1726 in.insert(pos, result);
1727 len = in.length();
1728 openedInputs.push(pos + result.length());
1729 }
1730 }
1731 }
1732 }
1733}
1734
1735void DocParser::startFormat(const QString& format, int cmd)
1736{
1737 enterPara();
1738
1739 QMap<int, QString>::ConstIterator f = pendingFormats.begin();
1740 while (f != pendingFormats.end()) {
1741 if (*f == format) {
1742 location().warning(tr("Cannot nest '\\%1' commands")
1743 .arg(cmdName(cmd)));
1744 return;
1745 }
1746 ++f;
1747 }
1748
1749 append(Atom::FormattingLeft, format);
1750
1751 if (isLeftBraceAhead()) {
1752 skipSpacesOrOneEndl();
1753 pendingFormats.insert(braceDepth, format);
1754 ++braceDepth;
1755 ++pos;
1756 }
1757 else {
1758 append(Atom::String, getArgument());
1759 append(Atom::FormattingRight, format);
1760 if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
1761 skipAllSpaces();
1762 indexStartedPara = false;
1763 }
1764 }
1765}
1766
1767bool DocParser::openCommand(int cmd)
1768{
1769 int outer = openedCommands.top();
1770 bool ok = true;
1771
1772 if (cmd != CMD_LINK) {
1773 if (outer == CMD_LIST) {
1774 ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
1775 }
1776 else if (outer == CMD_ABSTRACT) {
1777 ok = (cmd == CMD_LIST ||
1778 cmd == CMD_QUOTATION ||
1779 cmd == CMD_TABLE);
1780 }
1781 else if (outer == CMD_SIDEBAR) {
1782 ok = (cmd == CMD_LIST ||
1783 cmd == CMD_QUOTATION ||
1784 cmd == CMD_SIDEBAR);
1785 }
1786 else if (outer == CMD_QUOTATION) {
1787 ok = (cmd == CMD_LIST);
1788 }
1789 else if (outer == CMD_TABLE) {
1790 ok = (cmd == CMD_LIST ||
1791 cmd == CMD_FOOTNOTE ||
1792 cmd == CMD_QUOTATION);
1793 }
1794 else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
1795 ok = false;
1796 }
1797 }
1798
1799 if (ok) {
1800 openedCommands.push(cmd);
1801 }
1802 else {
1803 location().warning(tr("Cannot use '\\%1' within '\\%2'")
1804 .arg(cmdName(cmd)).arg(cmdName(outer)));
1805 }
1806 return ok;
1807}
1808
1809bool DocParser::closeCommand(int endCmd)
1810{
1811 if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
1812 openedCommands.pop();
1813 return true;
1814 }
1815 else {
1816 bool contains = false;
1817 QStack<int> opened2 = openedCommands;
1818 while (opened2.size() > 1) {
1819 if (endCmdFor(opened2.top()) == endCmd) {
1820 contains = true;
1821 break;
1822 }
1823 opened2.pop();
1824 }
1825
1826 if (contains) {
1827 while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) {
1828 location().warning(tr("Missing '\\%1' before '\\%2'")
1829 .arg(endCmdName(openedCommands.top()))
1830 .arg(cmdName(endCmd)));
1831 openedCommands.pop();
1832 }
1833 }
1834 else {
1835 location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd)));
1836 }
1837 return false;
1838 }
1839}
1840
1841void DocParser::startSection(Doc::Sections unit, int cmd)
1842{
1843 leaveValueList();
1844
1845 if (currentSection == Doc::NoSection) {
1846 currentSection = (Doc::Sections) (unit);
1847 priv->constructExtra();
1848 priv->extra->section = currentSection;
1849 }
1850 else
1851 endSection(unit,cmd);
1852
1853 append(Atom::SectionLeft, QString::number(unit));
1854 priv->constructExtra();
1855 priv->extra->tableOfContents.append(priv->text.lastAtom());
1856 priv->extra->tableOfContentsLevels.append(unit);
1857 enterPara(Atom::SectionHeadingLeft,
1858 Atom::SectionHeadingRight,
1859 QString::number(unit));
1860 currentSection = unit;
1861
1862}
1863
1864void DocParser::endSection(int , int) // (int unit, int endCmd)
1865{
1866 leavePara();
1867 append(Atom::SectionRight, QString::number(currentSection));
1868 currentSection = (Doc::NoSection);
1869}
1870
1871void DocParser::parseAlso()
1872{
1873 leavePara();
1874 skipSpacesOnLine();
1875 while (pos < len && in[pos] != '\n') {
1876 QString target;
1877 QString str;
1878
1879 if (in[pos] == '{') {
1880 target = getArgument();
1881 skipSpacesOnLine();
1882 if (in[pos] == '{') {
1883 str = getArgument();
1884
1885 // hack for C++ to support links like \l{QString::}{count()}
1886 if (target.endsWith("::"))
1887 target += str;
1888 }
1889 else {
1890 str = target;
1891 }
1892#ifdef QDOC2_COMPAT
1893 }
1894 else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
1895 pos += 6;
1896 target = getArgument();
1897 int endPos = in.indexOf("\\endlink", pos);
1898 if (endPos != -1) {
1899 str = in.mid(pos, endPos - pos).trimmed();
1900 pos = endPos + 8;
1901 }
1902#endif
1903 }
1904 else {
1905 target = getArgument();
1906 str = cleanLink(target);
1907 }
1908
1909 Text also;
1910 also << Atom(Atom::Link, target)
1911 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1912 << str
1913 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1914 priv->addAlso(also);
1915
1916 skipSpacesOnLine();
1917 if (pos < len && in[pos] == ',') {
1918 pos++;
1919 skipSpacesOrOneEndl();
1920 }
1921 else if (in[pos] != '\n') {
1922 location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
1923 }
1924 }
1925}
1926
1927//static bool debug = false;
1928#if 0
1929 if (type == Atom::DivLeft)
1930 debug = true;
1931 if (debug)
1932 qDebug() << type << string;
1933 if (type == Atom::DivRight)
1934 debug = false;
1935#endif
1936
1937void DocParser::append(Atom::Type type, const QString &string)
1938{
1939 Atom::Type lastType = priv->text.lastAtom()->type();
1940 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
1941 priv->text.lastAtom()->chopString();
1942 priv->text << Atom(type, string);
1943}
1944
1945void DocParser::append(Atom::Type type, const QString& p1, const QString& p2)
1946{
1947 Atom::Type lastType = priv->text.lastAtom()->type();
1948 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
1949 priv->text.lastAtom()->chopString();
1950 priv->text << Atom(type, p1, p2);
1951}
1952
1953void DocParser::appendChar(QChar ch)
1954{
1955 if (priv->text.lastAtom()->type() != Atom::String)
1956 append(Atom::String);
1957 Atom *atom = priv->text.lastAtom();
1958 if (ch == QLatin1Char(' ')) {
1959 if (!atom->string().endsWith(QLatin1Char(' ')))
1960 atom->appendChar(QLatin1Char(' '));
1961 }
1962 else
1963 atom->appendChar(ch);
1964}
1965
1966void DocParser::appendWord(const QString &word)
1967{
1968 if (priv->text.lastAtom()->type() != Atom::String) {
1969 append(Atom::String, word);
1970 }
1971 else
1972 priv->text.lastAtom()->appendString(word);
1973}
1974
1975void DocParser::appendToCode(const QString& markedCode)
1976{
1977 Atom::Type lastType = priv->text.lastAtom()->type();
1978#ifdef QDOC_QML
1979 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
1980 append(Atom::Qml);
1981#else
1982 if (lastType != Atom::Code)
1983 append(Atom::Code);
1984#endif
1985 priv->text.lastAtom()->appendString(markedCode);
1986}
1987
1988void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType)
1989{
1990 Atom::Type lastType = priv->text.lastAtom()->type();
1991 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
1992 append(defaultType, markedCode);
1993 else
1994 priv->text.lastAtom()->appendString(markedCode);
1995}
1996
1997void DocParser::startNewPara()
1998{
1999 leavePara();
2000 enterPara();
2001}
2002
2003void DocParser::enterPara(Atom::Type leftType,
2004 Atom::Type rightType,
2005 const QString& string)
2006{
2007 if (paraState == OutsideParagraph) {
2008
2009 if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
2010 (priv->text.lastAtom()->type() != Atom::DivLeft)) {
2011 leaveValueList();
2012 }
2013
2014 append(leftType, string);
2015 indexStartedPara = false;
2016 pendingParaLeftType = leftType;
2017 pendingParaRightType = rightType;
2018 pendingParaString = string;
2019 if (leftType == Atom::SectionHeadingLeft) {
2020 paraState = InSingleLineParagraph;
2021 }
2022 else {
2023 paraState = InMultiLineParagraph;
2024 }
2025 skipSpacesOrOneEndl();
2026 }
2027}
2028
2029void DocParser::leavePara()
2030{
2031 if (paraState != OutsideParagraph) {
2032 if (!pendingFormats.isEmpty()) {
2033 location().warning(tr("Missing '}'"));
2034 pendingFormats.clear();
2035 }
2036
2037 if (priv->text.lastAtom()->type() == pendingParaLeftType) {
2038 priv->text.stripLastAtom();
2039 }
2040 else {
2041 if (priv->text.lastAtom()->type() == Atom::String &&
2042 priv->text.lastAtom()->string().endsWith(" ")) {
2043 priv->text.lastAtom()->chopString();
2044 }
2045 append(pendingParaRightType, pendingParaString);
2046 }
2047 paraState = OutsideParagraph;
2048 indexStartedPara = false;
2049 pendingParaRightType = Atom::Nop;
2050 pendingParaString = "";
2051 }
2052}
2053
2054void DocParser::leaveValue()
2055{
2056 leavePara();
2057 if (openedLists.isEmpty()) {
2058 openedLists.push(OpenedList(OpenedList::Value));
2059 append(Atom::ListLeft, ATOM_LIST_VALUE);
2060 }
2061 else {
2062 if (priv->text.lastAtom()->type() == Atom::Nop)
2063 priv->text.stripLastAtom();
2064 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2065 }
2066}
2067
2068void DocParser::leaveValueList()
2069{
2070 leavePara();
2071 if (!openedLists.isEmpty() &&
2072 (openedLists.top().style() == OpenedList::Value)) {
2073 if (priv->text.lastAtom()->type() == Atom::Nop)
2074 priv->text.stripLastAtom();
2075 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2076 append(Atom::ListRight, ATOM_LIST_VALUE);
2077 openedLists.pop();
2078 }
2079}
2080
2081void DocParser::leaveTableRow()
2082{
2083 if (inTableItem) {
2084 leavePara();
2085 append(Atom::TableItemRight);
2086 inTableItem = false;
2087 }
2088 if (inTableHeader) {
2089 append(Atom::TableHeaderRight);
2090 inTableHeader = false;
2091 }
2092 if (inTableRow) {
2093 append(Atom::TableRowRight);
2094 inTableRow = false;
2095 }
2096}
2097
2098CodeMarker *DocParser::quoteFromFile()
2099{
2100 return Doc::quoteFromFile(location(), quoter, getArgument());
2101}
2102
2103void DocParser::expandMacro(const QString &name,
2104 const QString &def,
2105 int numParams)
2106{
2107 if (numParams == 0) {
2108 append(Atom::RawString, def);
2109 }
2110 else {
2111 QStringList args;
2112 QString rawString;
2113
2114 for (int i = 0; i < numParams; i++) {
2115 if (numParams == 1 || isLeftBraceAhead()) {
2116 args << getArgument(true);
2117 }
2118 else {
2119 location().warning(tr("Macro '\\%1' invoked with too few"
2120 " arguments (expected %2, got %3)")
2121 .arg(name).arg(numParams).arg(i));
2122 break;
2123 }
2124 }
2125
2126 int j = 0;
2127 while (j < def.size()) {
2128 int paramNo;
2129 if (((paramNo = def[j].unicode()) >= 1) &&
2130 (paramNo <= numParams)) {
2131 if (!rawString.isEmpty()) {
2132 append(Atom::RawString, rawString);
2133 rawString = "";
2134 }
2135 append(Atom::String, args[paramNo - 1]);
2136 j += 1;
2137 }
2138 else {
2139 rawString += def[j++];
2140 }
2141 }
2142 if (!rawString.isEmpty())
2143 append(Atom::RawString, rawString);
2144 }
2145}
2146
2147QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
2148{
2149 if (numParams == 0) {
2150 return def;
2151 }
2152 else {
2153 QStringList args;
2154 QString rawString;
2155
2156 for (int i = 0; i < numParams; i++) {
2157 if (numParams == 1 || isLeftBraceAhead()) {
2158 args << getArgument(true);
2159 }
2160 else {
2161 location().warning(tr("Macro '\\%1' invoked with too few"
2162 " arguments (expected %2, got %3)")
2163 .arg(name).arg(numParams).arg(i));
2164 break;
2165 }
2166 }
2167
2168 int j = 0;
2169 while (j < def.size()) {
2170 int paramNo;
2171 if (((paramNo = def[j].unicode()) >= 1) &&
2172 (paramNo <= numParams)) {
2173 rawString += args[paramNo - 1];
2174 j += 1;
2175 }
2176 else {
2177 rawString += def[j++];
2178 }
2179 }
2180 return rawString;
2181 }
2182}
2183
2184Doc::Sections DocParser::getSectioningUnit()
2185{
2186 QString name = getOptionalArgument();
2187
2188 if (name == "part") {
2189 return Doc::Part;
2190 }
2191 else if (name == "chapter") {
2192 return Doc::Chapter;
2193 }
2194 else if (name == "section1") {
2195 return Doc::Section1;
2196 }
2197 else if (name == "section2") {
2198 return Doc::Section2;
2199 }
2200 else if (name == "section3") {
2201 return Doc::Section3;
2202 }
2203 else if (name == "section4") {
2204 return Doc::Section4;
2205 }
2206 else if (name.isEmpty()) {
2207 return Doc::NoSection;
2208 }
2209 else {
2210 location().warning(tr("Invalid section '%1'").arg(name));
2211 return Doc::NoSection;
2212 }
2213}
2214
2215QString DocParser::getArgument(bool verbatim)
2216{
2217 QString arg;
2218 int delimDepth = 0;
2219
2220 skipSpacesOrOneEndl();
2221
2222 int startPos = pos;
2223
2224 /*
2225 Typically, an argument ends at the next white-space. However,
2226 braces can be used to group words:
2227
2228 {a few words}
2229
2230 Also, opening and closing parentheses have to match. Thus,
2231
2232 printf("%d\n", x)
2233
2234 is an argument too, although it contains spaces. Finally,
2235 trailing punctuation is not included in an argument, nor is 's.
2236 */
2237 if (pos < (int) in.length() && in[pos] == '{') {
2238 pos++;
2239 while (pos < (int) in.length() && delimDepth >= 0) {
2240 switch (in[pos].unicode()) {
2241 case '{':
2242 delimDepth++;
2243 arg += "{";
2244 pos++;
2245 break;
2246 case '}':
2247 delimDepth--;
2248 if (delimDepth >= 0)
2249 arg += "}";
2250 pos++;
2251 break;
2252 case '\\':
2253 if (verbatim) {
2254 arg += in[pos];
2255 pos++;
2256 }
2257 else {
2258 pos++;
2259 if (pos < (int) in.length()) {
2260 if (in[pos].isLetterOrNumber())
2261 break;
2262 arg += in[pos];
2263 if (in[pos].isSpace()) {
2264 skipAllSpaces();
2265 }
2266 else {
2267 pos++;
2268 }
2269 }
2270 }
2271 break;
2272 default:
2273 arg += in[pos];
2274 pos++;
2275 }
2276 }
2277 if (delimDepth > 0)
2278 location().warning(tr("Missing '}'"));
2279 }
2280 else {
2281 while ((pos < in.length()) &&
2282 ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) {
2283 switch (in[pos].unicode()) {
2284 case '(':
2285 case '[':
2286 case '{':
2287 delimDepth++;
2288 arg += in[pos];
2289 pos++;
2290 break;
2291 case ')':
2292 case ']':
2293 case '}':
2294 delimDepth--;
2295 if (pos == startPos || delimDepth >= 0) {
2296 arg += in[pos];
2297 pos++;
2298 }
2299 break;
2300 case '\\':
2301 if (verbatim) {
2302 arg += in[pos];
2303 pos++;
2304 }
2305 else {
2306 pos++;
2307 if (pos < (int) in.length()) {
2308 if (in[pos].isLetterOrNumber())
2309 break;
2310 arg += in[pos];
2311 if (in[pos].isSpace()) {
2312 skipAllSpaces();
2313 }
2314 else {
2315 pos++;
2316 }
2317 }
2318 }
2319 break;
2320 default:
2321 arg += in[pos];
2322 pos++;
2323 }
2324 }
2325 if ((arg.length() > 1) &&
2326 (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
2327 !arg.endsWith("...")) {
2328 arg.truncate(arg.length() - 1);
2329 pos--;
2330 }
2331 if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
2332 arg.truncate(arg.length() - 2);
2333 pos -= 2;
2334 }
2335 }
2336 return arg.simplified();
2337}
2338
2339QString DocParser::getOptionalArgument()
2340{
2341 skipSpacesOrOneEndl();
2342 if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
2343 in[pos + 1].isLetterOrNumber()) {
2344 return "";
2345 }
2346 else {
2347 return getArgument();
2348 }
2349}
2350
2351QString DocParser::getRestOfLine()
2352{
2353 QString t;
2354
2355 skipSpacesOnLine();
2356
2357 bool trailingSlash = false;
2358
2359 do {
2360 int begin = pos;
2361
2362 while (pos < in.size() && in[pos] != '\n') {
2363 if (in[pos] == '\\' && !trailingSlash) {
2364 trailingSlash = true;
2365 ++pos;
2366 while ((pos < in.size()) &&
2367 in[pos].isSpace() &&
2368 (in[pos] != '\n'))
2369 ++pos;
2370 }
2371 else {
2372 trailingSlash = false;
2373 ++pos;
2374 }
2375 }
2376
2377 if (!t.isEmpty())
2378 t += " ";
2379 t += in.mid(begin, pos - begin).simplified();
2380
2381 if (trailingSlash) {
2382 t.chop(1);
2383 t = t.simplified();
2384 }
2385 if (pos < in.size())
2386 ++pos;
2387 } while (pos < in.size() && trailingSlash);
2388
2389 return t;
2390}
2391
2392/*!
2393 The metacommand argument is normally the remaining text to
2394 the right of the metacommand itself. The extra blanks are
2395 stripped and the argument string is returned.
2396 */
2397QString DocParser::getMetaCommandArgument(const QString &cmdStr)
2398{
2399 skipSpacesOnLine();
2400
2401 int begin = pos;
2402 int parenDepth = 0;
2403
2404 while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
2405 if (in.at(pos) == '(')
2406 ++parenDepth;
2407 else if (in.at(pos) == ')')
2408 --parenDepth;
2409
2410 ++pos;
2411 }
2412 if (pos == in.size() && parenDepth > 0) {
2413 pos = begin;
2414 location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
2415 }
2416
2417 QString t = in.mid(begin, pos - begin).simplified();
2418 skipSpacesOnLine();
2419 return t;
2420}
2421
2422QString DocParser::getUntilEnd(int cmd)
2423{
2424 int endCmd = endCmdFor(cmd);
2425 QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
2426 QString t;
2427 int end = rx.indexIn(in, pos);
2428
2429 if (end == -1) {
2430 location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
2431 pos = in.length();
2432 }
2433 else {
2434 t = in.mid(pos, end - pos);
2435 pos = end + rx.matchedLength();
2436 }
2437 return t;
2438}
2439
2440QString DocParser::getCode(int cmd, CodeMarker *marker)
2441{
2442 QString code = untabifyEtc(getUntilEnd(cmd));
2443 int indent = indentLevel(code);
2444 if (indent < minIndent)
2445 minIndent = indent;
2446 code = unindent(minIndent, code);
2447 if (!marker)
2448 marker = CodeMarker::markerForCode(code);
2449 return marker->markedUpCode(code, 0, location());
2450}
2451
2452/*!
2453 Was used only for generating doxygen output.
2454 */
2455QString DocParser::getUnmarkedCode(int cmd)
2456{
2457 QString code = getUntilEnd(cmd);
2458 return code;
2459}
2460
2461bool DocParser::isBlankLine()
2462{
2463 int i = pos;
2464
2465 while (i < len && in[i].isSpace()) {
2466 if (in[i] == '\n')
2467 return true;
2468 i++;
2469 }
2470 return false;
2471}
2472
2473bool DocParser::isLeftBraceAhead()
2474{
2475 int numEndl = 0;
2476 int i = pos;
2477
2478 while (i < len && in[i].isSpace() && numEndl < 2) {
2479 // ### bug with '\\'
2480 if (in[i] == '\n')
2481 numEndl++;
2482 i++;
2483 }
2484 return numEndl < 2 && i < len && in[i] == '{';
2485}
2486
2487/*!
2488 Skips to the next non-space character or EOL.
2489 */
2490void DocParser::skipSpacesOnLine()
2491{
2492 while ((pos < in.length()) &&
2493 in[pos].isSpace() &&
2494 (in[pos].unicode() != '\n'))
2495 ++pos;
2496}
2497
2498/*!
2499 Skips spaces and on EOL.
2500 */
2501void DocParser::skipSpacesOrOneEndl()
2502{
2503 int firstEndl = -1;
2504 while (pos < (int) in.length() && in[pos].isSpace()) {
2505 QChar ch = in[pos];
2506 if (ch == '\n') {
2507 if (firstEndl == -1) {
2508 firstEndl = pos;
2509 }
2510 else {
2511 pos = firstEndl;
2512 break;
2513 }
2514 }
2515 pos++;
2516 }
2517}
2518
2519void DocParser::skipAllSpaces()
2520{
2521 while (pos < len && in[pos].isSpace())
2522 pos++;
2523}
2524
2525void DocParser::skipToNextPreprocessorCommand()
2526{
2527 QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + "|" +
2528 cmdName(CMD_ELSE) + "|" +
2529 cmdName(CMD_ENDIF) + ")\\b");
2530 int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
2531
2532 if (end == -1)
2533 pos = in.length();
2534 else
2535 pos = end;
2536}
2537
2538int DocParser::endCmdFor(int cmd)
2539{
2540 switch (cmd) {
2541 case CMD_ABSTRACT:
2542 return CMD_ENDABSTRACT;
2543 case CMD_BADCODE:
2544 return CMD_ENDCODE;
2545 case CMD_CHAPTER:
2546 return CMD_ENDCHAPTER;
2547 case CMD_CODE:
2548 return CMD_ENDCODE;
2549 case CMD_DIV:
2550 return CMD_ENDDIV;
2551 case CMD_QML:
2552 return CMD_ENDQML;
2553 case CMD_QMLTEXT:
2554 return CMD_ENDQMLTEXT;
2555 case CMD_JS:
2556 return CMD_ENDJS;
2557 case CMD_FOOTNOTE:
2558 return CMD_ENDFOOTNOTE;
2559 case CMD_LEGALESE:
2560 return CMD_ENDLEGALESE;
2561 case CMD_LINK:
2562 return CMD_ENDLINK;
2563 case CMD_LIST:
2564 return CMD_ENDLIST;
2565 case CMD_NEWCODE:
2566 return CMD_ENDCODE;
2567 case CMD_OLDCODE:
2568 return CMD_NEWCODE;
2569 case CMD_OMIT:
2570 return CMD_ENDOMIT;
2571 case CMD_PART:
2572 return CMD_ENDPART;
2573 case CMD_QUOTATION:
2574 return CMD_ENDQUOTATION;
2575 case CMD_RAW:
2576 return CMD_ENDRAW;
2577 case CMD_SECTION1:
2578 return CMD_ENDSECTION1;
2579 case CMD_SECTION2:
2580 return CMD_ENDSECTION2;
2581 case CMD_SECTION3:
2582 return CMD_ENDSECTION3;
2583 case CMD_SECTION4:
2584 return CMD_ENDSECTION4;
2585 case CMD_SIDEBAR:
2586 return CMD_ENDSIDEBAR;
2587 case CMD_TABLE:
2588 return CMD_ENDTABLE;
2589 default:
2590 return cmd;
2591 }
2592}
2593
2594QString DocParser::cmdName(int cmd)
2595{
2596 return *cmds[cmd].alias;
2597}
2598
2599QString DocParser::endCmdName(int cmd)
2600{
2601 return cmdName(endCmdFor(cmd));
2602}
2603
2604QString DocParser::untabifyEtc(const QString& str)
2605{
2606 QString result;
2607 result.reserve(str.length());
2608 int column = 0;
2609
2610 for (int i = 0; i < str.length(); i++) {
2611 const QChar c = str.at(i);
2612 if (c == QLatin1Char('\r'))
2613 continue;
2614 if (c == QLatin1Char('\t')) {
2615 result += " " + (column % tabSize);
2616 column = ((column / tabSize) + 1) * tabSize;
2617 continue;
2618 }
2619 if (c == QLatin1Char('\n')) {
2620 while (result.endsWith(QLatin1Char(' ')))
2621 result.chop(1);
2622 result += c;
2623 column = 0;
2624 continue;
2625 }
2626 result += c;
2627 column++;
2628 }
2629
2630 while (result.endsWith("\n\n"))
2631 result.truncate(result.length() - 1);
2632 while (result.startsWith("\n"))
2633 result = result.mid(1);
2634
2635 return result;
2636}
2637
2638int DocParser::indentLevel(const QString& str)
2639{
2640 int minIndent = INT_MAX;
2641 int column = 0;
2642
2643 for (int i = 0; i < (int) str.length(); i++) {
2644 if (str[i] == '\n') {
2645 column = 0;
2646 }
2647 else {
2648 if (str[i] != ' ' && column < minIndent)
2649 minIndent = column;
2650 column++;
2651 }
2652 }
2653 return minIndent;
2654}
2655
2656QString DocParser::unindent(int level, const QString& str)
2657{
2658 if (level == 0)
2659 return str;
2660
2661 QString t;
2662 int column = 0;
2663
2664 for (int i = 0; i < (int) str.length(); i++) {
2665 if (str[i] == QLatin1Char('\n')) {
2666 t += '\n';
2667 column = 0;
2668 }
2669 else {
2670 if (column >= level)
2671 t += str[i];
2672 column++;
2673 }
2674 }
2675 return t;
2676}
2677
2678QString DocParser::slashed(const QString& str)
2679{
2680 QString result = str;
2681 result.replace("/", "\\/");
2682 return "/" + result + "/";
2683}
2684
2685#define COMMAND_BRIEF Doc::alias("brief")
2686#define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
2687
2688Doc::Doc(const Location& start_loc,
2689 const Location& end_loc,
2690 const QString& source,
2691 const QSet<QString>& metaCommandSet)
2692{
2693 priv = new DocPrivate(start_loc,end_loc,source);
2694 DocParser parser;
2695 parser.parse(source,priv,metaCommandSet);
2696}
2697
2698Doc::Doc(const Doc& doc)
2699 : priv(0)
2700{
2701 operator=(doc);
2702}
2703
2704Doc::~Doc()
2705{
2706 if (priv && priv->deref())
2707 delete priv;
2708}
2709
2710Doc &Doc::operator=(const Doc& doc)
2711{
2712 if (doc.priv)
2713 doc.priv->ref();
2714 if (priv && priv->deref())
2715 delete priv;
2716 priv = doc.priv;
2717 return *this;
2718}
2719
2720void Doc::renameParameters(const QStringList &oldNames,
2721 const QStringList &newNames)
2722{
2723 if (priv && oldNames != newNames) {
2724 detach();
2725
2726 priv->params = newNames.toSet();
2727
2728 Atom *atom = priv->text.firstAtom();
2729 while (atom) {
2730 if (atom->type() == Atom::FormattingLeft
2731 && atom->string() == ATOM_FORMATTING_PARAMETER) {
2732 atom = atom->next();
2733 if (!atom)
2734 return;
2735 int index = oldNames.indexOf(atom->string());
2736 if (index != -1 && index < newNames.count())
2737 atom->setString(newNames.at(index));
2738 }
2739 atom = atom->next();
2740 }
2741 }
2742}
2743
2744void Doc::simplifyEnumDoc()
2745{
2746 if (priv) {
2747 if (priv->isEnumDocSimplifiable()) {
2748 detach();
2749
2750 Text newText;
2751
2752 Atom *atom = priv->text.firstAtom();
2753 while (atom) {
2754 if ((atom->type() == Atom::ListLeft) &&
2755 (atom->string() == ATOM_LIST_VALUE)) {
2756 while (atom && ((atom->type() != Atom::ListRight) ||
2757 (atom->string() != ATOM_LIST_VALUE)))
2758 atom = atom->next();
2759 if (atom)
2760 atom = atom->next();
2761 }
2762 else {
2763 newText << *atom;
2764 atom = atom->next();
2765 }
2766 }
2767 priv->text = newText;
2768 }
2769 }
2770}
2771
2772void Doc::setBody(const Text &text)
2773{
2774 detach();
2775 priv->text = text;
2776}
2777
2778/*!
2779 Returns the starting location of a qdoc comment.
2780 */
2781const Location &Doc::location() const
2782{
2783 static const Location dummy;
2784 return priv == 0 ? dummy : priv->start_loc;
2785}
2786
2787const QString &Doc::source() const
2788{
2789 static QString null;
2790 return priv == 0 ? null : priv->src;
2791}
2792
2793bool Doc::isEmpty() const
2794{
2795 return priv == 0 || priv->src.isEmpty();
2796}
2797
2798const Text& Doc::body() const
2799{
2800 static const Text dummy;
2801 return priv == 0 ? dummy : priv->text;
2802}
2803
2804Text Doc::briefText(bool inclusive) const
2805{
2806 return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
2807}
2808
2809Text Doc::trimmedBriefText(const QString &className) const
2810{
2811 QString classNameOnly = className;
2812 if (className.contains("::"))
2813 classNameOnly = className.split("::").last();
2814
2815 Text originalText = briefText();
2816 Text resultText;
2817 const Atom *atom = originalText.firstAtom();
2818 if (atom) {
2819 QString briefStr;
2820 QString whats;
2821 bool standardWording = true;
2822
2823 /*
2824 This code is really ugly. The entire \brief business
2825 should be rethought.
2826 */
2827 while (atom) {
2828 if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
2829 briefStr += atom->string();
2830 }
2831 atom = atom->next();
2832 }
2833
2834 QStringList w = briefStr.split(" ");
2835 if (!w.isEmpty() && w.first() == "Returns") {
2836 }
2837 else {
2838 if (!w.isEmpty() && w.first() == "The")
2839 w.removeFirst();
2840 else {
2841 location().warning(
2842 tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
2843 .arg(COMMAND_BRIEF).arg(className));
2844 standardWording = false;
2845 }
2846
2847 if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
2848 w.removeFirst();
2849 else {
2850 location().warning(
2851 tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
2852 .arg(COMMAND_BRIEF).arg(className).arg(className));
2853 standardWording = false;
2854 }
2855
2856 if (!w.isEmpty() && ((w.first() == "class") ||
2857 (w.first() == "function") ||
2858 (w.first() == "macro") ||
2859 (w.first() == "widget") ||
2860 (w.first() == "namespace") ||
2861 (w.first() == "header")))
2862 w.removeFirst();
2863 else {
2864 location().warning(
2865 tr("Nonstandard wording in '\\%1' text for '%2' ("
2866 "expected 'class', 'function', 'macro', 'widget', "
2867 "'namespace' or 'header')")
2868 .arg(COMMAND_BRIEF).arg(className));
2869 standardWording = false;
2870 }
2871
2872 if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
2873 w.removeFirst();
2874
2875 if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
2876 w.removeFirst();
2877 }
2878
2879 whats = w.join(" ");
2880
2881 if (whats.endsWith("."))
2882 whats.truncate(whats.length() - 1);
2883
2884 if (whats.isEmpty()) {
2885 location().warning(
2886 tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
2887 .arg(COMMAND_BRIEF).arg(className));
2888 standardWording = false;
2889 }
2890 else
2891 whats[0] = whats[0].toUpper();
2892
2893 // ### move this once \brief is abolished for properties
2894 if (standardWording)
2895 resultText << whats;
2896 }
2897 return resultText;
2898}
2899
2900Text Doc::legaleseText() const
2901{
2902 if (priv == 0 || !priv->hasLegalese)
2903 return Text();
2904 else
2905 return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
2906}
2907
2908const QString& Doc::baseName() const
2909{
2910 static QString null;
2911 if (priv == 0 || priv->extra == 0) {
2912 return null;
2913 }
2914 else {
2915 return priv->extra->baseName;
2916 }
2917}
2918
2919Doc::Sections Doc::granularity() const
2920{
2921 if (priv == 0 || priv->extra == 0) {
2922 return DocPrivateExtra().granularity;
2923 }
2924 else {
2925 return priv->extra->granularity;
2926 }
2927}
2928
2929const QSet<QString> &Doc::parameterNames() const
2930{
2931 return priv == 0 ? *null_Set_QString() : priv->params;
2932}
2933
2934const QStringList &Doc::enumItemNames() const
2935{
2936 return priv == 0 ? *null_QStringList() : priv->enumItemList;
2937}
2938
2939const QStringList &Doc::omitEnumItemNames() const
2940{
2941 return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
2942}
2943
2944const QSet<QString> &Doc::metaCommandsUsed() const
2945{
2946 return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
2947}
2948
2949QStringList Doc::metaCommandArgs(const QString& metacommand) const
2950{
2951 return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand);
2952}
2953
2954const QList<Text> &Doc::alsoList() const
2955{
2956 return priv == 0 ? *null_QList_Text() : priv->alsoList;
2957}
2958
2959bool Doc::hasTableOfContents() const
2960{
2961 return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
2962}
2963
2964bool Doc::hasKeywords() const
2965{
2966 return priv && priv->extra && !priv->extra->keywords.isEmpty();
2967}
2968
2969bool Doc::hasTargets() const
2970{
2971 return priv && priv->extra && !priv->extra->targets.isEmpty();
2972}
2973
2974const QList<Atom *> &Doc::tableOfContents() const
2975{
2976 priv->constructExtra();
2977 return priv->extra->tableOfContents;
2978}
2979
2980const QList<int> &Doc::tableOfContentsLevels() const
2981{
2982 priv->constructExtra();
2983 return priv->extra->tableOfContentsLevels;
2984}
2985
2986const QList<Atom *> &Doc::keywords() const
2987{
2988 priv->constructExtra();
2989 return priv->extra->keywords;
2990}
2991
2992const QList<Atom *> &Doc::targets() const
2993{
2994 priv->constructExtra();
2995 return priv->extra->targets;
2996}
2997
2998const QStringMultiMap &Doc::metaTagMap() const
2999{
3000 return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
3001}
3002
3003void Doc::initialize(const Config& config)
3004{
3005 DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
3006 DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES);
3007 DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
3008 DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES);
3009 DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS);
3010 DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
3011
3012 QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
3013
3014 QStringMap reverseAliasMap;
3015
3016 QSet<QString> commands = config.subVars(CONFIG_ALIAS);
3017 QSet<QString>::ConstIterator c = commands.begin();
3018 while (c != commands.end()) {
3019 QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
3020 if (reverseAliasMap.contains(alias)) {
3021 config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
3022 " for both '\\%2' and '\\%3'")
3023 .arg(alias)
3024 .arg(reverseAliasMap[alias])
3025 .arg(*c));
3026 }
3027 else {
3028 reverseAliasMap.insert(alias, *c);
3029 }
3030 aliasMap()->insert(*c, alias);
3031 ++c;
3032 }
3033
3034 int i = 0;
3035 while (cmds[i].english) {
3036 cmds[i].alias = new QString(alias(cmds[i].english));
3037 cmdHash()->insert(*cmds[i].alias, cmds[i].no);
3038
3039 if (cmds[i].no != i)
3040 Location::internalError(tr("command %1 missing").arg(i));
3041 i++;
3042 }
3043
3044 QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
3045 QSet<QString>::ConstIterator n = macroNames.begin();
3046 while (n != macroNames.end()) {
3047 QString macroDotName = CONFIG_MACRO + Config::dot + *n;
3048 Macro macro;
3049 macro.numParams = -1;
3050 macro.defaultDef = config.getString(macroDotName);
3051 if (!macro.defaultDef.isEmpty()) {
3052 macro.defaultDefLocation = config.lastLocation();
3053 macro.numParams = Config::numParams(macro.defaultDef);
3054 }
3055 bool silent = false;
3056
3057 QSet<QString> formats = config.subVars(macroDotName);
3058 QSet<QString>::ConstIterator f = formats.begin();
3059 while (f != formats.end()) {
3060 QString def = config.getString(macroDotName + Config::dot + *f);
3061 if (!def.isEmpty()) {
3062 macro.otherDefs.insert(*f, def);
3063 int m = Config::numParams(def);
3064 if (macro.numParams == -1) {
3065 macro.numParams = m;
3066 }
3067 else if (macro.numParams != m) {
3068 if (!silent) {
3069 QString other = tr("default");
3070 if (macro.defaultDef.isEmpty())
3071 other = macro.otherDefs.begin().key();
3072 config.lastLocation().warning(tr("Macro '\\%1' takes"
3073 " inconsistent number"
3074 " of arguments (%2"
3075 " %3, %4 %5)")
3076 .arg(*n)
3077 .arg(*f)
3078 .arg(m)
3079 .arg(other)
3080 .arg(macro.numParams));
3081 silent = true;
3082 }
3083 if (macro.numParams < m)
3084 macro.numParams = m;
3085 }
3086 }
3087 ++f;
3088 }
3089
3090 if (macro.numParams != -1)
3091 macroHash()->insert(*n, macro);
3092 ++n;
3093 }
3094}
3095
3096void Doc::terminate()
3097{
3098 DocParser::exampleFiles.clear();
3099 DocParser::exampleDirs.clear();
3100 DocParser::sourceFiles.clear();
3101 DocParser::sourceDirs.clear();
3102 aliasMap()->clear();
3103 cmdHash()->clear();
3104 macroHash()->clear();
3105
3106 int i = 0;
3107 while (cmds[i].english) {
3108 delete cmds[i].alias;
3109 cmds[i].alias = 0;
3110 ++i;
3111 }
3112}
3113
3114QString Doc::alias(const QString &english)
3115{
3116 return aliasMap()->value(english, english);
3117}
3118
3119/*!
3120 Trims the deadwood out of \a str. i.e., this function
3121 cleans up \a str.
3122 */
3123void Doc::trimCStyleComment(Location& location, QString& str)
3124{
3125 QString cleaned;
3126 Location m = location;
3127 bool metAsterColumn = true;
3128 int asterColumn = location.columnNo() + 1;
3129 int i;
3130
3131 for (i = 0; i < (int) str.length(); i++) {
3132 if (m.columnNo() == asterColumn) {
3133 if (str[i] != '*')
3134 break;
3135 cleaned += ' ';
3136 metAsterColumn = true;
3137 }
3138 else {
3139 if (str[i] == '\n') {
3140 if (!metAsterColumn)
3141 break;
3142 metAsterColumn = false;
3143 }
3144 cleaned += str[i];
3145 }
3146 m.advance(str[i]);
3147 }
3148 if (cleaned.length() == str.length())
3149 str = cleaned;
3150
3151 for (int i = 0; i < 3; i++)
3152 location.advance(str[i]);
3153 str = str.mid(3, str.length() - 5);
3154}
3155
3156CodeMarker *Doc::quoteFromFile(const Location &location,
3157 Quoter &quoter,
3158 const QString &fileName)
3159{
3160 quoter.reset();
3161
3162 QString code;
3163
3164 QString userFriendlyFilePath;
3165 QString filePath = Config::findFile(location,
3166 DocParser::exampleFiles,
3167 DocParser::exampleDirs,
3168 fileName, userFriendlyFilePath);
3169 if (filePath.isEmpty()) {
3170 location.warning(tr("Cannot find example file '%1'").arg(fileName));
3171 }
3172 else {
3173 QFile inFile(filePath);
3174 if (!inFile.open(QFile::ReadOnly)) {
3175 location.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath));
3176 }
3177 else {
3178 QTextStream inStream(&inFile);
3179 code = DocParser::untabifyEtc(inStream.readAll());
3180 }
3181 }
3182
3183 QString dirPath = QFileInfo(filePath).path();
3184 CodeMarker *marker = CodeMarker::markerForFileName(fileName);
3185 quoter.quoteFromFile(userFriendlyFilePath,
3186 code,
3187 marker->markedUpCode(code, 0, location));
3188 return marker;
3189}
3190
3191QString Doc::canonicalTitle(const QString &title)
3192{
3193 // The code below is equivalent to the following chunk, but _much_
3194 // faster (accounts for ~10% of total running time)
3195 //
3196 // QRegExp attributeExpr("[^A-Za-z0-9]+");
3197 // QString result = title.toLower();
3198 // result.replace(attributeExpr, " ");
3199 // result = result.simplified();
3200 // result.replace(QLatin1Char(' '), QLatin1Char('-'));
3201
3202 QString result;
3203 result.reserve(title.size());
3204
3205 bool dashAppended = false;
3206 bool begun = false;
3207 int lastAlnum = 0;
3208 for (int i = 0; i != title.size(); ++i) {
3209 uint c = title.at(i).unicode();
3210 if (c >= 'A' && c <= 'Z')
3211 c -= 'A' - 'a';
3212 bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
3213 if (alnum) {
3214 result += QLatin1Char(c);
3215 begun = true;
3216 dashAppended = false;
3217 lastAlnum = result.size();
3218 }
3219 else if (!dashAppended) {
3220 if (begun)
3221 result += QLatin1Char('-');
3222 dashAppended = true;
3223 }
3224 }
3225 result.truncate(lastAlnum);
3226 return result;
3227}
3228
3229void Doc::detach()
3230{
3231 if (!priv) {
3232 priv = new DocPrivate;
3233 return;
3234 }
3235 if (priv->count == 1)
3236 return;
3237
3238 --priv->count;
3239
3240 DocPrivate *newPriv = new DocPrivate(*priv);
3241 newPriv->count = 1;
3242 if (priv->extra)
3243 newPriv->extra = new DocPrivateExtra(*priv->extra);
3244
3245 priv = newPriv;
3246}
3247
3248QT_END_NAMESPACE
3249

Warning: That file was not part of the compilation database. It may have many parsing errors.