1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #include "codemarker.h"
45 #include "editdistance.h"
46 #include "openedlist.h"
49 #include "tokenizer.h"
50 #include <qdatetime.h>
52 #include <qfileinfo.h>
54 #include <qtextstream.h>
62 Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
63 Q_GLOBAL_STATIC(TopicList, nullTopicList)
64 Q_GLOBAL_STATIC(QStringList, null_QStringList)
65 Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
66 //Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
67 Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap)
72 Location defaultDefLocation;
197 { "abstract", CMD_ABSTRACT, 0 },
198 { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
200 { "badcode", CMD_BADCODE, 0 },
201 { "basename", CMD_BASENAME, 0 }, // ### don't document for now
202 { "bold", CMD_BOLD, 0 },
204 { "brief", CMD_BRIEF, 0 },
206 { "caption", CMD_CAPTION, 0 },
207 { "chapter", CMD_CHAPTER, 0 },
208 { "code", CMD_CODE, 0 },
209 { "codeline", CMD_CODELINE, 0},
210 { "div", CMD_DIV, 0 },
211 { "dots", CMD_DOTS, 0 },
213 { "else", CMD_ELSE, 0 },
214 { "endabstract", CMD_ENDABSTRACT, 0 },
215 { "endchapter", CMD_ENDCHAPTER, 0 },
216 { "endcode", CMD_ENDCODE, 0 },
217 { "enddiv", CMD_ENDDIV, 0 },
218 { "endfootnote", CMD_ENDFOOTNOTE, 0 },
219 { "endif", CMD_ENDIF, 0 },
220 { "endlegalese", CMD_ENDLEGALESE, 0 },
221 { "endlink", CMD_ENDLINK, 0 },
222 { "endlist", CMD_ENDLIST, 0 },
223 { "endmapref", CMD_ENDMAPREF, 0 },
224 { "endomit", CMD_ENDOMIT, 0 },
225 { "endpart", CMD_ENDPART, 0 },
226 { "endquotation", CMD_ENDQUOTATION, 0 },
227 { "endraw", CMD_ENDRAW, 0 },
228 { "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
229 { "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now
230 { "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now
231 { "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now
232 { "endsidebar", CMD_ENDSIDEBAR, 0 },
233 { "endtable", CMD_ENDTABLE, 0 },
234 { "endtopicref", CMD_ENDTOPICREF, 0 },
235 { "footnote", CMD_FOOTNOTE, 0 },
236 { "generatelist", CMD_GENERATELIST, 0 },
237 { "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
238 { "header", CMD_HEADER, 0 },
242 { "image", CMD_IMAGE, 0 },
243 { "important", CMD_IMPORTANT, 0 },
244 { "include", CMD_INCLUDE, 0 },
245 { "inlineimage", CMD_INLINEIMAGE, 0 },
246 { "index", CMD_INDEX, 0 }, // ### don't document for now
247 { "keyword", CMD_KEYWORD, 0 },
249 { "legalese", CMD_LEGALESE, 0 },
251 { "link", CMD_LINK, 0 },
252 { "list", CMD_LIST, 0 },
253 { "mapref", CMD_MAPREF, 0 },
254 { "meta", CMD_META, 0 },
255 { "newcode", CMD_NEWCODE, 0 },
256 { "note", CMD_NOTE, 0 },
258 { "oldcode", CMD_OLDCODE, 0 },
259 { "omit", CMD_OMIT, 0 },
260 { "omitvalue", CMD_OMITVALUE, 0 },
261 { "overload", CMD_OVERLOAD, 0 },
262 { "part", CMD_PART, 0 },
263 { "printline", CMD_PRINTLINE, 0 },
264 { "printto", CMD_PRINTTO, 0 },
265 { "printuntil", CMD_PRINTUNTIL, 0 },
266 { "quotation", CMD_QUOTATION, 0 },
267 { "quotefile", CMD_QUOTEFILE, 0 },
268 { "quotefromfile", CMD_QUOTEFROMFILE, 0 },
269 { "quotefunction", CMD_QUOTEFUNCTION, 0 },
270 { "raw", CMD_RAW, 0 },
271 { "row", CMD_ROW, 0 },
273 { "section1", CMD_SECTION1, 0 },
274 { "section2", CMD_SECTION2, 0 },
275 { "section3", CMD_SECTION3, 0 },
276 { "section4", CMD_SECTION4, 0 },
277 { "sidebar", CMD_SIDEBAR, 0 },
278 { "sincelist", CMD_SINCELIST, 0 },
279 { "skipline", CMD_SKIPLINE, 0 },
280 { "skipto", CMD_SKIPTO, 0 },
281 { "skipuntil", CMD_SKIPUNTIL, 0 },
282 { "snippet", CMD_SNIPPET, 0 },
283 { "span", CMD_SPAN, 0 },
284 { "sub", CMD_SUB, 0 },
285 { "sup", CMD_SUP, 0 },
286 { "table", CMD_TABLE, 0 },
287 { "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
288 { "target", CMD_TARGET, 0 },
289 { "topicref", CMD_TOPICREF, 0 },
291 { "uicontrol", CMD_UICONTROL, 0 },
292 { "underline", CMD_UNDERLINE, 0 },
293 { "unicode", CMD_UNICODE, 0 },
294 { "value", CMD_VALUE, 0 },
295 { "warning", CMD_WARNING, 0 },
296 { "qml", CMD_QML, 0 },
297 { "endqml", CMD_ENDQML, 0 },
298 { "cpp", CMD_CPP, 0 },
299 { "endcpp", CMD_ENDCPP, 0 },
300 { "qmltext", CMD_QMLTEXT, 0 },
301 { "endqmltext", CMD_ENDQMLTEXT, 0 },
302 { "cpptext", CMD_CPPTEXT, 0 },
303 { "endcpptext", CMD_ENDCPPTEXT, 0 },
305 { "endjs", CMD_ENDJS, 0 },
309 typedef QHash<QString, int> QHash_QString_int;
310 typedef QHash<QString, Macro> QHash_QString_Macro;
312 Q_GLOBAL_STATIC(QStringMap, aliasMap)
313 Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
314 Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
316 class DocPrivateExtra
320 Doc::Sections granularity;
321 Doc::Sections section; // ###
322 QList<Atom*> tableOfContents;
323 QList<int> tableOfContentsLevels;
324 QList<Atom*> keywords;
325 QList<Atom*> targets;
326 QStringMultiMap metaMap;
329 : granularity(Doc::Part) { }
332 struct Shared // ### get rid of
336 void ref() { ++count; }
337 bool deref() { return (--count == 0); }
342 static QString cleanLink(const QString &link)
344 int colonPos = link.indexOf(':');
345 if ((colonPos == -1) ||
346 (!link.startsWith("file:") && !link.startsWith("mailto:")))
348 return link.mid(colonPos + 1).simplified();
351 typedef QMap<QString, ArgList> CommandMap;
353 class DocPrivate : public Shared
356 DocPrivate(const Location& start = Location::null,
357 const Location& end = Location::null,
358 const QString& source = "");
361 void addAlso(const Text& also);
362 void constructExtra();
363 bool isEnumDocSimplifiable() const;
365 // ### move some of this in DocPrivateExtra
370 QSet<QString> params;
371 QList<Text> alsoList;
372 QStringList enumItemList;
373 QStringList omitEnumItemList;
374 QSet<QString> metacommandsUsed;
375 CommandMap metaCommandMap;
376 bool hasLegalese : 1;
377 bool hasSectioningUnits : 1;
378 DocPrivateExtra *extra;
380 DitaRefList ditamap_;
383 DocPrivate::DocPrivate(const Location& start,
385 const QString& source)
390 hasSectioningUnits(false),
397 If the doc is a ditamap, the destructor deletes each element
398 in the ditamap structure. These were allocated as needed.
400 DocPrivate::~DocPrivate()
403 foreach (DitaRef* t, ditamap_) {
408 void DocPrivate::addAlso(const Text& also)
410 alsoList.append(also);
413 void DocPrivate::constructExtra()
416 extra = new DocPrivateExtra;
419 bool DocPrivate::isEnumDocSimplifiable() const
421 bool justMetColon = false;
422 int numValueTables = 0;
424 const Atom *atom = text.firstAtom();
426 if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
427 justMetColon = atom->string().endsWith(QLatin1Char(':'));
429 else if ((atom->type() == Atom::ListLeft) &&
430 (atom->string() == ATOM_LIST_VALUE)) {
431 if (justMetColon || numValueTables > 0)
443 void parse(const QString &source,
444 DocPrivate *docPrivate,
445 const QSet<QString> &metaCommandSet,
446 const QSet<QString>& possibleTopics);
448 static int endCmdFor(int cmd);
449 static QString cmdName(int cmd);
450 static QString endCmdName(int cmd);
451 static QString untabifyEtc(const QString& str);
452 static int indentLevel(const QString& str);
453 static QString unindent(int level, const QString& str);
454 static QString slashed(const QString& str);
457 static QStringList exampleFiles;
458 static QStringList exampleDirs;
459 static QStringList sourceFiles;
460 static QStringList sourceDirs;
464 Location& location();
465 QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
467 void insertBaseName(const QString &baseName);
468 void insertTarget(const QString& target, bool keyword);
469 void include(const QString& fileName, const QString& identifier);
470 void startFormat(const QString& format, int cmd);
471 bool openCommand(int cmd);
472 bool closeCommand(int endCmd);
473 void startSection(Doc::Sections unit, int cmd);
474 void endSection(int unit, int endCmd);
476 void append(Atom::Type type, const QString& string = "");
477 void append(Atom::Type type, const QString& p1, const QString& p2);
478 void appendChar(QChar ch);
479 void appendWord(const QString &word);
480 void appendToCode(const QString &code);
481 void appendToCode(const QString &code, Atom::Type defaultType);
483 void enterPara(Atom::Type leftType = Atom::ParaLeft,
484 Atom::Type rightType = Atom::ParaRight,
485 const QString& string = "");
488 void leaveValueList();
489 void leaveTableRow();
490 CodeMarker *quoteFromFile();
491 void expandMacro(const QString& name, const QString& def, int numParams);
492 QString expandMacroToString(const QString &name, const QString &def, int numParams);
493 Doc::Sections getSectioningUnit();
494 QString getArgument(bool verbatim = false);
495 QString getBracedArgument(bool verbatim);
496 QString getOptionalArgument();
497 QString getRestOfLine();
498 QString getMetaCommandArgument(const QString &cmdStr);
499 QString getUntilEnd(int cmd);
500 QString getCode(int cmd, CodeMarker *marker);
501 QString getUnmarkedCode(int cmd);
504 bool isLeftBraceAhead();
505 void skipSpacesOnLine();
506 void skipSpacesOrOneEndl();
507 void skipAllSpaces();
508 void skipToNextPreprocessorCommand();
510 QStack<int> openedInputs;
519 enum ParagraphState {
521 InSingleLineParagraph,
524 ParagraphState paraState;
528 bool indexStartedPara; // ### rename
529 Atom::Type pendingParaLeftType;
530 Atom::Type pendingParaRightType;
531 QString pendingParaString;
535 Doc::Sections currentSection;
536 QMap<QString, Location> targetMap;
537 QMap<int, QString> pendingFormats;
538 QStack<int> openedCommands;
539 QStack<OpenedList> openedLists;
541 QStack<DitaRef*> ditarefs_;
544 int DocParser::tabSize;
545 QStringList DocParser::exampleFiles;
546 QStringList DocParser::exampleDirs;
547 QStringList DocParser::sourceFiles;
548 QStringList DocParser::sourceDirs;
549 bool DocParser::quoting;
552 Parse the \a source string to build a Text data structure
553 in \a docPrivate. The Text data structure is a linked list
556 \a metaCommandSet is the set of metacommands that may be
557 found in \a source. These metacommands are not markup text
558 commands. They are topic commands and related metacommands.
560 void DocParser::parse(const QString& source,
561 DocPrivate *docPrivate,
562 const QSet<QString>& metaCommandSet,
563 const QSet<QString>& possibleTopics)
568 cachedLoc = docPrivate->start_loc;
571 priv->text << Atom::Nop;
572 priv->topics.clear();
574 paraState = OutsideParagraph;
575 inTableHeader = false;
578 indexStartedPara = false;
579 pendingParaLeftType = Atom::Nop;
580 pendingParaRightType = Atom::Nop;
584 currentSection = Doc::NoSection;
585 openedCommands.push(CMD_OMIT);
588 CodeMarker *marker = 0;
589 Atom *currentLinkAtom = 0;
591 QStack<bool> preprocessorSkipping;
592 int numPreprocessorSkipping = 0;
595 QChar ch = in.at(pos);
597 switch (ch.unicode()) {
604 if (ch.isLetterOrNumber()) {
612 if (cmdStr.isEmpty()) {
615 if (in.at(pos).isSpace()) {
617 appendChar(QLatin1Char(' '));
620 appendChar(in.at(pos++));
625 int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
630 append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
631 append(Atom::String, p1);
632 append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
633 priv->params.insert(p1);
636 if (openCommand(cmd)) {
638 append(Atom::AbstractLeft);
643 append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
647 insertBaseName(getArgument());
654 location().warning(tr("'\\bold' is deprecated. Use '\\b'"));
656 startFormat(ATOM_FORMATTING_BOLD, cmd);
660 enterPara(Atom::BriefLeft, Atom::BriefRight);
664 p1 = untabifyEtc(getArgument(true));
665 marker = CodeMarker::markerForCode(p1);
666 append(Atom::C, marker->markedUpCode(p1, 0, location()));
670 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
673 startSection(Doc::Chapter, cmd);
677 append(Atom::Code, getCode(CMD_CODE, 0));
681 append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
684 append(Atom::QmlText);
688 append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
692 p1 = getArgument(true);
693 append(Atom::DivLeft, p1);
694 openedCommands.push(cmd);
698 append(Atom::DivRight);
704 if (priv->text.lastAtom()->type() == Atom::Code
705 && priv->text.lastAtom()->string().endsWith("\n\n"))
706 priv->text.lastAtom()->chopString();
710 append(Atom::CodeQuoteCommand, cmdStr);
711 append(Atom::CodeQuoteArgument, " ");
718 if (priv->text.lastAtom()->type() == Atom::Code
719 && priv->text.lastAtom()->string().endsWith("\n\n"))
720 priv->text.lastAtom()->chopString();
722 QString arg = getOptionalArgument();
725 indent = arg.toInt();
726 for (int i = 0; i < indent; ++i)
728 appendToCode("...\n");
731 append(Atom::CodeQuoteCommand, cmdStr);
732 QString arg = getOptionalArgument();
735 append(Atom::CodeQuoteArgument, arg);
740 if (preprocessorSkipping.size() > 0) {
741 if (preprocessorSkipping.top()) {
742 --numPreprocessorSkipping;
745 ++numPreprocessorSkipping;
747 preprocessorSkipping.top() = !preprocessorSkipping.top();
748 (void)getRestOfLine(); // ### should ensure that it's empty
749 if (numPreprocessorSkipping)
750 skipToNextPreprocessorCommand();
753 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
756 case CMD_ENDABSTRACT:
757 if (closeCommand(cmd)) {
759 append(Atom::AbstractRight);
763 endSection(Doc::Chapter, cmd);
772 append(Atom::EndQmlText);
777 case CMD_ENDFOOTNOTE:
778 if (closeCommand(cmd)) {
780 append(Atom::FootnoteRight);
781 paraState = InMultiLineParagraph; // ###
785 if (preprocessorSkipping.count() > 0) {
786 if (preprocessorSkipping.pop())
787 --numPreprocessorSkipping;
788 (void)getRestOfLine(); // ### should ensure that it's empty
789 if (numPreprocessorSkipping)
790 skipToNextPreprocessorCommand();
793 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
796 case CMD_ENDLEGALESE:
797 if (closeCommand(cmd)) {
799 append(Atom::LegaleseRight);
803 if (closeCommand(cmd)) {
804 if (priv->text.lastAtom()->type() == Atom::String
805 && priv->text.lastAtom()->string().endsWith(QLatin1Char(' ')))
806 priv->text.lastAtom()->chopString();
807 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
811 if (closeCommand(cmd)) {
813 if (openedLists.top().isStarted()) {
814 append(Atom::ListItemRight,
815 openedLists.top().styleString());
816 append(Atom::ListRight,
817 openedLists.top().styleString());
823 case CMD_ENDTOPICREF:
824 if (closeCommand(cmd)) {
825 ditarefs_.pop(); // zzz
832 endSection(Doc::Part, cmd);
834 case CMD_ENDQUOTATION:
835 if (closeCommand(cmd)) {
837 append(Atom::QuotationRight);
841 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
843 case CMD_ENDSECTION1:
844 endSection(Doc::Section1, cmd);
846 case CMD_ENDSECTION2:
847 endSection(Doc::Section2, cmd);
849 case CMD_ENDSECTION3:
850 endSection(Doc::Section3, cmd);
852 case CMD_ENDSECTION4:
853 endSection(Doc::Section4, cmd);
856 if (closeCommand(cmd)) {
858 append(Atom::SidebarRight);
862 if (closeCommand(cmd)) {
864 append(Atom::TableRight);
868 if (openCommand(cmd)) {
870 append(Atom::FootnoteLeft);
871 paraState = OutsideParagraph; // ###
874 case CMD_ANNOTATEDLIST:
875 append(Atom::AnnotatedList, getArgument());
878 append(Atom::SinceList, getRestOfLine().simplified());
880 case CMD_GENERATELIST:
881 append(Atom::GeneratedList, getArgument());
883 case CMD_GRANULARITY:
884 priv->constructExtra();
885 priv->extra->granularity = getSectioningUnit();
888 if (openedCommands.top() == CMD_TABLE) {
890 append(Atom::TableHeaderLeft);
891 inTableHeader = true;
894 if (openedCommands.contains(CMD_TABLE)) {
895 location().warning(tr("Cannot use '\\%1' within '\\%2'")
896 .arg(cmdName(CMD_HEADER))
897 .arg(cmdName(openedCommands.top())));
900 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
901 .arg(cmdName(CMD_HEADER))
902 .arg(cmdName(CMD_TABLE)));
907 location().warning(tr("'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
909 startFormat(ATOM_FORMATTING_ITALIC, cmd);
916 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
917 if (preprocessorSkipping.top())
918 ++numPreprocessorSkipping;
919 if (numPreprocessorSkipping)
920 skipToNextPreprocessorCommand();
924 append(Atom::Image, getArgument());
925 append(Atom::ImageText, getRestOfLine());
929 enterPara(Atom::ImportantLeft, Atom::ImportantRight);
933 QString fileName = getArgument();
934 QString identifier = getRestOfLine();
935 include(fileName, identifier);
938 case CMD_INLINEIMAGE:
940 append(Atom::InlineImage, getArgument());
941 append(Atom::ImageText, getRestOfLine());
942 append(Atom::String, " ");
945 if (paraState == OutsideParagraph) {
947 indexStartedPara = true;
950 const Atom *last = priv->text.lastAtom();
951 if (indexStartedPara &&
952 (last->type() != Atom::FormattingRight ||
953 last->string() != ATOM_FORMATTING_INDEX))
954 indexStartedPara = false;
956 startFormat(ATOM_FORMATTING_INDEX, cmd);
959 insertTarget(getRestOfLine(),true);
963 if (isLeftBraceAhead()) {
965 append(Atom::Link, p1);
966 if (isLeftBraceAhead()) {
967 currentLinkAtom = priv->text.lastAtom();
968 startFormat(ATOM_FORMATTING_LINK, cmd);
971 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
972 append(Atom::String, cleanLink(p1));
973 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
978 append(Atom::Link, p1);
979 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
980 append(Atom::String, cleanLink(p1));
981 append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
986 if (openCommand(cmd))
987 append(Atom::LegaleseLeft);
988 docPrivate->hasLegalese = true;
991 if (openCommand(cmd)) {
994 append(Atom::Link, p1);
995 append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
996 skipSpacesOrOneEndl();
1000 if (openCommand(cmd)) {
1002 openedLists.push(OpenedList(location(),
1003 getOptionalArgument()));
1008 if (openCommand(cmd)) {
1010 if (cmd == CMD_MAPREF)
1014 t->setNavtitle(getArgument(true));
1015 if (cmd == CMD_MAPREF)
1016 t->setHref(getArgument());
1018 t->setHref(getOptionalArgument());
1019 if (ditarefs_.isEmpty())
1020 priv->ditamap_.append(t);
1022 ditarefs_.top()->appendSubref(t);
1027 priv->constructExtra();
1029 priv->extra->metaMap.insert(p1, getArgument());
1032 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
1036 enterPara(Atom::NoteLeft, Atom::NoteRight);
1039 location().warning(tr("'\\o' is deprecated. Use '\\li'"));
1042 if (openedCommands.top() == CMD_LIST) {
1043 if (openedLists.top().isStarted()) {
1044 append(Atom::ListItemRight,
1045 openedLists.top().styleString());
1048 append(Atom::ListLeft,
1049 openedLists.top().styleString());
1051 openedLists.top().next();
1052 append(Atom::ListItemNumber,
1053 openedLists.top().numberString());
1054 append(Atom::ListItemLeft,
1055 openedLists.top().styleString());
1058 else if (openedCommands.top() == CMD_TABLE) {
1061 if (isLeftBraceAhead()) {
1063 if (isLeftBraceAhead()) {
1068 if (!inTableHeader && !inTableRow) {
1069 location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
1070 .arg(cmdName(CMD_HEADER))
1071 .arg(cmdName(CMD_ROW))
1072 .arg(cmdName(CMD_LI)));
1073 append(Atom::TableRowLeft);
1076 else if (inTableItem) {
1077 append(Atom::TableItemRight);
1078 inTableItem = false;
1081 append(Atom::TableItemLeft, p1, p2);
1085 location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
1087 .arg(cmdName(CMD_LIST))
1088 .arg(cmdName(CMD_TABLE)));
1093 append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
1094 append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
1101 if (!priv->enumItemList.contains(p1))
1102 priv->enumItemList.append(p1);
1103 if (!priv->omitEnumItemList.contains(p1))
1104 priv->omitEnumItemList.append(p1);
1107 startSection(Doc::Part, cmd);
1112 appendToCode(quoter.quoteLine(location(), cmdStr,
1115 append(Atom::CodeQuoteCommand, cmdStr);
1116 append(Atom::CodeQuoteArgument, getRestOfLine());
1122 appendToCode(quoter.quoteTo(location(), cmdStr,
1125 append(Atom::CodeQuoteCommand, cmdStr);
1126 append(Atom::CodeQuoteArgument, getRestOfLine());
1129 case CMD_PRINTUNTIL:
1132 appendToCode(quoter.quoteUntil(location(), cmdStr,
1135 append(Atom::CodeQuoteCommand, cmdStr);
1136 append(Atom::CodeQuoteArgument, getRestOfLine());
1140 if (openCommand(cmd)) {
1142 append(Atom::QuotationLeft);
1148 QString fileName = getArgument();
1149 Doc::quoteFromFile(location(), quoter, fileName);
1152 quoter.quoteTo(location(), cmdStr, ""));
1156 append(Atom::CodeQuoteCommand, cmdStr);
1157 append(Atom::CodeQuoteArgument, fileName);
1161 case CMD_QUOTEFROMFILE:
1166 append(Atom::CodeQuoteCommand, cmdStr);
1167 append(Atom::CodeQuoteArgument, getArgument());
1170 case CMD_QUOTEFUNCTION:
1172 marker = quoteFromFile();
1173 p1 = getRestOfLine();
1175 quoter.quoteTo(location(), cmdStr,
1176 slashed(marker->functionBeginRegExp(p1)));
1178 quoter.quoteUntil(location(), cmdStr,
1179 slashed(marker->functionEndRegExp(p1))));
1183 append(Atom::CodeQuoteCommand, cmdStr);
1184 append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
1189 p1 = getRestOfLine();
1191 location().warning(tr("Missing format name after '\\%1")
1192 .arg(cmdName(CMD_RAW)));
1193 append(Atom::FormatIf, p1);
1194 append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
1195 append(Atom::FormatElse);
1196 append(Atom::FormatEndif);
1199 if (openedCommands.top() == CMD_TABLE) {
1201 if (isLeftBraceAhead())
1202 p1 = getArgument(true);
1204 append(Atom::TableRowLeft,p1);
1208 if (openedCommands.contains(CMD_TABLE)) {
1209 location().warning(tr("Cannot use '\\%1' within '\\%2'")
1210 .arg(cmdName(CMD_ROW))
1211 .arg(cmdName(openedCommands.top())));
1214 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
1215 .arg(cmdName(CMD_ROW))
1216 .arg(cmdName(CMD_TABLE)));
1224 startSection(Doc::Section1, cmd);
1227 startSection(Doc::Section2, cmd);
1230 startSection(Doc::Section3, cmd);
1233 startSection(Doc::Section4, cmd);
1236 if (openCommand(cmd)) {
1238 append(Atom::SidebarLeft);
1244 quoter.quoteLine(location(),
1248 append(Atom::CodeQuoteCommand, cmdStr);
1249 append(Atom::CodeQuoteArgument, getRestOfLine());
1255 quoter.quoteTo(location(),
1259 append(Atom::CodeQuoteCommand, cmdStr);
1260 append(Atom::CodeQuoteArgument, getRestOfLine());
1266 quoter.quoteUntil(location(),
1270 append(Atom::CodeQuoteCommand, cmdStr);
1271 append(Atom::CodeQuoteArgument, getRestOfLine());
1275 p1 = ATOM_FORMATTING_SPAN + getArgument(true);
1276 startFormat(p1, cmd);
1281 QString snippet = getArgument();
1282 QString identifier = getRestOfLine();
1284 append(Atom::SnippetCommand, cmdStr);
1285 append(Atom::SnippetLocation, snippet);
1286 append(Atom::SnippetIdentifier, identifier);
1289 marker = Doc::quoteFromFile(location(),quoter,snippet);
1290 appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
1295 startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
1298 startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
1301 //p1 = getRestOfLine();
1302 p1 = getOptionalArgument();
1303 p2 = getOptionalArgument();
1304 if (openCommand(cmd)) {
1306 append(Atom::TableLeft, p1, p2);
1307 inTableHeader = false;
1309 inTableItem = false;
1312 case CMD_TABLEOFCONTENTS:
1314 if (isLeftBraceAhead())
1316 p1 += QLatin1Char(',');
1317 p1 += QString::number((int)getSectioningUnit());
1318 append(Atom::TableOfContents, p1);
1321 insertTarget(getRestOfLine(),false);
1324 startFormat(ATOM_FORMATTING_TELETYPE, cmd);
1327 startFormat(ATOM_FORMATTING_UICONTROL, cmd);
1330 startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
1337 uint unicodeChar = p1.toUInt(&ok, 0);
1339 (unicodeChar == 0x0000) ||
1340 (unicodeChar > 0xFFFE)) {
1341 location().warning(tr("Invalid Unicode character '%1' specified "
1343 .arg(p1, cmdName(CMD_UNICODE)));
1346 append(Atom::String, QChar(unicodeChar));
1352 if (openedLists.top().style() == OpenedList::Value) {
1354 if (!priv->enumItemList.contains(p1))
1355 priv->enumItemList.append(p1);
1357 openedLists.top().next();
1358 append(Atom::ListTagLeft, ATOM_LIST_VALUE);
1359 append(Atom::String, p1);
1360 append(Atom::ListTagRight, ATOM_LIST_VALUE);
1361 append(Atom::ListItemLeft, ATOM_LIST_VALUE);
1363 skipSpacesOrOneEndl();
1374 append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
1375 append(Atom::String, "Warning:");
1376 append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
1377 append(Atom::String, " ");
1380 priv->metacommandsUsed.insert(cmdStr);
1383 p1 = getRestOfLine();
1384 if (!p1.isEmpty()) {
1385 append(Atom::ParaLeft);
1386 append(Atom::String, "This function overloads ");
1387 append(Atom::AutoLink,p1);
1388 append(Atom::String, ".");
1389 append(Atom::ParaRight);
1392 append(Atom::ParaLeft);
1393 append(Atom::String,"This is an overloaded function.");
1394 append(Atom::ParaRight);
1395 p1 = getMetaCommandArgument(cmdStr);
1397 priv->metaCommandMap[cmdStr].append(ArgLocPair(p1,location()));
1400 if (metaCommandSet.contains(cmdStr)) {
1401 priv->metacommandsUsed.insert(cmdStr);
1402 QString arg = getMetaCommandArgument(cmdStr);
1403 priv->metaCommandMap[cmdStr].append(ArgLocPair(arg,location()));
1404 if (possibleTopics.contains(cmdStr)) {
1405 priv->topics.append(Topic(cmdStr,arg));
1408 else if (macroHash()->contains(cmdStr)) {
1409 const Macro ¯o = macroHash()->value(cmdStr);
1410 int numPendingFi = 0;
1411 QStringMap::ConstIterator d;
1412 d = macro.otherDefs.constBegin();
1413 while (d != macro.otherDefs.constEnd()) {
1414 append(Atom::FormatIf, d.key());
1415 expandMacro(cmdStr, *d, macro.numParams);
1418 if (d == macro.otherDefs.constEnd()) {
1419 append(Atom::FormatEndif);
1422 append(Atom::FormatElse);
1426 while (numPendingFi-- > 0)
1427 append(Atom::FormatEndif);
1429 if (!macro.defaultDef.isEmpty()) {
1430 if (!macro.otherDefs.isEmpty()) {
1431 macro.defaultDefLocation.warning(
1432 tr("Macro cannot have both "
1433 "format-specific and qdoc- "
1434 "syntax definitions"));
1437 location().push(macro.defaultDefLocation.filePath());
1438 in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
1440 openedInputs.push(pos + macro.defaultDef.length());
1446 tr("Unknown command '\\%1'").arg(cmdStr),
1447 detailsUnknownCommand(metaCommandSet,cmdStr));
1449 append(Atom::UnknownCommand, cmdStr);
1466 QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth);
1467 if (f == pendingFormats.end()) {
1472 append(Atom::FormattingRight, *f);
1473 if (*f == ATOM_FORMATTING_INDEX) {
1474 if (indexStartedPara)
1477 else if (*f == ATOM_FORMATTING_LINK) {
1478 // hack for C++ to support links like
1479 // \l{QString::}{count()}
1480 if (currentLinkAtom &&
1481 currentLinkAtom->string().endsWith("::")) {
1482 QString suffix = Text::subText(currentLinkAtom,
1483 priv->text.lastAtom()).toString();
1484 currentLinkAtom->appendString(suffix);
1486 currentLinkAtom = 0;
1488 pendingFormats.erase(f);
1495 switch (priv->text.lastAtom()->type()) {
1496 case Atom::ParaLeft:
1503 if (paraState == OutsideParagraph) {
1517 (paraState == InSingleLineParagraph ||
1534 int numInternalUppercase = 0;
1535 int numLowercase = 0;
1536 int numStrangeSymbols = 0;
1539 unsigned char latin1Ch = in.at(pos).toLatin1();
1540 if (islower(latin1Ch)) {
1544 else if (isupper(latin1Ch)) {
1546 ++numInternalUppercase;
1549 else if (isdigit(latin1Ch)) {
1550 if (pos > startPos) {
1557 else if (latin1Ch == '_' || latin1Ch == '@') {
1558 ++numStrangeSymbols;
1561 else if (latin1Ch == ':' && pos < len - 1
1562 && in.at(pos + 1) == QLatin1Char(':')) {
1563 ++numStrangeSymbols;
1566 else if (latin1Ch == '(') {
1567 if (pos > startPos) {
1568 if (pos < len - 1 &&
1569 in.at(pos + 1) == QLatin1Char(')')) {
1570 ++numStrangeSymbols;
1575 // ### handle functions with signatures
1576 // and function calls
1589 if (pos == startPos) {
1590 if (!ch.isSpace()) {
1596 QString word = in.mid(startPos, pos - startPos);
1597 // is word a C++ symbol or an English word?
1598 if ((numInternalUppercase >= 1 && numLowercase >= 2)
1599 || numStrangeSymbols >= 1) {
1600 append(Atom::AutoLink, word);
1612 // for compatibility
1613 if (openedCommands.top() == CMD_LEGALESE) {
1614 append(Atom::LegaleseRight);
1615 openedCommands.pop();
1618 if (openedCommands.top() != CMD_OMIT) {
1619 location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
1621 else if (preprocessorSkipping.count() > 0) {
1622 location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
1625 if (currentSection > Doc::NoSection) {
1626 append(Atom::SectionRight, QString::number(currentSection));
1627 currentSection = Doc::NoSection;
1630 if (priv->extra && priv->extra->granularity < priv->extra->section)
1631 priv->extra->granularity = priv->extra->section;
1632 priv->text.stripFirstAtom();
1636 Returns the current location.
1638 Location &DocParser::location()
1640 while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
1642 cachedPos = openedInputs.pop();
1644 while (cachedPos < pos)
1645 cachedLoc.advance(in.at(cachedPos++));
1649 QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
1652 QSet<QString> commandSet = metaCommandSet;
1654 while (cmds[i].english != 0) {
1655 commandSet.insert(*cmds[i].alias);
1659 if (aliasMap()->contains(str))
1660 return tr("The command '\\%1' was renamed '\\%2' by the configuration"
1661 " file. Use the new name.")
1662 .arg(str).arg((*aliasMap())[str]);
1664 QString best = nearestName(str, commandSet);
1667 return tr("Maybe you meant '\\%1'?").arg(best);
1670 void DocParser::insertBaseName(const QString &baseName)
1672 priv->constructExtra();
1673 if (currentSection == priv->extra->section) {
1674 priv->extra->baseName = baseName;
1677 Atom *atom = priv->text.firstAtom();
1678 Atom *sectionLeft = 0;
1680 int delta = currentSection - priv->extra->section;
1683 if (atom->type() == Atom::SectionLeft &&
1684 atom->string().toInt() == delta)
1686 atom = atom->next();
1688 if (sectionLeft != 0)
1689 (void) new Atom(sectionLeft, Atom::BaseName, baseName);
1693 void DocParser::insertTarget(const QString &target, bool keyword)
1695 if (targetMap.contains(target)) {
1696 location().warning(tr("Duplicate target name '%1'").arg(target));
1697 targetMap[target].warning(tr("(The previous occurrence is here)"));
1700 targetMap.insert(target, location());
1701 append(Atom::Target, target);
1702 priv->constructExtra();
1704 priv->extra->keywords.append(priv->text.lastAtom());
1706 priv->extra->targets.append(priv->text.lastAtom());
1710 void DocParser::include(const QString& fileName, const QString& identifier)
1712 if (location().depth() > 16)
1713 location().fatal(tr("Too many nested '\\%1's")
1714 .arg(cmdName(CMD_INCLUDE)));
1716 QString userFriendlyFilePath;
1717 // ### use current directory?
1718 QString filePath = Config::findFile(location(),
1722 userFriendlyFilePath);
1723 if (filePath.isEmpty()) {
1724 location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName));
1727 QFile inFile(filePath);
1728 if (!inFile.open(QFile::ReadOnly)) {
1729 location().warning(tr("Cannot open qdoc include file '%1'")
1730 .arg(userFriendlyFilePath));
1733 location().push(userFriendlyFilePath);
1735 QTextStream inStream(&inFile);
1736 QString includedStuff = inStream.readAll();
1739 if (identifier.isEmpty()) {
1740 in.insert(pos, includedStuff);
1742 openedInputs.push(pos + includedStuff.length());
1745 QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
1748 while (i < lineBuffer.size()) {
1749 if (lineBuffer[i].startsWith("//!")) {
1750 if (lineBuffer[i].contains(identifier)) {
1757 if (startLine < 0) {
1758 location().warning(tr("Cannot find '%1' in '%2'")
1760 .arg(userFriendlyFilePath));
1767 if (lineBuffer[i].startsWith("//!")) {
1768 if (i<lineBuffer.size()) {
1769 if (lineBuffer[i].contains(identifier)) {
1775 result += lineBuffer[i] + QLatin1Char('\n');
1777 } while (i < lineBuffer.size());
1778 if (result.isEmpty()) {
1779 location().warning(tr("Empty qdoc snippet '%1' in '%2'")
1781 .arg(userFriendlyFilePath));
1784 in.insert(pos, result);
1786 openedInputs.push(pos + result.length());
1793 void DocParser::startFormat(const QString& format, int cmd)
1797 QMap<int, QString>::ConstIterator f = pendingFormats.constBegin();
1798 while (f != pendingFormats.constEnd()) {
1800 location().warning(tr("Cannot nest '\\%1' commands")
1801 .arg(cmdName(cmd)));
1807 append(Atom::FormattingLeft, format);
1809 if (isLeftBraceAhead()) {
1810 skipSpacesOrOneEndl();
1811 pendingFormats.insert(braceDepth, format);
1816 append(Atom::String, getArgument());
1817 append(Atom::FormattingRight, format);
1818 if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
1820 indexStartedPara = false;
1825 bool DocParser::openCommand(int cmd)
1827 int outer = openedCommands.top();
1830 if (cmd != CMD_LINK) {
1831 if (outer == CMD_LIST) {
1832 ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
1834 else if (outer == CMD_ABSTRACT) {
1835 ok = (cmd == CMD_LIST ||
1836 cmd == CMD_QUOTATION ||
1839 else if (outer == CMD_SIDEBAR) {
1840 ok = (cmd == CMD_LIST ||
1841 cmd == CMD_QUOTATION ||
1842 cmd == CMD_SIDEBAR);
1844 else if (outer == CMD_QUOTATION) {
1845 ok = (cmd == CMD_LIST);
1847 else if (outer == CMD_TABLE) {
1848 ok = (cmd == CMD_LIST ||
1849 cmd == CMD_FOOTNOTE ||
1850 cmd == CMD_QUOTATION);
1852 else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
1855 else if (outer == CMD_TOPICREF)
1856 ok = (cmd == CMD_TOPICREF || cmd == CMD_MAPREF);
1857 else if (outer == CMD_MAPREF)
1862 openedCommands.push(cmd);
1865 location().warning(tr("Can't use '\\%1' in '\\%2'").arg(cmdName(cmd)).arg(cmdName(outer)));
1870 bool DocParser::closeCommand(int endCmd)
1872 if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
1873 openedCommands.pop();
1877 bool contains = false;
1878 QStack<int> opened2 = openedCommands;
1879 while (opened2.size() > 1) {
1880 if (endCmdFor(opened2.top()) == endCmd) {
1888 while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) {
1889 location().warning(tr("Missing '\\%1' before '\\%2'")
1890 .arg(endCmdName(openedCommands.top()))
1891 .arg(cmdName(endCmd)));
1892 openedCommands.pop();
1896 location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd)));
1902 void DocParser::startSection(Doc::Sections unit, int cmd)
1906 if (currentSection == Doc::NoSection) {
1907 currentSection = (Doc::Sections) (unit);
1908 priv->constructExtra();
1909 priv->extra->section = currentSection;
1912 endSection(unit,cmd);
1914 append(Atom::SectionLeft, QString::number(unit));
1915 priv->constructExtra();
1916 priv->extra->tableOfContents.append(priv->text.lastAtom());
1917 priv->extra->tableOfContentsLevels.append(unit);
1918 enterPara(Atom::SectionHeadingLeft,
1919 Atom::SectionHeadingRight,
1920 QString::number(unit));
1921 currentSection = unit;
1925 void DocParser::endSection(int , int) // (int unit, int endCmd)
1928 append(Atom::SectionRight, QString::number(currentSection));
1929 currentSection = (Doc::NoSection);
1932 void DocParser::parseAlso()
1936 while (pos < len && in[pos] != '\n') {
1940 if (in[pos] == '{') {
1941 target = getArgument();
1943 if (in[pos] == '{') {
1944 str = getArgument();
1946 // hack for C++ to support links like \l{QString::}{count()}
1947 if (target.endsWith("::"))
1955 else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
1957 target = getArgument();
1958 int endPos = in.indexOf("\\endlink", pos);
1960 str = in.mid(pos, endPos - pos).trimmed();
1966 target = getArgument();
1967 str = cleanLink(target);
1971 also << Atom(Atom::Link, target)
1972 << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
1974 << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
1975 priv->addAlso(also);
1978 if (pos < len && in[pos] == ',') {
1980 skipSpacesOrOneEndl();
1982 else if (in[pos] != '\n') {
1983 location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
1988 //static bool debug = false;
1990 if (type == Atom::DivLeft)
1993 qDebug() << type << string;
1994 if (type == Atom::DivRight)
1998 void DocParser::append(Atom::Type type, const QString &string)
2000 Atom::Type lastType = priv->text.lastAtom()->type();
2001 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
2002 priv->text.lastAtom()->chopString();
2003 priv->text << Atom(type, string);
2006 void DocParser::append(Atom::Type type, const QString& p1, const QString& p2)
2008 Atom::Type lastType = priv->text.lastAtom()->type();
2009 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
2010 priv->text.lastAtom()->chopString();
2011 priv->text << Atom(type, p1, p2);
2014 void DocParser::appendChar(QChar ch)
2016 if (priv->text.lastAtom()->type() != Atom::String)
2017 append(Atom::String);
2018 Atom *atom = priv->text.lastAtom();
2019 if (ch == QLatin1Char(' ')) {
2020 if (!atom->string().endsWith(QLatin1Char(' ')))
2021 atom->appendChar(QLatin1Char(' '));
2024 atom->appendChar(ch);
2027 void DocParser::appendWord(const QString &word)
2029 if (priv->text.lastAtom()->type() != Atom::String) {
2030 append(Atom::String, word);
2033 priv->text.lastAtom()->appendString(word);
2036 void DocParser::appendToCode(const QString& markedCode)
2038 Atom::Type lastType = priv->text.lastAtom()->type();
2039 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
2041 priv->text.lastAtom()->appendString(markedCode);
2044 void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType)
2046 Atom::Type lastType = priv->text.lastAtom()->type();
2047 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
2048 append(defaultType, markedCode);
2050 priv->text.lastAtom()->appendString(markedCode);
2053 void DocParser::startNewPara()
2059 void DocParser::enterPara(Atom::Type leftType,
2060 Atom::Type rightType,
2061 const QString& string)
2063 if (paraState == OutsideParagraph) {
2065 if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
2066 (priv->text.lastAtom()->type() != Atom::DivLeft)) {
2070 append(leftType, string);
2071 indexStartedPara = false;
2072 pendingParaLeftType = leftType;
2073 pendingParaRightType = rightType;
2074 pendingParaString = string;
2075 if (leftType == Atom::SectionHeadingLeft) {
2076 paraState = InSingleLineParagraph;
2079 paraState = InMultiLineParagraph;
2081 skipSpacesOrOneEndl();
2085 void DocParser::leavePara()
2087 if (paraState != OutsideParagraph) {
2088 if (!pendingFormats.isEmpty()) {
2089 location().warning(tr("Missing '}'"));
2090 pendingFormats.clear();
2093 if (priv->text.lastAtom()->type() == pendingParaLeftType) {
2094 priv->text.stripLastAtom();
2097 if (priv->text.lastAtom()->type() == Atom::String &&
2098 priv->text.lastAtom()->string().endsWith(QLatin1Char(' '))) {
2099 priv->text.lastAtom()->chopString();
2101 append(pendingParaRightType, pendingParaString);
2103 paraState = OutsideParagraph;
2104 indexStartedPara = false;
2105 pendingParaRightType = Atom::Nop;
2106 pendingParaString.clear();
2110 void DocParser::leaveValue()
2113 if (openedLists.isEmpty()) {
2114 openedLists.push(OpenedList(OpenedList::Value));
2115 append(Atom::ListLeft, ATOM_LIST_VALUE);
2118 if (priv->text.lastAtom()->type() == Atom::Nop)
2119 priv->text.stripLastAtom();
2120 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2124 void DocParser::leaveValueList()
2127 if (!openedLists.isEmpty() &&
2128 (openedLists.top().style() == OpenedList::Value)) {
2129 if (priv->text.lastAtom()->type() == Atom::Nop)
2130 priv->text.stripLastAtom();
2131 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2132 append(Atom::ListRight, ATOM_LIST_VALUE);
2137 void DocParser::leaveTableRow()
2141 append(Atom::TableItemRight);
2142 inTableItem = false;
2144 if (inTableHeader) {
2145 append(Atom::TableHeaderRight);
2146 inTableHeader = false;
2149 append(Atom::TableRowRight);
2154 CodeMarker *DocParser::quoteFromFile()
2156 return Doc::quoteFromFile(location(), quoter, getArgument());
2159 void DocParser::expandMacro(const QString &name,
2163 if (numParams == 0) {
2164 append(Atom::RawString, def);
2170 for (int i = 0; i < numParams; i++) {
2171 if (numParams == 1 || isLeftBraceAhead()) {
2172 args << getArgument(true);
2175 location().warning(tr("Macro '\\%1' invoked with too few"
2176 " arguments (expected %2, got %3)")
2177 .arg(name).arg(numParams).arg(i));
2183 while (j < def.size()) {
2185 if (((paramNo = def[j].unicode()) >= 1) &&
2186 (paramNo <= numParams)) {
2187 if (!rawString.isEmpty()) {
2188 append(Atom::RawString, rawString);
2191 append(Atom::String, args[paramNo - 1]);
2195 rawString += def[j++];
2198 if (!rawString.isEmpty())
2199 append(Atom::RawString, rawString);
2203 QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
2205 if (numParams == 0) {
2212 for (int i = 0; i < numParams; i++) {
2213 if (numParams == 1 || isLeftBraceAhead()) {
2214 args << getArgument(true);
2217 location().warning(tr("Macro '\\%1' invoked with too few"
2218 " arguments (expected %2, got %3)")
2219 .arg(name).arg(numParams).arg(i));
2225 while (j < def.size()) {
2227 if (((paramNo = def[j].unicode()) >= 1) &&
2228 (paramNo <= numParams)) {
2229 rawString += args[paramNo - 1];
2233 rawString += def[j++];
2240 Doc::Sections DocParser::getSectioningUnit()
2242 QString name = getOptionalArgument();
2244 if (name == "part") {
2247 else if (name == "chapter") {
2248 return Doc::Chapter;
2250 else if (name == "section1") {
2251 return Doc::Section1;
2253 else if (name == "section2") {
2254 return Doc::Section2;
2256 else if (name == "section3") {
2257 return Doc::Section3;
2259 else if (name == "section4") {
2260 return Doc::Section4;
2262 else if (name.isEmpty()) {
2263 return Doc::NoSection;
2266 location().warning(tr("Invalid section '%1'").arg(name));
2267 return Doc::NoSection;
2272 Gets an argument that is enclosed in braces and returns it
2273 without the enclosing braces. On entry, the current character
2274 is the left brace. On exit, the current character is the one
2275 that comes afterr the right brace.
2277 If \a verbatim is true, extra whitespace is retained in the
2278 returned string. Otherwise, extr whitespace is removed.
2280 QString DocParser::getBracedArgument(bool verbatim)
2284 if (pos < (int) in.length() && in[pos] == '{') {
2286 while (pos < (int) in.length() && delimDepth >= 0) {
2287 switch (in[pos].unicode()) {
2290 arg += QLatin1Char('{');
2295 if (delimDepth >= 0)
2296 arg += QLatin1Char('}');
2306 if (pos < (int) in.length()) {
2307 if (in[pos].isLetterOrNumber())
2310 if (in[pos].isSpace()) {
2325 location().warning(tr("Missing '}'"));
2331 Typically, an argument ends at the next white-space. However,
2332 braces can be used to group words:
2336 Also, opening and closing parentheses have to match. Thus,
2340 is an argument too, although it contains spaces. Finally,
2341 trailing punctuation is not included in an argument, nor is 's.
2343 QString DocParser::getArgument(bool verbatim)
2345 skipSpacesOrOneEndl();
2349 QString arg = getBracedArgument(verbatim);
2350 if (arg.isEmpty()) {
2351 while ((pos < in.length()) &&
2352 ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) {
2353 switch (in[pos].unicode()) {
2365 if (pos == startPos || delimDepth >= 0) {
2377 if (pos < (int) in.length()) {
2378 if (in[pos].isLetterOrNumber())
2381 if (in[pos].isSpace()) {
2395 if ((arg.length() > 1) &&
2396 (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
2397 !arg.endsWith("...")) {
2398 arg.truncate(arg.length() - 1);
2401 if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
2402 arg.truncate(arg.length() - 2);
2406 return arg.simplified();
2409 QString DocParser::getOptionalArgument()
2411 skipSpacesOrOneEndl();
2412 if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
2413 in[pos + 1].isLetterOrNumber()) {
2417 return getArgument();
2421 QString DocParser::getRestOfLine()
2427 bool trailingSlash = false;
2432 while (pos < in.size() && in[pos] != '\n') {
2433 if (in[pos] == '\\' && !trailingSlash) {
2434 trailingSlash = true;
2436 while ((pos < in.size()) &&
2437 in[pos].isSpace() &&
2442 trailingSlash = false;
2448 t += QLatin1Char(' ');
2449 t += in.mid(begin, pos - begin).simplified();
2451 if (trailingSlash) {
2455 if (pos < in.size())
2457 } while (pos < in.size() && trailingSlash);
2463 The metacommand argument is normally the remaining text to
2464 the right of the metacommand itself. The extra blanks are
2465 stripped and the argument string is returned.
2467 QString DocParser::getMetaCommandArgument(const QString &cmdStr)
2474 while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
2475 if (in.at(pos) == '(')
2477 else if (in.at(pos) == ')')
2482 if (pos == in.size() && parenDepth > 0) {
2484 location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
2487 QString t = in.mid(begin, pos - begin).simplified();
2492 QString DocParser::getUntilEnd(int cmd)
2494 int endCmd = endCmdFor(cmd);
2495 QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
2497 int end = rx.indexIn(in, pos);
2500 location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
2504 t = in.mid(pos, end - pos);
2505 pos = end + rx.matchedLength();
2510 QString DocParser::getCode(int cmd, CodeMarker *marker)
2512 QString code = untabifyEtc(getUntilEnd(cmd));
2513 int indent = indentLevel(code);
2514 if (indent < minIndent)
2516 code = unindent(minIndent, code);
2518 marker = CodeMarker::markerForCode(code);
2519 return marker->markedUpCode(code, 0, location());
2523 Was used only for generating doxygen output.
2525 QString DocParser::getUnmarkedCode(int cmd)
2527 QString code = getUntilEnd(cmd);
2531 bool DocParser::isBlankLine()
2535 while (i < len && in[i].isSpace()) {
2543 bool DocParser::isLeftBraceAhead()
2548 while (i < len && in[i].isSpace() && numEndl < 2) {
2549 // ### bug with '\\'
2554 return numEndl < 2 && i < len && in[i] == '{';
2558 Skips to the next non-space character or EOL.
2560 void DocParser::skipSpacesOnLine()
2562 while ((pos < in.length()) &&
2563 in[pos].isSpace() &&
2564 (in[pos].unicode() != '\n'))
2569 Skips spaces and on EOL.
2571 void DocParser::skipSpacesOrOneEndl()
2574 while (pos < (int) in.length() && in[pos].isSpace()) {
2577 if (firstEndl == -1) {
2589 void DocParser::skipAllSpaces()
2591 while (pos < len && in[pos].isSpace())
2595 void DocParser::skipToNextPreprocessorCommand()
2597 QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + QLatin1Char('|') +
2598 cmdName(CMD_ELSE) + QLatin1Char('|') +
2599 cmdName(CMD_ENDIF) + ")\\b");
2600 int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
2608 int DocParser::endCmdFor(int cmd)
2612 return CMD_ENDABSTRACT;
2616 return CMD_ENDCHAPTER;
2624 return CMD_ENDQMLTEXT;
2628 return CMD_ENDFOOTNOTE;
2630 return CMD_ENDLEGALESE;
2644 return CMD_ENDQUOTATION;
2648 return CMD_ENDSECTION1;
2650 return CMD_ENDSECTION2;
2652 return CMD_ENDSECTION3;
2654 return CMD_ENDSECTION4;
2656 return CMD_ENDSIDEBAR;
2658 return CMD_ENDTABLE;
2660 return CMD_ENDTOPICREF;
2662 return CMD_ENDMAPREF;
2668 QString DocParser::cmdName(int cmd)
2670 return *cmds[cmd].alias;
2673 QString DocParser::endCmdName(int cmd)
2675 return cmdName(endCmdFor(cmd));
2678 QString DocParser::untabifyEtc(const QString& str)
2681 result.reserve(str.length());
2684 for (int i = 0; i < str.length(); i++) {
2685 const QChar c = str.at(i);
2686 if (c == QLatin1Char('\r'))
2688 if (c == QLatin1Char('\t')) {
2689 result += " " + (column % tabSize);
2690 column = ((column / tabSize) + 1) * tabSize;
2693 if (c == QLatin1Char('\n')) {
2694 while (result.endsWith(QLatin1Char(' ')))
2704 while (result.endsWith("\n\n"))
2705 result.truncate(result.length() - 1);
2706 while (result.startsWith(QLatin1Char('\n')))
2707 result = result.mid(1);
2712 int DocParser::indentLevel(const QString& str)
2714 int minIndent = INT_MAX;
2717 for (int i = 0; i < (int) str.length(); i++) {
2718 if (str[i] == '\n') {
2722 if (str[i] != ' ' && column < minIndent)
2730 QString DocParser::unindent(int level, const QString& str)
2738 for (int i = 0; i < (int) str.length(); i++) {
2739 if (str[i] == QLatin1Char('\n')) {
2744 if (column >= level)
2752 QString DocParser::slashed(const QString& str)
2754 QString result = str;
2755 result.replace(QLatin1Char('/'), "\\/");
2756 return QLatin1Char('/') + result + QLatin1Char('/');
2759 #define COMMAND_BRIEF Doc::alias("brief")
2760 #define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
2762 Doc::Doc(const Location& start_loc,
2763 const Location& end_loc,
2764 const QString& source,
2765 const QSet<QString>& metaCommandSet)
2767 priv = new DocPrivate(start_loc,end_loc,source);
2769 parser.parse(source,priv,metaCommandSet,QSet<QString>());
2773 Parse the qdoc comment \a source. Build up a list of all the topic
2774 commands found including their arguments. This constructor is used
2775 when there can be more than one topic command in theqdoc comment.
2776 Normally, there is only one topic command in a qdoc comment, but in
2777 QML documentation, there is the case where the qdoc \e{qmlproperty}
2778 command can appear multiple times in a qdoc comment.
2780 Doc::Doc(const Location& start_loc,
2781 const Location& end_loc,
2782 const QString& source,
2783 const QSet<QString>& metaCommandSet,
2784 const QSet<QString>& topics)
2786 priv = new DocPrivate(start_loc,end_loc,source);
2788 parser.parse(source,priv,metaCommandSet,topics);
2791 Doc::Doc(const Doc& doc)
2799 if (priv && priv->deref())
2803 Doc &Doc::operator=(const Doc& doc)
2807 if (priv && priv->deref())
2813 void Doc::renameParameters(const QStringList &oldNames,
2814 const QStringList &newNames)
2816 if (priv && oldNames != newNames) {
2819 priv->params = newNames.toSet();
2821 Atom *atom = priv->text.firstAtom();
2823 if (atom->type() == Atom::FormattingLeft
2824 && atom->string() == ATOM_FORMATTING_PARAMETER) {
2825 atom = atom->next();
2828 int index = oldNames.indexOf(atom->string());
2829 if (index != -1 && index < newNames.count())
2830 atom->setString(newNames.at(index));
2832 atom = atom->next();
2837 void Doc::simplifyEnumDoc()
2840 if (priv->isEnumDocSimplifiable()) {
2845 Atom *atom = priv->text.firstAtom();
2847 if ((atom->type() == Atom::ListLeft) &&
2848 (atom->string() == ATOM_LIST_VALUE)) {
2849 while (atom && ((atom->type() != Atom::ListRight) ||
2850 (atom->string() != ATOM_LIST_VALUE)))
2851 atom = atom->next();
2853 atom = atom->next();
2857 atom = atom->next();
2860 priv->text = newText;
2865 void Doc::setBody(const Text &text)
2872 Returns the starting location of a qdoc comment.
2874 const Location &Doc::location() const
2876 static const Location dummy;
2877 return priv == 0 ? dummy : priv->start_loc;
2881 Returns the starting location of a qdoc comment.
2883 const Location& Doc::startLocation() const
2889 Returns the ending location of a qdoc comment.
2891 const Location& Doc::endLocation() const
2893 static const Location dummy;
2894 return priv == 0 ? dummy : priv->end_loc;
2897 const QString &Doc::source() const
2899 static QString null;
2900 return priv == 0 ? null : priv->src;
2903 bool Doc::isEmpty() const
2905 return priv == 0 || priv->src.isEmpty();
2908 const Text& Doc::body() const
2910 static const Text dummy;
2911 return priv == 0 ? dummy : priv->text;
2914 Text Doc::briefText(bool inclusive) const
2916 return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
2919 Text Doc::trimmedBriefText(const QString &className) const
2921 QString classNameOnly = className;
2922 if (className.contains("::"))
2923 classNameOnly = className.split("::").last();
2925 Text originalText = briefText();
2927 const Atom *atom = originalText.firstAtom();
2931 bool standardWording = true;
2934 This code is really ugly. The entire \brief business
2935 should be rethought.
2938 if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
2939 briefStr += atom->string();
2941 atom = atom->next();
2944 QStringList w = briefStr.split(QLatin1Char(' '));
2945 if (!w.isEmpty() && w.first() == "Returns") {
2948 if (!w.isEmpty() && w.first() == "The")
2952 tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
2953 .arg(COMMAND_BRIEF).arg(className));
2954 standardWording = false;
2957 if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
2961 tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
2962 .arg(COMMAND_BRIEF).arg(className).arg(className));
2963 standardWording = false;
2966 if (!w.isEmpty() && ((w.first() == "class") ||
2967 (w.first() == "function") ||
2968 (w.first() == "macro") ||
2969 (w.first() == "widget") ||
2970 (w.first() == "namespace") ||
2971 (w.first() == "header")))
2975 tr("Nonstandard wording in '\\%1' text for '%2' ("
2976 "expected 'class', 'function', 'macro', 'widget', "
2977 "'namespace' or 'header')")
2978 .arg(COMMAND_BRIEF).arg(className));
2979 standardWording = false;
2982 if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
2985 if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
2989 whats = w.join(" ");
2991 if (whats.endsWith(QLatin1Char('.')))
2992 whats.truncate(whats.length() - 1);
2994 if (whats.isEmpty()) {
2996 tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
2997 .arg(COMMAND_BRIEF).arg(className));
2998 standardWording = false;
3001 whats[0] = whats[0].toUpper();
3003 // ### move this once \brief is abolished for properties
3004 if (standardWording)
3005 resultText << whats;
3010 Text Doc::legaleseText() const
3012 if (priv == 0 || !priv->hasLegalese)
3015 return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
3018 const QString& Doc::baseName() const
3020 static QString null;
3021 if (priv == 0 || priv->extra == 0) {
3025 return priv->extra->baseName;
3029 Doc::Sections Doc::granularity() const
3031 if (priv == 0 || priv->extra == 0) {
3032 return DocPrivateExtra().granularity;
3035 return priv->extra->granularity;
3039 const QSet<QString> &Doc::parameterNames() const
3041 return priv == 0 ? *null_Set_QString() : priv->params;
3044 const QStringList &Doc::enumItemNames() const
3046 return priv == 0 ? *null_QStringList() : priv->enumItemList;
3049 const QStringList &Doc::omitEnumItemNames() const
3051 return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
3054 const QSet<QString> &Doc::metaCommandsUsed() const
3056 return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
3060 Returns a reference to the list of topic commands used in the
3061 current qdoc comment. Normally there is only one, but there
3062 can be multiple \e{qmlproperty} commands, for example.
3064 const TopicList& Doc::topicsUsed() const
3066 return priv == 0 ? *nullTopicList() : priv->topics;
3069 ArgList Doc::metaCommandArgs(const QString& metacommand) const
3071 return priv == 0 ? ArgList() : priv->metaCommandMap.value(metacommand);
3074 const QList<Text> &Doc::alsoList() const
3076 return priv == 0 ? *null_QList_Text() : priv->alsoList;
3079 bool Doc::hasTableOfContents() const
3081 return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
3084 bool Doc::hasKeywords() const
3086 return priv && priv->extra && !priv->extra->keywords.isEmpty();
3089 bool Doc::hasTargets() const
3091 return priv && priv->extra && !priv->extra->targets.isEmpty();
3094 const QList<Atom *> &Doc::tableOfContents() const
3096 priv->constructExtra();
3097 return priv->extra->tableOfContents;
3100 const QList<int> &Doc::tableOfContentsLevels() const
3102 priv->constructExtra();
3103 return priv->extra->tableOfContentsLevels;
3106 const QList<Atom *> &Doc::keywords() const
3108 priv->constructExtra();
3109 return priv->extra->keywords;
3112 const QList<Atom *> &Doc::targets() const
3114 priv->constructExtra();
3115 return priv->extra->targets;
3118 const QStringMultiMap &Doc::metaTagMap() const
3120 return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
3123 void Doc::initialize(const Config& config)
3125 DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
3126 DocParser::exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES);
3127 DocParser::exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
3128 DocParser::sourceFiles = config.getCleanPathList(CONFIG_SOURCES);
3129 DocParser::sourceDirs = config.getCleanPathList(CONFIG_SOURCEDIRS);
3130 DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
3132 QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
3134 QStringMap reverseAliasMap;
3136 QSet<QString> commands = config.subVars(CONFIG_ALIAS);
3137 QSet<QString>::ConstIterator c = commands.constBegin();
3138 while (c != commands.constEnd()) {
3139 QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
3140 if (reverseAliasMap.contains(alias)) {
3141 config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
3142 " for both '\\%2' and '\\%3'")
3144 .arg(reverseAliasMap[alias])
3148 reverseAliasMap.insert(alias, *c);
3150 aliasMap()->insert(*c, alias);
3155 while (cmds[i].english) {
3156 cmds[i].alias = new QString(alias(cmds[i].english));
3157 cmdHash()->insert(*cmds[i].alias, cmds[i].no);
3159 if (cmds[i].no != i)
3160 Location::internalError(tr("command %1 missing").arg(i));
3164 QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
3165 QSet<QString>::ConstIterator n = macroNames.constBegin();
3166 while (n != macroNames.constEnd()) {
3167 QString macroDotName = CONFIG_MACRO + Config::dot + *n;
3169 macro.numParams = -1;
3170 macro.defaultDef = config.getString(macroDotName);
3171 if (!macro.defaultDef.isEmpty()) {
3172 macro.defaultDefLocation = config.lastLocation();
3173 macro.numParams = Config::numParams(macro.defaultDef);
3175 bool silent = false;
3177 QSet<QString> formats = config.subVars(macroDotName);
3178 QSet<QString>::ConstIterator f = formats.constBegin();
3179 while (f != formats.constEnd()) {
3180 QString def = config.getString(macroDotName + Config::dot + *f);
3181 if (!def.isEmpty()) {
3182 macro.otherDefs.insert(*f, def);
3183 int m = Config::numParams(def);
3184 if (macro.numParams == -1) {
3185 macro.numParams = m;
3187 else if (macro.numParams != m) {
3189 QString other = tr("default");
3190 if (macro.defaultDef.isEmpty())
3191 other = macro.otherDefs.constBegin().key();
3192 config.lastLocation().warning(tr("Macro '\\%1' takes"
3193 " inconsistent number"
3200 .arg(macro.numParams));
3203 if (macro.numParams < m)
3204 macro.numParams = m;
3210 if (macro.numParams != -1)
3211 macroHash()->insert(*n, macro);
3216 void Doc::terminate()
3218 DocParser::exampleFiles.clear();
3219 DocParser::exampleDirs.clear();
3220 DocParser::sourceFiles.clear();
3221 DocParser::sourceDirs.clear();
3222 aliasMap()->clear();
3224 macroHash()->clear();
3227 while (cmds[i].english) {
3228 delete cmds[i].alias;
3234 QString Doc::alias(const QString &english)
3236 return aliasMap()->value(english, english);
3240 Trims the deadwood out of \a str. i.e., this function
3243 void Doc::trimCStyleComment(Location& location, QString& str)
3246 Location m = location;
3247 bool metAsterColumn = true;
3248 int asterColumn = location.columnNo() + 1;
3251 for (i = 0; i < (int) str.length(); i++) {
3252 if (m.columnNo() == asterColumn) {
3256 metAsterColumn = true;
3259 if (str[i] == '\n') {
3260 if (!metAsterColumn)
3262 metAsterColumn = false;
3268 if (cleaned.length() == str.length())
3271 for (int i = 0; i < 3; i++)
3272 location.advance(str[i]);
3273 str = str.mid(3, str.length() - 5);
3276 CodeMarker *Doc::quoteFromFile(const Location &location,
3278 const QString &fileName)
3284 QString userFriendlyFilePath;
3285 QString filePath = Config::findFile(location,
3286 DocParser::exampleFiles,
3287 DocParser::exampleDirs,
3288 fileName, userFriendlyFilePath);
3289 if (filePath.isEmpty()) {
3290 location.warning(tr("Cannot find file to quote from: '%1'").arg(fileName));
3293 QFile inFile(filePath);
3294 if (!inFile.open(QFile::ReadOnly)) {
3295 location.warning(tr("Cannot open file to quote from: '%1'").arg(userFriendlyFilePath));
3298 QTextStream inStream(&inFile);
3299 code = DocParser::untabifyEtc(inStream.readAll());
3303 QString dirPath = QFileInfo(filePath).path();
3304 CodeMarker *marker = CodeMarker::markerForFileName(fileName);
3305 quoter.quoteFromFile(userFriendlyFilePath,
3307 marker->markedUpCode(code, 0, location));
3311 QString Doc::canonicalTitle(const QString &title)
3313 // The code below is equivalent to the following chunk, but _much_
3314 // faster (accounts for ~10% of total running time)
3316 // QRegExp attributeExpr("[^A-Za-z0-9]+");
3317 // QString result = title.toLower();
3318 // result.replace(attributeExpr, " ");
3319 // result = result.simplified();
3320 // result.replace(QLatin1Char(' '), QLatin1Char('-'));
3323 result.reserve(title.size());
3325 bool dashAppended = false;
3328 for (int i = 0; i != title.size(); ++i) {
3329 uint c = title.at(i).unicode();
3330 if (c >= 'A' && c <= 'Z')
3332 bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
3334 result += QLatin1Char(c);
3336 dashAppended = false;
3337 lastAlnum = result.size();
3339 else if (!dashAppended) {
3341 result += QLatin1Char('-');
3342 dashAppended = true;
3345 result.truncate(lastAlnum);
3352 priv = new DocPrivate;
3355 if (priv->count == 1)
3360 DocPrivate *newPriv = new DocPrivate(*priv);
3363 newPriv->extra = new DocPrivateExtra(*priv->extra);
3369 The destructor deletes all the sub-TopicRefs.
3371 TopicRef::~TopicRef()
3373 foreach (DitaRef* t, subrefs_) {
3379 Returns a reference to the structure that will be used
3380 for generating a DITA mao.
3382 const DitaRefList& Doc::ditamap() const { return priv->ditamap_; }