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 void DocParser::append(Atom::Type type, const QString &string)
1990 Atom::Type lastType = priv->text.lastAtom()->type();
1991 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
1992 priv->text.lastAtom()->chopString();
1993 priv->text << Atom(type, string);
1996 void DocParser::append(Atom::Type type, const QString& p1, const QString& p2)
1998 Atom::Type lastType = priv->text.lastAtom()->type();
1999 if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
2000 priv->text.lastAtom()->chopString();
2001 priv->text << Atom(type, p1, p2);
2004 void DocParser::appendChar(QChar ch)
2006 if (priv->text.lastAtom()->type() != Atom::String)
2007 append(Atom::String);
2008 Atom *atom = priv->text.lastAtom();
2009 if (ch == QLatin1Char(' ')) {
2010 if (!atom->string().endsWith(QLatin1Char(' ')))
2011 atom->appendChar(QLatin1Char(' '));
2014 atom->appendChar(ch);
2017 void DocParser::appendWord(const QString &word)
2019 if (priv->text.lastAtom()->type() != Atom::String) {
2020 append(Atom::String, word);
2023 priv->text.lastAtom()->appendString(word);
2026 void DocParser::appendToCode(const QString& markedCode)
2028 Atom::Type lastType = priv->text.lastAtom()->type();
2029 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
2031 priv->text.lastAtom()->appendString(markedCode);
2034 void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType)
2036 Atom::Type lastType = priv->text.lastAtom()->type();
2037 if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
2038 append(defaultType, markedCode);
2040 priv->text.lastAtom()->appendString(markedCode);
2043 void DocParser::startNewPara()
2049 void DocParser::enterPara(Atom::Type leftType,
2050 Atom::Type rightType,
2051 const QString& string)
2053 if (paraState == OutsideParagraph) {
2055 if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
2056 (priv->text.lastAtom()->type() != Atom::DivLeft)) {
2060 append(leftType, string);
2061 indexStartedPara = false;
2062 pendingParaLeftType = leftType;
2063 pendingParaRightType = rightType;
2064 pendingParaString = string;
2065 if (leftType == Atom::SectionHeadingLeft) {
2066 paraState = InSingleLineParagraph;
2069 paraState = InMultiLineParagraph;
2071 skipSpacesOrOneEndl();
2075 void DocParser::leavePara()
2077 if (paraState != OutsideParagraph) {
2078 if (!pendingFormats.isEmpty()) {
2079 location().warning(tr("Missing '}'"));
2080 pendingFormats.clear();
2083 if (priv->text.lastAtom()->type() == pendingParaLeftType) {
2084 priv->text.stripLastAtom();
2087 if (priv->text.lastAtom()->type() == Atom::String &&
2088 priv->text.lastAtom()->string().endsWith(QLatin1Char(' '))) {
2089 priv->text.lastAtom()->chopString();
2091 append(pendingParaRightType, pendingParaString);
2093 paraState = OutsideParagraph;
2094 indexStartedPara = false;
2095 pendingParaRightType = Atom::Nop;
2096 pendingParaString.clear();
2100 void DocParser::leaveValue()
2103 if (openedLists.isEmpty()) {
2104 openedLists.push(OpenedList(OpenedList::Value));
2105 append(Atom::ListLeft, ATOM_LIST_VALUE);
2108 if (priv->text.lastAtom()->type() == Atom::Nop)
2109 priv->text.stripLastAtom();
2110 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2114 void DocParser::leaveValueList()
2117 if (!openedLists.isEmpty() &&
2118 (openedLists.top().style() == OpenedList::Value)) {
2119 if (priv->text.lastAtom()->type() == Atom::Nop)
2120 priv->text.stripLastAtom();
2121 append(Atom::ListItemRight, ATOM_LIST_VALUE);
2122 append(Atom::ListRight, ATOM_LIST_VALUE);
2127 void DocParser::leaveTableRow()
2131 append(Atom::TableItemRight);
2132 inTableItem = false;
2134 if (inTableHeader) {
2135 append(Atom::TableHeaderRight);
2136 inTableHeader = false;
2139 append(Atom::TableRowRight);
2144 CodeMarker *DocParser::quoteFromFile()
2146 return Doc::quoteFromFile(location(), quoter, getArgument());
2149 void DocParser::expandMacro(const QString &name,
2153 if (numParams == 0) {
2154 append(Atom::RawString, def);
2160 for (int i = 0; i < numParams; i++) {
2161 if (numParams == 1 || isLeftBraceAhead()) {
2162 args << getArgument(true);
2165 location().warning(tr("Macro '\\%1' invoked with too few"
2166 " arguments (expected %2, got %3)")
2167 .arg(name).arg(numParams).arg(i));
2173 while (j < def.size()) {
2175 if (((paramNo = def[j].unicode()) >= 1) &&
2176 (paramNo <= numParams)) {
2177 if (!rawString.isEmpty()) {
2178 append(Atom::RawString, rawString);
2181 append(Atom::String, args[paramNo - 1]);
2185 rawString += def[j++];
2188 if (!rawString.isEmpty())
2189 append(Atom::RawString, rawString);
2193 QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
2195 if (numParams == 0) {
2202 for (int i = 0; i < numParams; i++) {
2203 if (numParams == 1 || isLeftBraceAhead()) {
2204 args << getArgument(true);
2207 location().warning(tr("Macro '\\%1' invoked with too few"
2208 " arguments (expected %2, got %3)")
2209 .arg(name).arg(numParams).arg(i));
2215 while (j < def.size()) {
2217 if (((paramNo = def[j].unicode()) >= 1) &&
2218 (paramNo <= numParams)) {
2219 rawString += args[paramNo - 1];
2223 rawString += def[j++];
2230 Doc::Sections DocParser::getSectioningUnit()
2232 QString name = getOptionalArgument();
2234 if (name == "part") {
2237 else if (name == "chapter") {
2238 return Doc::Chapter;
2240 else if (name == "section1") {
2241 return Doc::Section1;
2243 else if (name == "section2") {
2244 return Doc::Section2;
2246 else if (name == "section3") {
2247 return Doc::Section3;
2249 else if (name == "section4") {
2250 return Doc::Section4;
2252 else if (name.isEmpty()) {
2253 return Doc::NoSection;
2256 location().warning(tr("Invalid section '%1'").arg(name));
2257 return Doc::NoSection;
2262 Gets an argument that is enclosed in braces and returns it
2263 without the enclosing braces. On entry, the current character
2264 is the left brace. On exit, the current character is the one
2265 that comes afterr the right brace.
2267 If \a verbatim is true, extra whitespace is retained in the
2268 returned string. Otherwise, extr whitespace is removed.
2270 QString DocParser::getBracedArgument(bool verbatim)
2274 if (pos < (int) in.length() && in[pos] == '{') {
2276 while (pos < (int) in.length() && delimDepth >= 0) {
2277 switch (in[pos].unicode()) {
2280 arg += QLatin1Char('{');
2285 if (delimDepth >= 0)
2286 arg += QLatin1Char('}');
2296 if (pos < (int) in.length()) {
2297 if (in[pos].isLetterOrNumber())
2300 if (in[pos].isSpace()) {
2315 location().warning(tr("Missing '}'"));
2321 Typically, an argument ends at the next white-space. However,
2322 braces can be used to group words:
2326 Also, opening and closing parentheses have to match. Thus,
2330 is an argument too, although it contains spaces. Finally,
2331 trailing punctuation is not included in an argument, nor is 's.
2333 QString DocParser::getArgument(bool verbatim)
2335 skipSpacesOrOneEndl();
2339 QString arg = getBracedArgument(verbatim);
2340 if (arg.isEmpty()) {
2341 while ((pos < in.length()) &&
2342 ((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) {
2343 switch (in[pos].unicode()) {
2355 if (pos == startPos || delimDepth >= 0) {
2367 if (pos < (int) in.length()) {
2368 if (in[pos].isLetterOrNumber())
2371 if (in[pos].isSpace()) {
2385 if ((arg.length() > 1) &&
2386 (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
2387 !arg.endsWith("...")) {
2388 arg.truncate(arg.length() - 1);
2391 if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
2392 arg.truncate(arg.length() - 2);
2396 return arg.simplified();
2399 QString DocParser::getOptionalArgument()
2401 skipSpacesOrOneEndl();
2402 if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
2403 in[pos + 1].isLetterOrNumber()) {
2407 return getArgument();
2411 QString DocParser::getRestOfLine()
2417 bool trailingSlash = false;
2422 while (pos < in.size() && in[pos] != '\n') {
2423 if (in[pos] == '\\' && !trailingSlash) {
2424 trailingSlash = true;
2426 while ((pos < in.size()) &&
2427 in[pos].isSpace() &&
2432 trailingSlash = false;
2438 t += QLatin1Char(' ');
2439 t += in.mid(begin, pos - begin).simplified();
2441 if (trailingSlash) {
2445 if (pos < in.size())
2447 } while (pos < in.size() && trailingSlash);
2453 The metacommand argument is normally the remaining text to
2454 the right of the metacommand itself. The extra blanks are
2455 stripped and the argument string is returned.
2457 QString DocParser::getMetaCommandArgument(const QString &cmdStr)
2464 while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
2465 if (in.at(pos) == '(')
2467 else if (in.at(pos) == ')')
2472 if (pos == in.size() && parenDepth > 0) {
2474 location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
2477 QString t = in.mid(begin, pos - begin).simplified();
2482 QString DocParser::getUntilEnd(int cmd)
2484 int endCmd = endCmdFor(cmd);
2485 QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
2487 int end = rx.indexIn(in, pos);
2490 location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
2494 t = in.mid(pos, end - pos);
2495 pos = end + rx.matchedLength();
2500 QString DocParser::getCode(int cmd, CodeMarker *marker)
2502 QString code = untabifyEtc(getUntilEnd(cmd));
2503 int indent = indentLevel(code);
2504 if (indent < minIndent)
2506 code = unindent(minIndent, code);
2508 marker = CodeMarker::markerForCode(code);
2509 return marker->markedUpCode(code, 0, location());
2513 Was used only for generating doxygen output.
2515 QString DocParser::getUnmarkedCode(int cmd)
2517 QString code = getUntilEnd(cmd);
2521 bool DocParser::isBlankLine()
2525 while (i < len && in[i].isSpace()) {
2533 bool DocParser::isLeftBraceAhead()
2538 while (i < len && in[i].isSpace() && numEndl < 2) {
2539 // ### bug with '\\'
2544 return numEndl < 2 && i < len && in[i] == '{';
2548 Skips to the next non-space character or EOL.
2550 void DocParser::skipSpacesOnLine()
2552 while ((pos < in.length()) &&
2553 in[pos].isSpace() &&
2554 (in[pos].unicode() != '\n'))
2559 Skips spaces and on EOL.
2561 void DocParser::skipSpacesOrOneEndl()
2564 while (pos < (int) in.length() && in[pos].isSpace()) {
2567 if (firstEndl == -1) {
2579 void DocParser::skipAllSpaces()
2581 while (pos < len && in[pos].isSpace())
2585 void DocParser::skipToNextPreprocessorCommand()
2587 QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + QLatin1Char('|') +
2588 cmdName(CMD_ELSE) + QLatin1Char('|') +
2589 cmdName(CMD_ENDIF) + ")\\b");
2590 int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
2598 int DocParser::endCmdFor(int cmd)
2602 return CMD_ENDABSTRACT;
2606 return CMD_ENDCHAPTER;
2614 return CMD_ENDQMLTEXT;
2618 return CMD_ENDFOOTNOTE;
2620 return CMD_ENDLEGALESE;
2634 return CMD_ENDQUOTATION;
2638 return CMD_ENDSECTION1;
2640 return CMD_ENDSECTION2;
2642 return CMD_ENDSECTION3;
2644 return CMD_ENDSECTION4;
2646 return CMD_ENDSIDEBAR;
2648 return CMD_ENDTABLE;
2650 return CMD_ENDTOPICREF;
2652 return CMD_ENDMAPREF;
2658 QString DocParser::cmdName(int cmd)
2660 return *cmds[cmd].alias;
2663 QString DocParser::endCmdName(int cmd)
2665 return cmdName(endCmdFor(cmd));
2668 QString DocParser::untabifyEtc(const QString& str)
2671 result.reserve(str.length());
2674 for (int i = 0; i < str.length(); i++) {
2675 const QChar c = str.at(i);
2676 if (c == QLatin1Char('\r'))
2678 if (c == QLatin1Char('\t')) {
2679 result += " " + (column % tabSize);
2680 column = ((column / tabSize) + 1) * tabSize;
2683 if (c == QLatin1Char('\n')) {
2684 while (result.endsWith(QLatin1Char(' ')))
2694 while (result.endsWith("\n\n"))
2695 result.truncate(result.length() - 1);
2696 while (result.startsWith(QLatin1Char('\n')))
2697 result = result.mid(1);
2702 int DocParser::indentLevel(const QString& str)
2704 int minIndent = INT_MAX;
2707 for (int i = 0; i < (int) str.length(); i++) {
2708 if (str[i] == '\n') {
2712 if (str[i] != ' ' && column < minIndent)
2720 QString DocParser::unindent(int level, const QString& str)
2728 for (int i = 0; i < (int) str.length(); i++) {
2729 if (str[i] == QLatin1Char('\n')) {
2734 if (column >= level)
2742 QString DocParser::slashed(const QString& str)
2744 QString result = str;
2745 result.replace(QLatin1Char('/'), "\\/");
2746 return QLatin1Char('/') + result + QLatin1Char('/');
2749 #define COMMAND_BRIEF Doc::alias("brief")
2750 #define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
2752 Doc::Doc(const Location& start_loc,
2753 const Location& end_loc,
2754 const QString& source,
2755 const QSet<QString>& metaCommandSet)
2757 priv = new DocPrivate(start_loc,end_loc,source);
2759 parser.parse(source,priv,metaCommandSet,QSet<QString>());
2763 Parse the qdoc comment \a source. Build up a list of all the topic
2764 commands found including their arguments. This constructor is used
2765 when there can be more than one topic command in theqdoc comment.
2766 Normally, there is only one topic command in a qdoc comment, but in
2767 QML documentation, there is the case where the qdoc \e{qmlproperty}
2768 command can appear multiple times in a qdoc comment.
2770 Doc::Doc(const Location& start_loc,
2771 const Location& end_loc,
2772 const QString& source,
2773 const QSet<QString>& metaCommandSet,
2774 const QSet<QString>& topics)
2776 priv = new DocPrivate(start_loc,end_loc,source);
2778 parser.parse(source,priv,metaCommandSet,topics);
2781 Doc::Doc(const Doc& doc)
2789 if (priv && priv->deref())
2793 Doc &Doc::operator=(const Doc& doc)
2797 if (priv && priv->deref())
2803 void Doc::renameParameters(const QStringList &oldNames,
2804 const QStringList &newNames)
2806 if (priv && oldNames != newNames) {
2809 priv->params = newNames.toSet();
2811 Atom *atom = priv->text.firstAtom();
2813 if (atom->type() == Atom::FormattingLeft
2814 && atom->string() == ATOM_FORMATTING_PARAMETER) {
2815 atom = atom->next();
2818 int index = oldNames.indexOf(atom->string());
2819 if (index != -1 && index < newNames.count())
2820 atom->setString(newNames.at(index));
2822 atom = atom->next();
2827 void Doc::simplifyEnumDoc()
2830 if (priv->isEnumDocSimplifiable()) {
2835 Atom *atom = priv->text.firstAtom();
2837 if ((atom->type() == Atom::ListLeft) &&
2838 (atom->string() == ATOM_LIST_VALUE)) {
2839 while (atom && ((atom->type() != Atom::ListRight) ||
2840 (atom->string() != ATOM_LIST_VALUE)))
2841 atom = atom->next();
2843 atom = atom->next();
2847 atom = atom->next();
2850 priv->text = newText;
2855 void Doc::setBody(const Text &text)
2862 Returns the starting location of a qdoc comment.
2864 const Location &Doc::location() const
2866 static const Location dummy;
2867 return priv == 0 ? dummy : priv->start_loc;
2871 Returns the starting location of a qdoc comment.
2873 const Location& Doc::startLocation() const
2879 Returns the ending location of a qdoc comment.
2881 const Location& Doc::endLocation() const
2883 static const Location dummy;
2884 return priv == 0 ? dummy : priv->end_loc;
2887 const QString &Doc::source() const
2889 static QString null;
2890 return priv == 0 ? null : priv->src;
2893 bool Doc::isEmpty() const
2895 return priv == 0 || priv->src.isEmpty();
2898 const Text& Doc::body() const
2900 static const Text dummy;
2901 return priv == 0 ? dummy : priv->text;
2904 Text Doc::briefText(bool inclusive) const
2906 return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
2909 Text Doc::trimmedBriefText(const QString &className) const
2911 QString classNameOnly = className;
2912 if (className.contains("::"))
2913 classNameOnly = className.split("::").last();
2915 Text originalText = briefText();
2917 const Atom *atom = originalText.firstAtom();
2921 bool standardWording = true;
2924 This code is really ugly. The entire \brief business
2925 should be rethought.
2928 if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
2929 briefStr += atom->string();
2931 atom = atom->next();
2934 QStringList w = briefStr.split(QLatin1Char(' '));
2935 if (!w.isEmpty() && w.first() == "Returns") {
2938 if (!w.isEmpty() && w.first() == "The")
2942 tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
2943 .arg(COMMAND_BRIEF).arg(className));
2944 standardWording = false;
2947 if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
2951 tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
2952 .arg(COMMAND_BRIEF).arg(className).arg(className));
2953 standardWording = false;
2956 if (!w.isEmpty() && ((w.first() == "class") ||
2957 (w.first() == "function") ||
2958 (w.first() == "macro") ||
2959 (w.first() == "widget") ||
2960 (w.first() == "namespace") ||
2961 (w.first() == "header")))
2965 tr("Nonstandard wording in '\\%1' text for '%2' ("
2966 "expected 'class', 'function', 'macro', 'widget', "
2967 "'namespace' or 'header')")
2968 .arg(COMMAND_BRIEF).arg(className));
2969 standardWording = false;
2972 if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
2975 if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
2979 whats = w.join(" ");
2981 if (whats.endsWith(QLatin1Char('.')))
2982 whats.truncate(whats.length() - 1);
2984 if (whats.isEmpty()) {
2986 tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
2987 .arg(COMMAND_BRIEF).arg(className));
2988 standardWording = false;
2991 whats[0] = whats[0].toUpper();
2993 // ### move this once \brief is abolished for properties
2994 if (standardWording)
2995 resultText << whats;
3000 Text Doc::legaleseText() const
3002 if (priv == 0 || !priv->hasLegalese)
3005 return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
3008 const QString& Doc::baseName() const
3010 static QString null;
3011 if (priv == 0 || priv->extra == 0) {
3015 return priv->extra->baseName;
3019 Doc::Sections Doc::granularity() const
3021 if (priv == 0 || priv->extra == 0) {
3022 return DocPrivateExtra().granularity;
3025 return priv->extra->granularity;
3029 const QSet<QString> &Doc::parameterNames() const
3031 return priv == 0 ? *null_Set_QString() : priv->params;
3034 const QStringList &Doc::enumItemNames() const
3036 return priv == 0 ? *null_QStringList() : priv->enumItemList;
3039 const QStringList &Doc::omitEnumItemNames() const
3041 return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
3044 const QSet<QString> &Doc::metaCommandsUsed() const
3046 return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
3050 Returns a reference to the list of topic commands used in the
3051 current qdoc comment. Normally there is only one, but there
3052 can be multiple \e{qmlproperty} commands, for example.
3054 const TopicList& Doc::topicsUsed() const
3056 return priv == 0 ? *nullTopicList() : priv->topics;
3059 ArgList Doc::metaCommandArgs(const QString& metacommand) const
3061 return priv == 0 ? ArgList() : priv->metaCommandMap.value(metacommand);
3064 const QList<Text> &Doc::alsoList() const
3066 return priv == 0 ? *null_QList_Text() : priv->alsoList;
3069 bool Doc::hasTableOfContents() const
3071 return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
3074 bool Doc::hasKeywords() const
3076 return priv && priv->extra && !priv->extra->keywords.isEmpty();
3079 bool Doc::hasTargets() const
3081 return priv && priv->extra && !priv->extra->targets.isEmpty();
3084 const QList<Atom *> &Doc::tableOfContents() const
3086 priv->constructExtra();
3087 return priv->extra->tableOfContents;
3090 const QList<int> &Doc::tableOfContentsLevels() const
3092 priv->constructExtra();
3093 return priv->extra->tableOfContentsLevels;
3096 const QList<Atom *> &Doc::keywords() const
3098 priv->constructExtra();
3099 return priv->extra->keywords;
3102 const QList<Atom *> &Doc::targets() const
3104 priv->constructExtra();
3105 return priv->extra->targets;
3108 const QStringMultiMap &Doc::metaTagMap() const
3110 return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
3113 void Doc::initialize(const Config& config)
3115 DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
3116 DocParser::exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES);
3117 DocParser::exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
3118 DocParser::sourceFiles = config.getCleanPathList(CONFIG_SOURCES);
3119 DocParser::sourceDirs = config.getCleanPathList(CONFIG_SOURCEDIRS);
3120 DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
3122 QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
3124 QStringMap reverseAliasMap;
3126 QSet<QString> commands = config.subVars(CONFIG_ALIAS);
3127 QSet<QString>::ConstIterator c = commands.constBegin();
3128 while (c != commands.constEnd()) {
3129 QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
3130 if (reverseAliasMap.contains(alias)) {
3131 config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
3132 " for both '\\%2' and '\\%3'")
3134 .arg(reverseAliasMap[alias])
3138 reverseAliasMap.insert(alias, *c);
3140 aliasMap()->insert(*c, alias);
3145 while (cmds[i].english) {
3146 cmds[i].alias = new QString(alias(cmds[i].english));
3147 cmdHash()->insert(*cmds[i].alias, cmds[i].no);
3149 if (cmds[i].no != i)
3150 Location::internalError(tr("command %1 missing").arg(i));
3154 QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
3155 QSet<QString>::ConstIterator n = macroNames.constBegin();
3156 while (n != macroNames.constEnd()) {
3157 QString macroDotName = CONFIG_MACRO + Config::dot + *n;
3159 macro.numParams = -1;
3160 macro.defaultDef = config.getString(macroDotName);
3161 if (!macro.defaultDef.isEmpty()) {
3162 macro.defaultDefLocation = config.lastLocation();
3163 macro.numParams = Config::numParams(macro.defaultDef);
3165 bool silent = false;
3167 QSet<QString> formats = config.subVars(macroDotName);
3168 QSet<QString>::ConstIterator f = formats.constBegin();
3169 while (f != formats.constEnd()) {
3170 QString def = config.getString(macroDotName + Config::dot + *f);
3171 if (!def.isEmpty()) {
3172 macro.otherDefs.insert(*f, def);
3173 int m = Config::numParams(def);
3174 if (macro.numParams == -1) {
3175 macro.numParams = m;
3177 else if (macro.numParams != m) {
3179 QString other = tr("default");
3180 if (macro.defaultDef.isEmpty())
3181 other = macro.otherDefs.constBegin().key();
3182 config.lastLocation().warning(tr("Macro '\\%1' takes"
3183 " inconsistent number"
3190 .arg(macro.numParams));
3193 if (macro.numParams < m)
3194 macro.numParams = m;
3200 if (macro.numParams != -1)
3201 macroHash()->insert(*n, macro);
3206 void Doc::terminate()
3208 DocParser::exampleFiles.clear();
3209 DocParser::exampleDirs.clear();
3210 DocParser::sourceFiles.clear();
3211 DocParser::sourceDirs.clear();
3212 aliasMap()->clear();
3214 macroHash()->clear();
3217 while (cmds[i].english) {
3218 delete cmds[i].alias;
3224 QString Doc::alias(const QString &english)
3226 return aliasMap()->value(english, english);
3230 Trims the deadwood out of \a str. i.e., this function
3233 void Doc::trimCStyleComment(Location& location, QString& str)
3236 Location m = location;
3237 bool metAsterColumn = true;
3238 int asterColumn = location.columnNo() + 1;
3241 for (i = 0; i < (int) str.length(); i++) {
3242 if (m.columnNo() == asterColumn) {
3246 metAsterColumn = true;
3249 if (str[i] == '\n') {
3250 if (!metAsterColumn)
3252 metAsterColumn = false;
3258 if (cleaned.length() == str.length())
3261 for (int i = 0; i < 3; i++)
3262 location.advance(str[i]);
3263 str = str.mid(3, str.length() - 5);
3266 CodeMarker *Doc::quoteFromFile(const Location &location,
3268 const QString &fileName)
3274 QString userFriendlyFilePath;
3275 QString filePath = Config::findFile(location,
3276 DocParser::exampleFiles,
3277 DocParser::exampleDirs,
3278 fileName, userFriendlyFilePath);
3279 if (filePath.isEmpty()) {
3280 location.warning(tr("Cannot find file to quote from: '%1'").arg(fileName));
3283 QFile inFile(filePath);
3284 if (!inFile.open(QFile::ReadOnly)) {
3285 location.warning(tr("Cannot open file to quote from: '%1'").arg(userFriendlyFilePath));
3288 QTextStream inStream(&inFile);
3289 code = DocParser::untabifyEtc(inStream.readAll());
3293 QString dirPath = QFileInfo(filePath).path();
3294 CodeMarker *marker = CodeMarker::markerForFileName(fileName);
3295 quoter.quoteFromFile(userFriendlyFilePath,
3297 marker->markedUpCode(code, 0, location));
3301 QString Doc::canonicalTitle(const QString &title)
3303 // The code below is equivalent to the following chunk, but _much_
3304 // faster (accounts for ~10% of total running time)
3306 // QRegExp attributeExpr("[^A-Za-z0-9]+");
3307 // QString result = title.toLower();
3308 // result.replace(attributeExpr, " ");
3309 // result = result.simplified();
3310 // result.replace(QLatin1Char(' '), QLatin1Char('-'));
3313 result.reserve(title.size());
3315 bool dashAppended = false;
3318 for (int i = 0; i != title.size(); ++i) {
3319 uint c = title.at(i).unicode();
3320 if (c >= 'A' && c <= 'Z')
3322 bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
3324 result += QLatin1Char(c);
3326 dashAppended = false;
3327 lastAlnum = result.size();
3329 else if (!dashAppended) {
3331 result += QLatin1Char('-');
3332 dashAppended = true;
3335 result.truncate(lastAlnum);
3342 priv = new DocPrivate;
3345 if (priv->count == 1)
3350 DocPrivate *newPriv = new DocPrivate(*priv);
3353 newPriv->extra = new DocPrivateExtra(*priv->extra);
3359 The destructor deletes all the sub-TopicRefs.
3361 TopicRef::~TopicRef()
3363 foreach (DitaRef* t, subrefs_) {
3369 Returns a reference to the structure that will be used
3370 for generating a DITA mao.
3372 const DitaRefList& Doc::ditamap() const { return priv->ditamap_; }