source: trunk/tools/qdoc3/doc.cpp@ 865

Last change on this file since 865 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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