1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the tools applications of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
44 #include "codemarker.h"
45 #include "editdistance.h"
46 #include "openedlist.h"
49 #include "tokenizer.h"
50 #include <qdatetime.h>
53 #include <qfileinfo.h>
55 #include <qtextstream.h>
62 Q_GLOBAL_STATIC(QSet
<QString
>, null_Set_QString
)
63 Q_GLOBAL_STATIC(QStringList
, null_QStringList
)
64 Q_GLOBAL_STATIC(QList
<Text
>, null_QList_Text
)
65 Q_GLOBAL_STATIC(QStringMap
, null_QStringMap
)
70 Location defaultDefLocation
;
76 CMD_A
, CMD_ABSTRACT
, CMD_ANNOTATEDLIST
, CMD_BADCODE
,
77 CMD_BASENAME
, CMD_BOLD
, CMD_BRIEF
, CMD_C
, CMD_CAPTION
,
78 CMD_CHAPTER
, CMD_CODE
, CMD_CODELINE
, CMD_DOTS
, CMD_ELSE
,
79 CMD_ENDABSTRACT
, CMD_ENDCHAPTER
, CMD_ENDCODE
,
80 CMD_ENDFOOTNOTE
, CMD_ENDIF
, CMD_ENDLEGALESE
, CMD_ENDLINK
,
81 CMD_ENDLIST
, CMD_ENDOMIT
, CMD_ENDPART
, CMD_ENDQUOTATION
,
82 CMD_ENDRAW
, CMD_ENDSECTION1
, CMD_ENDSECTION2
,
83 CMD_ENDSECTION3
, CMD_ENDSECTION4
, CMD_ENDSIDEBAR
,
84 CMD_ENDTABLE
, CMD_EXPIRE
, CMD_FOOTNOTE
, CMD_GENERATELIST
,
85 CMD_GRANULARITY
, CMD_HEADER
, CMD_I
, CMD_IF
, CMD_IMAGE
,
86 CMD_INCLUDE
, CMD_INLINEIMAGE
, CMD_INDEX
, CMD_KEYWORD
,
87 CMD_L
, CMD_LEGALESE
, CMD_LINK
, CMD_LIST
, CMD_META
,
88 CMD_NEWCODE
, CMD_O
, CMD_OLDCODE
, CMD_OMIT
, CMD_OMITVALUE
,
89 CMD_OVERLOAD
, CMD_PART
, CMD_PRINTLINE
, CMD_PRINTTO
,
90 CMD_PRINTUNTIL
, CMD_QUOTATION
, CMD_QUOTEFILE
,
91 CMD_QUOTEFROMFILE
, CMD_QUOTEFUNCTION
, CMD_RAW
, CMD_ROW
,
92 CMD_SA
, CMD_SECTION1
, CMD_SECTION2
, CMD_SECTION3
,
93 CMD_SECTION4
, CMD_SIDEBAR
, CMD_SINCELIST
, CMD_SKIPLINE
,
94 CMD_SKIPTO
, CMD_SKIPUNTIL
, CMD_SNIPPET
, CMD_SUB
, CMD_SUP
,
95 CMD_TABLE
, CMD_TABLEOFCONTENTS
, CMD_TARGET
, CMD_TT
,
96 CMD_UNDERLINE
, CMD_UNICODE
, CMD_VALUE
, CMD_WARNING
,
98 CMD_QML
, CMD_ENDQML
, CMD_CPP
, CMD_ENDCPP
, CMD_QMLTEXT
,
99 CMD_ENDQMLTEXT
, CMD_CPPTEXT
, CMD_ENDCPPTEXT
,
110 { "abstract", CMD_ABSTRACT
, 0 },
111 { "annotatedlist", CMD_ANNOTATEDLIST
, 0 },
112 { "badcode", CMD_BADCODE
, 0 },
113 { "basename", CMD_BASENAME
, 0 }, // ### don't document for now
114 { "bold", CMD_BOLD
, 0 },
115 { "brief", CMD_BRIEF
, 0 },
117 { "caption", CMD_CAPTION
, 0 },
118 { "chapter", CMD_CHAPTER
, 0 },
119 { "code", CMD_CODE
, 0 },
120 { "codeline", CMD_CODELINE
, 0},
121 { "dots", CMD_DOTS
, 0 },
122 { "else", CMD_ELSE
, 0 },
123 { "endabstract", CMD_ENDABSTRACT
, 0 },
124 { "endchapter", CMD_ENDCHAPTER
, 0 },
125 { "endcode", CMD_ENDCODE
, 0 },
126 { "endfootnote", CMD_ENDFOOTNOTE
, 0 },
127 { "endif", CMD_ENDIF
, 0 },
128 { "endlegalese", CMD_ENDLEGALESE
, 0 },
129 { "endlink", CMD_ENDLINK
, 0 },
130 { "endlist", CMD_ENDLIST
, 0 },
131 { "endomit", CMD_ENDOMIT
, 0 },
132 { "endpart", CMD_ENDPART
, 0 },
133 { "endquotation", CMD_ENDQUOTATION
, 0 },
134 { "endraw", CMD_ENDRAW
, 0 },
135 { "endsection1", CMD_ENDSECTION1
, 0 }, // ### don't document for now
136 { "endsection2", CMD_ENDSECTION2
, 0 }, // ### don't document for now
137 { "endsection3", CMD_ENDSECTION3
, 0 }, // ### don't document for now
138 { "endsection4", CMD_ENDSECTION4
, 0 }, // ### don't document for now
139 { "endsidebar", CMD_ENDSIDEBAR
, 0 },
140 { "endtable", CMD_ENDTABLE
, 0 },
141 { "expire", CMD_EXPIRE
, 0 },
142 { "footnote", CMD_FOOTNOTE
, 0 },
143 { "generatelist", CMD_GENERATELIST
, 0 },
144 { "granularity", CMD_GRANULARITY
, 0 }, // ### don't document for now
145 { "header", CMD_HEADER
, 0 },
148 { "image", CMD_IMAGE
, 0 },
149 { "include", CMD_INCLUDE
, 0 },
150 { "inlineimage", CMD_INLINEIMAGE
, 0 },
151 { "index", CMD_INDEX
, 0 }, // ### don't document for now
152 { "keyword", CMD_KEYWORD
, 0 },
154 { "legalese", CMD_LEGALESE
, 0 },
155 { "link", CMD_LINK
, 0 },
156 { "list", CMD_LIST
, 0 },
157 { "meta", CMD_META
, 0 },
158 { "newcode", CMD_NEWCODE
, 0 },
160 { "oldcode", CMD_OLDCODE
, 0 },
161 { "omit", CMD_OMIT
, 0 },
162 { "omitvalue", CMD_OMITVALUE
, 0 },
163 { "overload", CMD_OVERLOAD
, 0 },
164 { "part", CMD_PART
, 0 },
165 { "printline", CMD_PRINTLINE
, 0 },
166 { "printto", CMD_PRINTTO
, 0 },
167 { "printuntil", CMD_PRINTUNTIL
, 0 },
168 { "quotation", CMD_QUOTATION
, 0 },
169 { "quotefile", CMD_QUOTEFILE
, 0 },
170 { "quotefromfile", CMD_QUOTEFROMFILE
, 0 },
171 { "quotefunction", CMD_QUOTEFUNCTION
, 0 }, // ### don't document for now
172 { "raw", CMD_RAW
, 0 },
173 { "row", CMD_ROW
, 0 },
175 { "section1", CMD_SECTION1
, 0 },
176 { "section2", CMD_SECTION2
, 0 },
177 { "section3", CMD_SECTION3
, 0 },
178 { "section4", CMD_SECTION4
, 0 },
179 { "sidebar", CMD_SIDEBAR
, 0 }, // ### don't document for now
180 { "sincelist", CMD_SINCELIST
, 0 },
181 { "skipline", CMD_SKIPLINE
, 0 },
182 { "skipto", CMD_SKIPTO
, 0 },
183 { "skipuntil", CMD_SKIPUNTIL
, 0 },
184 { "snippet", CMD_SNIPPET
, 0 },
185 { "sub", CMD_SUB
, 0 },
186 { "sup", CMD_SUP
, 0 },
187 { "table", CMD_TABLE
, 0 },
188 { "tableofcontents", CMD_TABLEOFCONTENTS
, 0 },
189 { "target", CMD_TARGET
, 0 },
191 { "underline", CMD_UNDERLINE
, 0 },
192 { "unicode", CMD_UNICODE
, 0 },
193 { "value", CMD_VALUE
, 0 },
194 { "warning", CMD_WARNING
, 0 },
196 { "qml", CMD_QML
, 0 },
197 { "endqml", CMD_ENDQML
, 0 },
198 { "cpp", CMD_CPP
, 0 },
199 { "endcpp", CMD_ENDCPP
, 0 },
200 { "qmltext", CMD_QMLTEXT
, 0 },
201 { "endqmltext", CMD_ENDQMLTEXT
, 0 },
202 { "cpptext", CMD_CPPTEXT
, 0 },
203 { "endcpptext", CMD_ENDCPPTEXT
, 0 },
208 typedef QHash
<QString
, int> QHash_QString_int
;
209 typedef QHash
<QString
, Macro
> QHash_QString_Macro
;
211 Q_GLOBAL_STATIC(QStringMap
, aliasMap
)
212 Q_GLOBAL_STATIC(QHash_QString_int
, cmdHash
)
213 Q_GLOBAL_STATIC(QHash_QString_Macro
, macroHash
)
215 class DocPrivateExtra
219 Doc::SectioningUnit granularity
;
220 Doc::SectioningUnit sectioningUnit
; // ###
221 QList
<Atom
*> tableOfContents
;
222 QList
<int> tableOfContentsLevels
;
223 QList
<Atom
*> keywords
;
224 QList
<Atom
*> targets
;
228 : granularity(Doc::Part
) { }
231 struct Shared
// ### get rid of
235 void ref() { ++count
; }
236 bool deref() { return (--count
== 0); }
241 static QString
cleanLink(const QString
&link
)
243 int colonPos
= link
.indexOf(':');
244 if ((colonPos
== -1) ||
245 (!link
.startsWith("file:") && !link
.startsWith("mailto:")))
247 return link
.mid(colonPos
+ 1).simplified();
250 class DocPrivate
: public Shared
253 DocPrivate(const Location
& start
= Location::null
,
254 const Location
& end
= Location::null
,
255 const QString
& source
= "");
258 void addAlso(const Text
& also
);
259 void constructExtra();
260 bool isEnumDocSimplifiable() const;
262 // ### move some of this in DocPrivateExtra
267 QSet
<QString
> params
;
268 QList
<Text
> alsoList
;
269 QStringList enumItemList
;
270 QStringList omitEnumItemList
;
271 QSet
<QString
> metacommandsUsed
;
272 QCommandMap metaCommandMap
;
273 bool hasLegalese
: 1;
274 bool hasSectioningUnits
: 1;
275 DocPrivateExtra
*extra
;
278 DocPrivate::DocPrivate(const Location
& start
,
280 const QString
& source
)
285 hasSectioningUnits(false),
291 DocPrivate::~DocPrivate()
296 void DocPrivate::addAlso(const Text
& also
)
298 alsoList
.append(also
);
301 void DocPrivate::constructExtra()
304 extra
= new DocPrivateExtra
;
307 bool DocPrivate::isEnumDocSimplifiable() const
309 bool justMetColon
= false;
310 int numValueTables
= 0;
312 const Atom
*atom
= text
.firstAtom();
314 if (atom
->type() == Atom::AutoLink
|| atom
->type() == Atom::String
) {
315 justMetColon
= atom
->string().endsWith(":");
317 else if ((atom
->type() == Atom::ListLeft
) &&
318 (atom
->string() == ATOM_LIST_VALUE
)) {
319 if (justMetColon
|| numValueTables
> 0)
331 void parse(const QString
&source
,
332 DocPrivate
*docPrivate
,
333 const QSet
<QString
> &metaCommandSet
);
335 static int endCmdFor(int cmd
);
336 static QString
cmdName(int cmd
);
337 static QString
endCmdName(int cmd
);
338 static QString
untabifyEtc(const QString
& str
);
339 static int indentLevel(const QString
& str
);
340 static QString
unindent(int level
, const QString
& str
);
341 static QString
slashed(const QString
& str
);
344 static QStringList exampleFiles
;
345 static QStringList exampleDirs
;
346 static QStringList sourceFiles
;
347 static QStringList sourceDirs
;
351 Location
& location();
352 QString
detailsUnknownCommand(const QSet
<QString
>& metaCommandSet
,
354 void checkExpiry(const QString
& date
);
355 void insertBaseName(const QString
&baseName
);
356 void insertTarget(const QString
& target
, bool keyword
);
357 void include(const QString
& fileName
);
358 void startFormat(const QString
& format
, int cmd
);
359 bool openCommand(int cmd
);
360 bool closeCommand(int endCmd
);
361 void startSection(Doc::SectioningUnit unit
, int cmd
);
362 void endSection(int unit
, int endCmd
);
364 void append(Atom::Type type
, const QString
& string
= "");
365 void appendChar(QChar ch
);
366 void appendWord(const QString
&word
);
367 void appendToCode(const QString
&code
);
369 void enterPara(Atom::Type leftType
= Atom::ParaLeft
,
370 Atom::Type rightType
= Atom::ParaRight
,
371 const QString
& string
= "");
374 void leaveValueList();
375 void leaveTableRow();
376 CodeMarker
*quoteFromFile();
377 void expandMacro(const QString
& name
, const QString
& def
, int numParams
);
378 Doc::SectioningUnit
getSectioningUnit();
379 QString
getArgument(bool verbatim
= false);
380 QString
getOptionalArgument();
381 QString
getRestOfLine();
382 QString
getMetaCommandArgument(const QString
&cmdStr
);
383 QString
getUntilEnd(int cmd
);
384 QString
getCode(int cmd
, CodeMarker
*marker
);
385 QString
getUnmarkedCode(int cmd
);
388 bool isLeftBraceAhead();
389 void skipSpacesOnLine();
390 void skipSpacesOrOneEndl();
391 void skipAllSpaces();
392 void skipToNextPreprocessorCommand();
394 QStack
<int> openedInputs
;
403 enum ParaState
{ OutsidePara
, InsideSingleLinePara
, InsideMultiLinePara
};
408 bool indexStartedPara
; // ### rename
409 Atom::Type pendingParaLeftType
;
410 Atom::Type pendingParaRightType
;
411 QString pendingParaString
;
415 Doc::SectioningUnit currentSectioningUnit
;
416 QMap
<QString
, Location
> targetMap
;
417 QMap
<int, QString
> pendingFormats
;
418 QStack
<int> openedCommands
;
419 QStack
<OpenedList
> openedLists
;
423 int DocParser::tabSize
;
424 QStringList
DocParser::exampleFiles
;
425 QStringList
DocParser::exampleDirs
;
426 QStringList
DocParser::sourceFiles
;
427 QStringList
DocParser::sourceDirs
;
428 bool DocParser::quoting
;
431 Parse the \a source string to build a Text data structure
432 in \a docPrivate. The Text data structure is a linked list
435 \a metaCommandSet is the set of metacommands that may be
436 found in \a source. These metacommands are not markup text
437 commands. They are topic commands and related metacommands.
439 void DocParser::parse(const QString
& source
,
440 DocPrivate
*docPrivate
,
441 const QSet
<QString
>& metaCommandSet
)
446 cachedLoc
= docPrivate
->start_loc
;
449 priv
->text
<< Atom::Nop
;
451 paraState
= OutsidePara
;
452 inTableHeader
= false;
455 indexStartedPara
= false;
456 pendingParaLeftType
= Atom::Nop
;
457 pendingParaRightType
= Atom::Nop
;
461 currentSectioningUnit
= Doc::Book
;
462 openedCommands
.push(CMD_OMIT
);
465 CodeMarker
*marker
= 0;
466 Atom
*currentLinkAtom
= 0;
468 QStack
<bool> preprocessorSkipping
;
469 int numPreprocessorSkipping
= 0;
472 QChar ch
= in
.at(pos
);
474 switch (ch
.unicode()) {
481 if (ch
.isLetterOrNumber()) {
489 if (cmdStr
.isEmpty()) {
492 if (in
.at(pos
).isSpace()) {
494 appendChar(QLatin1Char(' '));
497 appendChar(in
.at(pos
++));
502 int cmd
= cmdHash()->value(cmdStr
,NOT_A_CMD
);
507 append(Atom::FormattingLeft
,ATOM_FORMATTING_PARAMETER
);
508 append(Atom::String
, x
);
509 append(Atom::FormattingRight
,ATOM_FORMATTING_PARAMETER
);
510 priv
->params
.insert(x
);
513 if (openCommand(cmd
)) {
515 append(Atom::AbstractLeft
);
520 append(Atom::CodeBad
,getCode(CMD_BADCODE
, marker
));
524 insertBaseName(getArgument());
527 startFormat(ATOM_FORMATTING_BOLD
, cmd
);
531 enterPara(Atom::BriefLeft
, Atom::BriefRight
);
535 x
= untabifyEtc(getArgument(true));
536 marker
= CodeMarker::markerForCode(x
);
537 append(Atom::C
, marker
->markedUpCode(x
, 0, ""));
544 startSection(Doc::Chapter
, cmd
);
548 append(Atom::Code
, getCode(CMD_CODE
, marker
));
553 append(Atom::Qml
, getCode(CMD_QML
, marker
));
556 append(Atom::QmlText
);
562 if (priv
->text
.lastAtom()->type() == Atom::Code
563 && priv
->text
.lastAtom()->string().endsWith("\n\n"))
564 priv
->text
.lastAtom()->chopString();
568 append(Atom::CodeQuoteCommand
, cmdStr
);
569 append(Atom::CodeQuoteArgument
, " ");
576 if (priv
->text
.lastAtom()->type() == Atom::Code
577 && priv
->text
.lastAtom()->string().endsWith("\n\n"))
578 priv
->text
.lastAtom()->chopString();
580 QString arg
= getOptionalArgument();
583 indent
= arg
.toInt();
584 for (int i
= 0; i
< indent
; ++i
)
586 appendToCode("...\n");
589 append(Atom::CodeQuoteCommand
, cmdStr
);
590 QString arg
= getOptionalArgument();
593 append(Atom::CodeQuoteArgument
, arg
);
598 if (preprocessorSkipping
.size() > 0) {
599 if (preprocessorSkipping
.top()) {
600 --numPreprocessorSkipping
;
603 ++numPreprocessorSkipping
;
605 preprocessorSkipping
.top() = !preprocessorSkipping
.top();
606 (void)getRestOfLine(); // ### should ensure that it's empty
607 if (numPreprocessorSkipping
)
608 skipToNextPreprocessorCommand();
611 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE
)));
614 case CMD_ENDABSTRACT
:
615 if (closeCommand(cmd
)) {
617 append(Atom::AbstractRight
);
631 append(Atom::EndQmlText
);
634 case CMD_ENDFOOTNOTE
:
635 if (closeCommand(cmd
)) {
637 append(Atom::FootnoteRight
);
638 paraState
= InsideMultiLinePara
; // ###
642 if (preprocessorSkipping
.count() > 0) {
643 if (preprocessorSkipping
.pop())
644 --numPreprocessorSkipping
;
645 (void)getRestOfLine(); // ### should ensure that it's empty
646 if (numPreprocessorSkipping
)
647 skipToNextPreprocessorCommand();
650 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF
)));
653 case CMD_ENDLEGALESE
:
654 if (closeCommand(cmd
)) {
656 append(Atom::LegaleseRight
);
660 if (closeCommand(cmd
)) {
661 if (priv
->text
.lastAtom()->type() == Atom::String
662 && priv
->text
.lastAtom()->string().endsWith(" "))
663 priv
->text
.lastAtom()->chopString();
664 append(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
668 if (closeCommand(cmd
)) {
670 if (openedLists
.top().isStarted()) {
671 append(Atom::ListItemRight
,
672 openedLists
.top().styleString());
673 append(Atom::ListRight
,
674 openedLists
.top().styleString());
685 case CMD_ENDQUOTATION
:
686 if (closeCommand(cmd
)) {
688 append(Atom::QuotationRight
);
692 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW
)));
694 case CMD_ENDSECTION1
:
697 case CMD_ENDSECTION2
:
700 case CMD_ENDSECTION3
:
703 case CMD_ENDSECTION4
:
707 if (closeCommand(cmd
)) {
709 append(Atom::SidebarRight
);
713 if (closeCommand(cmd
)) {
715 append(Atom::TableRight
);
719 checkExpiry(getArgument());
722 if (openCommand(cmd
)) {
724 append(Atom::FootnoteLeft
);
725 paraState
= OutsidePara
; // ###
728 case CMD_ANNOTATEDLIST
:
729 append(Atom::AnnotatedList
, getArgument());
732 append(Atom::SinceList
, getArgument());
734 case CMD_GENERATELIST
:
735 append(Atom::GeneratedList
, getArgument());
737 case CMD_GRANULARITY
:
738 priv
->constructExtra();
739 priv
->extra
->granularity
= getSectioningUnit();
742 if (openedCommands
.top() == CMD_TABLE
) {
744 append(Atom::TableHeaderLeft
);
745 inTableHeader
= true;
748 if (openedCommands
.contains(CMD_TABLE
)) {
749 location().warning(tr("Cannot use '\\%1' within '\\%2'")
750 .arg(cmdName(CMD_HEADER
))
751 .arg(cmdName(openedCommands
.top())));
754 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
755 .arg(cmdName(CMD_HEADER
))
756 .arg(cmdName(CMD_TABLE
)));
761 startFormat(ATOM_FORMATTING_ITALIC
, cmd
);
764 preprocessorSkipping
.push(!Tokenizer::isTrue(getRestOfLine()));
765 if (preprocessorSkipping
.top())
766 ++numPreprocessorSkipping
;
767 if (numPreprocessorSkipping
)
768 skipToNextPreprocessorCommand();
772 append(Atom::Image
, getArgument());
773 append(Atom::ImageText
, getRestOfLine());
776 include(getArgument());
778 case CMD_INLINEIMAGE
:
780 append(Atom::InlineImage
, getArgument());
781 append(Atom::ImageText
, getRestOfLine());
782 append(Atom::String
, " ");
785 if (paraState
== OutsidePara
) {
787 indexStartedPara
= true;
790 const Atom
*last
= priv
->text
.lastAtom();
791 if (indexStartedPara
&&
792 (last
->type() != Atom::FormattingRight
||
793 last
->string() != ATOM_FORMATTING_INDEX
))
794 indexStartedPara
= false;
796 startFormat(ATOM_FORMATTING_INDEX
, cmd
);
799 insertTarget(getRestOfLine(),true);
803 if (isLeftBraceAhead()) {
805 append(Atom::Link
, x
);
806 if (isLeftBraceAhead()) {
807 currentLinkAtom
= priv
->text
.lastAtom();
808 startFormat(ATOM_FORMATTING_LINK
, cmd
);
811 append(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
);
812 append(Atom::String
, cleanLink(x
));
813 append(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
818 append(Atom::Link
, x
);
819 append(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
);
820 append(Atom::String
, cleanLink(x
));
821 append(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
826 if (openCommand(cmd
))
827 append(Atom::LegaleseLeft
);
828 docPrivate
->hasLegalese
= true;
831 if (openCommand(cmd
)) {
834 append(Atom::Link
, x
);
835 append(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
);
836 skipSpacesOrOneEndl();
840 if (openCommand(cmd
)) {
842 openedLists
.push(OpenedList(location(),
843 getOptionalArgument()));
847 priv
->constructExtra();
849 priv
->extra
->metaMap
.insert(x
, getRestOfLine());
852 location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE
)));
856 if (openedCommands
.top() == CMD_LIST
) {
857 if (openedLists
.top().isStarted()) {
858 append(Atom::ListItemRight
,
859 openedLists
.top().styleString());
862 append(Atom::ListLeft
,
863 openedLists
.top().styleString());
865 openedLists
.top().next();
866 append(Atom::ListItemNumber
,
867 openedLists
.top().numberString());
868 append(Atom::ListItemLeft
,
869 openedLists
.top().styleString());
872 else if (openedCommands
.top() == CMD_TABLE
) {
874 if (isLeftBraceAhead())
877 if (!inTableHeader
&& !inTableRow
) {
878 location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
879 .arg(cmdName(CMD_HEADER
))
880 .arg(cmdName(CMD_ROW
))
881 .arg(cmdName(CMD_O
)));
882 append(Atom::TableRowLeft
);
885 else if (inTableItem
) {
886 append(Atom::TableItemRight
);
890 append(Atom::TableItemLeft
, x
);
894 location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
896 .arg(cmdName(CMD_LIST
))
897 .arg(cmdName(CMD_TABLE
)));
902 append(Atom::CodeOld
, getCode(CMD_OLDCODE
, marker
));
903 append(Atom::CodeNew
, getCode(CMD_NEWCODE
, marker
));
910 if (!priv
->enumItemList
.contains(x
))
911 priv
->enumItemList
.append(x
);
912 if (!priv
->omitEnumItemList
.contains(x
))
913 priv
->omitEnumItemList
.append(x
);
916 startSection(Doc::Part
, cmd
);
921 appendToCode(quoter
.quoteLine(location(), cmdStr
,
924 append(Atom::CodeQuoteCommand
, cmdStr
);
925 append(Atom::CodeQuoteArgument
, getRestOfLine());
931 appendToCode(quoter
.quoteTo(location(), cmdStr
,
934 append(Atom::CodeQuoteCommand
, cmdStr
);
935 append(Atom::CodeQuoteArgument
, getRestOfLine());
941 appendToCode(quoter
.quoteUntil(location(), cmdStr
,
944 append(Atom::CodeQuoteCommand
, cmdStr
);
945 append(Atom::CodeQuoteArgument
, getRestOfLine());
949 if (openCommand(cmd
)) {
951 append(Atom::QuotationLeft
);
957 QString fileName
= getArgument();
958 Doc::quoteFromFile(location(), quoter
, fileName
);
961 quoter
.quoteTo(location(), cmdStr
, ""));
965 append(Atom::CodeQuoteCommand
, cmdStr
);
966 append(Atom::CodeQuoteArgument
, fileName
);
970 case CMD_QUOTEFROMFILE
:
975 append(Atom::CodeQuoteCommand
, cmdStr
);
976 append(Atom::CodeQuoteArgument
, getArgument());
979 case CMD_QUOTEFUNCTION
:
981 marker
= quoteFromFile();
984 quoter
.quoteTo(location(), cmdStr
,
985 slashed(marker
->functionBeginRegExp(x
)));
987 quoter
.quoteUntil(location(), cmdStr
,
988 slashed(marker
->functionEndRegExp(x
))));
992 append(Atom::CodeQuoteCommand
, cmdStr
);
993 append(Atom::CodeQuoteArgument
, slashed(marker
->functionEndRegExp(x
)));
1000 location().warning(tr("Missing format name after '\\%1")
1001 .arg(cmdName(CMD_RAW
)));
1002 append(Atom::FormatIf
, x
);
1003 append(Atom::RawString
, untabifyEtc(getUntilEnd(cmd
)));
1004 append(Atom::FormatElse
);
1005 append(Atom::FormatEndif
);
1008 if (openedCommands
.top() == CMD_TABLE
) {
1010 append(Atom::TableRowLeft
);
1014 if (openedCommands
.contains(CMD_TABLE
)) {
1015 location().warning(tr("Cannot use '\\%1' within '\\%2'")
1016 .arg(cmdName(CMD_ROW
))
1017 .arg(cmdName(openedCommands
.top())));
1020 location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
1021 .arg(cmdName(CMD_ROW
))
1022 .arg(cmdName(CMD_TABLE
)));
1030 startSection(Doc::Section1
, cmd
);
1033 startSection(Doc::Section2
, cmd
);
1036 startSection(Doc::Section3
, cmd
);
1039 startSection(Doc::Section4
, cmd
);
1042 if (openCommand(cmd
)) {
1044 append(Atom::SidebarLeft
);
1050 quoter
.quoteLine(location(),
1054 append(Atom::CodeQuoteCommand
, cmdStr
);
1055 append(Atom::CodeQuoteArgument
, getRestOfLine());
1061 quoter
.quoteTo(location(),
1065 append(Atom::CodeQuoteCommand
, cmdStr
);
1066 append(Atom::CodeQuoteArgument
, getRestOfLine());
1072 quoter
.quoteUntil(location(),
1076 append(Atom::CodeQuoteCommand
, cmdStr
);
1077 append(Atom::CodeQuoteArgument
, getRestOfLine());
1083 QString snippet
= getArgument();
1084 QString identifier
= getRestOfLine();
1086 append(Atom::SnippetCommand
, cmdStr
);
1087 append(Atom::SnippetLocation
, snippet
);
1088 append(Atom::SnippetIdentifier
, identifier
);
1091 Doc::quoteFromFile(location(),quoter
,snippet
);
1092 appendToCode(quoter
.quoteSnippet(location(),
1098 startFormat(ATOM_FORMATTING_SUBSCRIPT
, cmd
);
1101 startFormat(ATOM_FORMATTING_SUPERSCRIPT
, cmd
);
1104 x
= getRestOfLine();
1105 if (openCommand(cmd
)) {
1107 append(Atom::TableLeft
, x
);
1108 inTableHeader
= false;
1110 inTableItem
= false;
1113 case CMD_TABLEOFCONTENTS
:
1115 if (isLeftBraceAhead())
1118 x
+= QString::number((int)getSectioningUnit());
1119 append(Atom::TableOfContents
, x
);
1122 insertTarget(getRestOfLine(),false);
1125 startFormat(ATOM_FORMATTING_TELETYPE
, cmd
);
1128 startFormat(ATOM_FORMATTING_UNDERLINE
, cmd
);
1135 uint unicodeChar
= x
.toUInt(&ok
, 0);
1137 (unicodeChar
== 0x0000) ||
1138 (unicodeChar
> 0xFFFE)) {
1139 location().warning(tr("Invalid Unicode character '%1' specified "
1141 .arg(x
, cmdName(CMD_UNICODE
)));
1144 append(Atom::String
, QChar(unicodeChar
));
1150 if (openedLists
.top().style() == OpenedList::Value
) {
1152 if (!priv
->enumItemList
.contains(x
))
1153 priv
->enumItemList
.append(x
);
1155 openedLists
.top().next();
1156 append(Atom::ListTagLeft
, ATOM_LIST_VALUE
);
1157 append(Atom::String
, x
);
1158 append(Atom::ListTagRight
, ATOM_LIST_VALUE
);
1159 append(Atom::ListItemLeft
, ATOM_LIST_VALUE
);
1161 skipSpacesOrOneEndl();
1171 append(Atom::FormattingLeft
, ATOM_FORMATTING_BOLD
);
1172 append(Atom::String
, "Warning:");
1173 append(Atom::FormattingRight
, ATOM_FORMATTING_BOLD
);
1174 append(Atom::String
, " ");
1177 priv
->metacommandsUsed
.insert(cmdStr
);
1180 x
= getRestOfLine();
1182 append(Atom::ParaLeft
);
1183 append(Atom::String
, "This function overloads ");
1184 append(Atom::AutoLink
,x
);
1185 append(Atom::String
, ".");
1186 append(Atom::ParaRight
);
1189 append(Atom::ParaLeft
);
1190 append(Atom::String
,"This is an overloaded function.");
1191 append(Atom::ParaRight
);
1192 x
= getMetaCommandArgument(cmdStr
);
1194 priv
->metaCommandMap
[cmdStr
].append(x
);
1197 if (metaCommandSet
.contains(cmdStr
)) {
1198 priv
->metacommandsUsed
.insert(cmdStr
);
1199 QString xxx
= getMetaCommandArgument(cmdStr
);
1200 priv
->metaCommandMap
[cmdStr
].append(xxx
);
1202 else if (macroHash()->contains(cmdStr
)) {
1203 const Macro
¯o
= macroHash()->value(cmdStr
);
1204 int numPendingFi
= 0;
1205 QStringMap::ConstIterator d
;
1206 d
= macro
.otherDefs
.begin();
1207 while (d
!= macro
.otherDefs
.end()) {
1208 append(Atom::FormatIf
, d
.key());
1209 expandMacro(cmdStr
, *d
, macro
.numParams
);
1212 if (d
== macro
.otherDefs
.end()) {
1213 append(Atom::FormatEndif
);
1216 append(Atom::FormatElse
);
1220 while (numPendingFi
-- > 0)
1221 append(Atom::FormatEndif
);
1223 if (!macro
.defaultDef
.isEmpty()) {
1224 if (!macro
.otherDefs
.isEmpty()) {
1225 macro
.defaultDefLocation
.warning(
1226 tr("Macro cannot have both "
1227 "format-specific and qdoc- "
1228 "syntax definitions"));
1231 location().push(macro
.defaultDefLocation
.filePath());
1232 in
.insert(pos
, macro
.defaultDef
);
1234 openedInputs
.push(pos
+ macro
.defaultDef
.length());
1240 tr("Unknown command '\\%1'").arg(cmdStr
),
1241 detailsUnknownCommand(metaCommandSet
,cmdStr
));
1243 append(Atom::UnknownCommand
, cmdStr
);
1260 QMap
<int, QString
>::Iterator f
=
1261 pendingFormats
.find(braceDepth
);
1262 if (f
== pendingFormats
.end()) {
1267 append(Atom::FormattingRight
, *f
);
1268 if (*f
== ATOM_FORMATTING_INDEX
) {
1269 if (indexStartedPara
)
1272 else if (*f
== ATOM_FORMATTING_LINK
) {
1273 // hack for C++ to support links like
1274 // \l{QString::}{count()}
1275 if (currentLinkAtom
&&
1276 currentLinkAtom
->string().endsWith("::")) {
1277 QString suffix
= Text::subText(currentLinkAtom
,
1278 priv
->text
.lastAtom()).toString();
1279 currentLinkAtom
->appendString(suffix
);
1281 currentLinkAtom
= 0;
1283 pendingFormats
.erase(f
);
1290 switch (priv
->text
.lastAtom()->type()) {
1291 case Atom::ParaLeft
:
1298 if (paraState
== OutsidePara
) {
1312 (paraState
== InsideSingleLinePara
||
1329 int numInternalUppercase
= 0;
1330 int numLowercase
= 0;
1331 int numStrangeSymbols
= 0;
1334 unsigned char latin1Ch
= in
.at(pos
).toLatin1();
1335 if (islower(latin1Ch
)) {
1339 else if (isupper(latin1Ch
)) {
1341 ++numInternalUppercase
;
1344 else if (isdigit(latin1Ch
)) {
1345 if (pos
> startPos
) {
1352 else if (latin1Ch
== '_' || latin1Ch
== '@') {
1353 ++numStrangeSymbols
;
1356 else if (latin1Ch
== ':' && pos
< len
- 1
1357 && in
.at(pos
+ 1) == QLatin1Char(':')) {
1358 ++numStrangeSymbols
;
1361 else if (latin1Ch
== '(') {
1362 if (pos
> startPos
) {
1363 if (pos
< len
- 1 &&
1364 in
.at(pos
+ 1) == QLatin1Char(')')) {
1365 ++numStrangeSymbols
;
1370 // ### handle functions with signatures
1371 // and function calls
1384 if (pos
== startPos
) {
1385 if (!ch
.isSpace()) {
1391 QString word
= in
.mid(startPos
, pos
- startPos
);
1392 // is word a C++ symbol or an English word?
1393 if ((numInternalUppercase
>= 1 && numLowercase
>= 2)
1394 || numStrangeSymbols
>= 1) {
1395 append(Atom::AutoLink
, word
);
1407 // for compatibility
1408 if (openedCommands
.top() == CMD_LEGALESE
) {
1409 append(Atom::LegaleseRight
);
1410 openedCommands
.pop();
1413 if (openedCommands
.top() != CMD_OMIT
) {
1414 location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands
.top())));
1416 else if (preprocessorSkipping
.count() > 0) {
1417 location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF
)));
1420 while (currentSectioningUnit
> Doc::Chapter
) {
1421 int delta
= currentSectioningUnit
- priv
->extra
->sectioningUnit
;
1422 append(Atom::SectionRight
, QString::number(delta
));
1423 currentSectioningUnit
= Doc::SectioningUnit(int(currentSectioningUnit
) - 1);
1426 if (priv
->extra
&& priv
->extra
->granularity
< priv
->extra
->sectioningUnit
)
1427 priv
->extra
->granularity
= priv
->extra
->sectioningUnit
;
1428 priv
->text
.stripFirstAtom();
1431 Location
&DocParser::location()
1433 while (!openedInputs
.isEmpty() && openedInputs
.top() <= pos
) {
1435 cachedPos
= openedInputs
.pop();
1437 while (cachedPos
< pos
)
1438 cachedLoc
.advance(in
.at(cachedPos
++));
1442 QString
DocParser::detailsUnknownCommand(const QSet
<QString
> &metaCommandSet
,
1445 QSet
<QString
> commandSet
= metaCommandSet
;
1447 while (cmds
[i
].english
!= 0) {
1448 commandSet
.insert(*cmds
[i
].alias
);
1452 if (aliasMap()->contains(str
))
1453 return tr("The command '\\%1' was renamed '\\%2' by the configuration"
1454 " file. Use the new name.")
1455 .arg(str
).arg((*aliasMap())[str
]);
1457 QString best
= nearestName(str
, commandSet
);
1460 return tr("Maybe you meant '\\%1'?").arg(best
);
1463 void DocParser::checkExpiry(const QString
& date
)
1465 QRegExp
ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))");
1467 if (ymd
.exactMatch(date
)) {
1468 int y
= ymd
.cap(1).toInt();
1469 int m
= ymd
.cap(2).toInt();
1470 int d
= ymd
.cap(3).toInt();
1476 QDate
expiryDate(y
, m
, d
);
1477 if (expiryDate
.isValid()) {
1478 int days
= expiryDate
.daysTo(QDate::currentDate());
1480 location().warning(tr("Documentation expires today"));
1482 else if (days
== 1) {
1483 location().warning(tr("Documentation expired yesterday"));
1485 else if (days
>= 2) {
1486 location().warning(tr("Documentation expired %1 days ago")
1491 location().warning(tr("Date '%1' invalid").arg(date
));
1495 location().warning(tr("Date '%1' not in YYYY-MM-DD format")
1500 void DocParser::insertBaseName(const QString
&baseName
)
1502 priv
->constructExtra();
1503 if (currentSectioningUnit
== priv
->extra
->sectioningUnit
) {
1504 priv
->extra
->baseName
= baseName
;
1507 Atom
*atom
= priv
->text
.firstAtom();
1508 Atom
*sectionLeft
= 0;
1510 int delta
= currentSectioningUnit
- priv
->extra
->sectioningUnit
;
1513 if (atom
->type() == Atom::SectionLeft
&&
1514 atom
->string().toInt() == delta
)
1516 atom
= atom
->next();
1518 if (sectionLeft
!= 0)
1519 (void) new Atom(sectionLeft
, Atom::BaseName
, baseName
);
1523 void DocParser::insertTarget(const QString
&target
, bool keyword
)
1525 if (targetMap
.contains(target
)) {
1526 location().warning(tr("Duplicate target name '%1'").arg(target
));
1527 targetMap
[target
].warning(tr("(The previous occurrence is here)"));
1530 targetMap
.insert(target
, location());
1531 append(Atom::Target
, target
);
1532 priv
->constructExtra();
1534 priv
->extra
->keywords
.append(priv
->text
.lastAtom());
1536 priv
->extra
->targets
.append(priv
->text
.lastAtom());
1540 void DocParser::include(const QString
& fileName
)
1542 if (location().depth() > 16)
1543 location().fatal(tr("Too many nested '\\%1's")
1544 .arg(cmdName(CMD_INCLUDE
)));
1546 QString userFriendlyFilePath
;
1547 // ### use current directory?
1548 QString filePath
= Config::findFile(location(),
1552 userFriendlyFilePath
);
1553 if (filePath
.isEmpty()) {
1554 location().warning(tr("Cannot find leaf file '%1'").arg(fileName
));
1557 QFile
inFile(filePath
);
1558 if (!inFile
.open(QFile::ReadOnly
)) {
1559 location().warning(tr("Cannot open leaf file '%1'")
1560 .arg(userFriendlyFilePath
));
1563 location().push(userFriendlyFilePath
);
1565 QTextStream
inStream(&inFile
);
1566 QString includedStuff
= inStream
.readAll();
1569 in
.insert(pos
, includedStuff
);
1571 openedInputs
.push(pos
+ includedStuff
.length());
1576 void DocParser::startFormat(const QString
& format
, int cmd
)
1580 QMap
<int, QString
>::ConstIterator f
= pendingFormats
.begin();
1581 while (f
!= pendingFormats
.end()) {
1583 location().warning(tr("Cannot nest '\\%1' commands")
1584 .arg(cmdName(cmd
)));
1590 append(Atom::FormattingLeft
, format
);
1592 if (isLeftBraceAhead()) {
1593 skipSpacesOrOneEndl();
1594 pendingFormats
.insert(braceDepth
, format
);
1599 append(Atom::String
, getArgument());
1600 append(Atom::FormattingRight
, format
);
1601 if (format
== ATOM_FORMATTING_INDEX
&& indexStartedPara
) {
1603 indexStartedPara
= false;
1608 bool DocParser::openCommand(int cmd
)
1610 int outer
= openedCommands
.top();
1613 if (cmd
!= CMD_LINK
) {
1614 if (outer
== CMD_LIST
) {
1615 ok
= (cmd
== CMD_FOOTNOTE
|| cmd
== CMD_LIST
);
1617 else if (outer
== CMD_ABSTRACT
) {
1618 ok
= (cmd
== CMD_LIST
||
1619 cmd
== CMD_QUOTATION
||
1622 else if (outer
== CMD_SIDEBAR
) {
1623 ok
= (cmd
== CMD_LIST
||
1624 cmd
== CMD_QUOTATION
||
1625 cmd
== CMD_SIDEBAR
);
1627 else if (outer
== CMD_QUOTATION
) {
1628 ok
= (cmd
== CMD_LIST
);
1630 else if (outer
== CMD_TABLE
) {
1631 ok
= (cmd
== CMD_LIST
||
1632 cmd
== CMD_FOOTNOTE
||
1633 cmd
== CMD_QUOTATION
);
1635 else if (outer
== CMD_FOOTNOTE
|| outer
== CMD_LINK
) {
1641 openedCommands
.push(cmd
);
1644 location().warning(tr("Cannot use '\\%1' within '\\%2'")
1645 .arg(cmdName(cmd
)).arg(cmdName(outer
)));
1650 bool DocParser::closeCommand(int endCmd
)
1652 if (endCmdFor(openedCommands
.top()) == endCmd
&& openedCommands
.size() > 1) {
1653 openedCommands
.pop();
1657 bool contains
= false;
1658 QStack
<int> opened2
= openedCommands
;
1659 while (opened2
.size() > 1) {
1660 if (endCmdFor(opened2
.top()) == endCmd
) {
1668 while (endCmdFor(openedCommands
.top()) != endCmd
&& openedCommands
.size() > 1) {
1669 location().warning(tr("Missing '\\%1' before '\\%2'")
1670 .arg(endCmdName(openedCommands
.top()))
1671 .arg(cmdName(endCmd
)));
1672 openedCommands
.pop();
1676 location().warning(tr("Unexpected '\\%1'")
1677 .arg(cmdName(endCmd
)));
1683 void DocParser::startSection(Doc::SectioningUnit unit
, int cmd
)
1687 if (currentSectioningUnit
== Doc::Book
) {
1689 // mws didn't think this was necessary.
1690 if (unit
> Doc::Section1
)
1691 location().warning(tr("Unexpected '\\%1' without '\\%2'")
1693 .arg(cmdName(CMD_SECTION1
)));
1695 currentSectioningUnit
= (Doc::SectioningUnit
) (unit
- 1);
1696 priv
->constructExtra();
1697 priv
->extra
->sectioningUnit
= currentSectioningUnit
;
1700 if (unit
<= priv
->extra
->sectioningUnit
) {
1701 location().warning(tr("Unexpected '\\%1' in this documentation")
1702 .arg(cmdName(cmd
)));
1704 else if (unit
- currentSectioningUnit
> 1) {
1705 location().warning(tr("Unexpected '\\%1' at this point")
1706 .arg(cmdName(cmd
)));
1709 if (currentSectioningUnit
>= unit
)
1710 endSection(unit
, cmd
);
1712 int delta
= unit
- priv
->extra
->sectioningUnit
;
1713 append(Atom::SectionLeft
, QString::number(delta
));
1714 priv
->constructExtra();
1715 priv
->extra
->tableOfContents
.append(priv
->text
.lastAtom());
1716 priv
->extra
->tableOfContentsLevels
.append(unit
);
1717 enterPara(Atom::SectionHeadingLeft
,
1718 Atom::SectionHeadingRight
,
1719 QString::number(delta
));
1720 currentSectioningUnit
= unit
;
1724 void DocParser::endSection(int unit
, int endCmd
)
1728 if (unit
< priv
->extra
->sectioningUnit
) {
1729 location().warning(tr("Unexpected '\\%1' in this documentation")
1730 .arg(cmdName(endCmd
)));
1732 else if (unit
> currentSectioningUnit
) {
1733 location().warning(tr("Unexpected '\\%1' at this point")
1734 .arg(cmdName(endCmd
)));
1737 while (currentSectioningUnit
>= unit
) {
1738 int delta
= currentSectioningUnit
- priv
->extra
->sectioningUnit
;
1739 append(Atom::SectionRight
, QString::number(delta
));
1740 currentSectioningUnit
=
1741 (Doc::SectioningUnit
) (currentSectioningUnit
- 1);
1746 void DocParser::parseAlso()
1750 while (pos
< len
&& in
[pos
] != '\n') {
1754 if (in
[pos
] == '{') {
1755 target
= getArgument();
1757 if (in
[pos
] == '{') {
1758 str
= getArgument();
1760 // hack for C++ to support links like \l{QString::}{count()}
1761 if (target
.endsWith("::"))
1769 else if (in
[pos
] == '\\' && in
.mid(pos
, 5) == "\\link") {
1771 target
= getArgument();
1772 int endPos
= in
.indexOf("\\endlink", pos
);
1774 str
= in
.mid(pos
, endPos
- pos
).trimmed();
1780 target
= getArgument();
1781 str
= cleanLink(target
);
1785 also
<< Atom(Atom::Link
, target
)
1786 << Atom(Atom::FormattingLeft
, ATOM_FORMATTING_LINK
)
1788 << Atom(Atom::FormattingRight
, ATOM_FORMATTING_LINK
);
1789 priv
->addAlso(also
);
1792 if (pos
< len
&& in
[pos
] == ',') {
1794 skipSpacesOrOneEndl();
1796 else if (in
[pos
] != '\n') {
1797 location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA
)));
1802 void DocParser::append(Atom::Type type
, const QString
&string
)
1804 Atom::Type lastType
= priv
->text
.lastAtom()->type();
1806 if (((lastType
== Atom::Code
) || (lastType
== Atom::Code
)) &&
1808 if ((lastType
== Atom::Code
) &&
1810 priv
->text
.lastAtom()->string().endsWith(QLatin1String("\n\n")))
1811 priv
->text
.lastAtom()->chopString();
1812 priv
->text
<< Atom(type
, string
);
1815 void DocParser::appendChar(QChar ch
)
1817 if (priv
->text
.lastAtom()->type() != Atom::String
)
1818 append(Atom::String
);
1819 Atom
*atom
= priv
->text
.lastAtom();
1820 if (ch
== QLatin1Char(' ')) {
1821 if (!atom
->string().endsWith(QLatin1Char(' ')))
1822 atom
->appendChar(QLatin1Char(' '));
1825 atom
->appendChar(ch
);
1828 void DocParser::appendWord(const QString
&word
)
1830 if (priv
->text
.lastAtom()->type() != Atom::String
) {
1831 append(Atom::String
, word
);
1834 priv
->text
.lastAtom()->appendString(word
);
1837 void DocParser::appendToCode(const QString
& markedCode
)
1839 Atom::Type lastType
= priv
->text
.lastAtom()->type();
1841 if (lastType
!= Atom::Qml
)
1844 if (lastType
!= Atom::Code
)
1847 priv
->text
.lastAtom()->appendString(markedCode
);
1850 void DocParser::startNewPara()
1856 void DocParser::enterPara(Atom::Type leftType
,
1857 Atom::Type rightType
,
1858 const QString
& string
)
1860 if (paraState
== OutsidePara
) {
1861 if (priv
->text
.lastAtom()->type() != Atom::ListItemLeft
)
1863 append(leftType
, string
);
1864 indexStartedPara
= false;
1865 pendingParaLeftType
= leftType
;
1866 pendingParaRightType
= rightType
;
1867 pendingParaString
= string
;
1870 leftType
== Atom::BriefLeft
||
1872 leftType
== Atom::SectionHeadingLeft
) {
1873 paraState
= InsideSingleLinePara
;
1876 paraState
= InsideMultiLinePara
;
1878 skipSpacesOrOneEndl();
1882 void DocParser::leavePara()
1884 if (paraState
!= OutsidePara
) {
1885 if (!pendingFormats
.isEmpty()) {
1886 location().warning(tr("Missing '}'"));
1887 pendingFormats
.clear();
1890 if (priv
->text
.lastAtom()->type() == pendingParaLeftType
) {
1891 priv
->text
.stripLastAtom();
1894 if (priv
->text
.lastAtom()->type() == Atom::String
&&
1895 priv
->text
.lastAtom()->string().endsWith(" ")) {
1896 priv
->text
.lastAtom()->chopString();
1898 append(pendingParaRightType
, pendingParaString
);
1900 paraState
= OutsidePara
;
1901 indexStartedPara
= false;
1902 pendingParaRightType
= Atom::Nop
;
1903 pendingParaString
= "";
1907 void DocParser::leaveValue()
1910 if (openedLists
.isEmpty()) {
1911 openedLists
.push(OpenedList(OpenedList::Value
));
1912 append(Atom::ListLeft
, ATOM_LIST_VALUE
);
1915 if (priv
->text
.lastAtom()->type() == Atom::Nop
)
1916 priv
->text
.stripLastAtom();
1917 append(Atom::ListItemRight
, ATOM_LIST_VALUE
);
1921 void DocParser::leaveValueList()
1924 if (!openedLists
.isEmpty() &&
1925 (openedLists
.top().style() == OpenedList::Value
)) {
1926 if (priv
->text
.lastAtom()->type() == Atom::Nop
)
1927 priv
->text
.stripLastAtom();
1928 append(Atom::ListItemRight
, ATOM_LIST_VALUE
);
1929 append(Atom::ListRight
, ATOM_LIST_VALUE
);
1934 void DocParser::leaveTableRow()
1938 append(Atom::TableItemRight
);
1939 inTableItem
= false;
1941 if (inTableHeader
) {
1942 append(Atom::TableHeaderRight
);
1943 inTableHeader
= false;
1946 append(Atom::TableRowRight
);
1951 CodeMarker
*DocParser::quoteFromFile()
1953 return Doc::quoteFromFile(location(), quoter
, getArgument());
1956 void DocParser::expandMacro(const QString
&name
,
1960 if (numParams
== 0) {
1961 append(Atom::RawString
, def
);
1967 for (int i
= 0; i
< numParams
; i
++) {
1968 if (numParams
== 1 || isLeftBraceAhead()) {
1969 args
<< getArgument(true);
1972 location().warning(tr("Macro '\\%1' invoked with too few"
1973 " arguments (expected %2, got %3)")
1974 .arg(name
).arg(numParams
).arg(i
));
1980 while (j
< def
.size()) {
1982 if ((def
[j
] == '\\') && (j
< def
.size() - 1) &&
1983 ((paramNo
= def
[j
+ 1].digitValue()) >= 1) &&
1984 (paramNo
<= numParams
)) {
1985 if (!rawString
.isEmpty()) {
1986 append(Atom::RawString
, rawString
);
1989 append(Atom::String
, args
[paramNo
- 1]);
1993 rawString
+= def
[j
++];
1996 if (!rawString
.isEmpty())
1997 append(Atom::RawString
, rawString
);
2001 Doc::SectioningUnit
DocParser::getSectioningUnit()
2003 QString name
= getOptionalArgument();
2005 if (name
== "part") {
2008 else if (name
== "chapter") {
2009 return Doc::Chapter
;
2011 else if (name
== "section1") {
2012 return Doc::Section1
;
2014 else if (name
== "section2") {
2015 return Doc::Section2
;
2017 else if (name
== "section3") {
2018 return Doc::Section3
;
2020 else if (name
== "section4") {
2021 return Doc::Section4
;
2023 else if (name
.isEmpty()) {
2024 return Doc::Section4
;
2027 location().warning(tr("Invalid sectioning unit '%1'").arg(name
));
2032 QString
DocParser::getArgument(bool verbatim
)
2037 skipSpacesOrOneEndl();
2042 Typically, an argument ends at the next white-space. However,
2043 braces can be used to group words:
2047 Also, opening and closing parentheses have to match. Thus,
2051 is an argument too, although it contains spaces. Finally,
2052 trailing punctuation is not included in an argument, nor is 's.
2054 if (pos
< (int) in
.length() && in
[pos
] == '{') {
2056 while (pos
< (int) in
.length() && delimDepth
>= 0) {
2057 switch (in
[pos
].unicode()) {
2065 if (delimDepth
>= 0)
2076 if (pos
< (int) in
.length()) {
2077 if (in
[pos
].isLetterOrNumber())
2080 if (in
[pos
].isSpace()) {
2095 location().warning(tr("Missing '}'"));
2098 while (pos
< in
.length() &&
2099 ((delimDepth
> 0) ||
2100 ((delimDepth
== 0) &&
2101 !in
[pos
].isSpace()))) {
2102 switch (in
[pos
].unicode()) {
2114 if (pos
== startPos
|| delimDepth
>= 0) {
2126 if (pos
< (int) in
.length()) {
2127 if (in
[pos
].isLetterOrNumber())
2130 if (in
[pos
].isSpace()) {
2144 if ((arg
.length() > 1) &&
2145 (QString(".,:;!?").indexOf(in
[pos
- 1]) != -1) &&
2146 !arg
.endsWith("...")) {
2147 arg
.truncate(arg
.length() - 1);
2150 if (arg
.length() > 2 && in
.mid(pos
- 2, 2) == "'s") {
2151 arg
.truncate(arg
.length() - 2);
2155 return arg
.simplified();
2158 QString
DocParser::getOptionalArgument()
2160 skipSpacesOrOneEndl();
2161 if (pos
+ 1 < (int) in
.length() && in
[pos
] == '\\' &&
2162 in
[pos
+ 1].isLetterOrNumber()) {
2166 return getArgument();
2170 QString
DocParser::getRestOfLine()
2176 bool trailingSlash
= false;
2181 while (pos
< in
.size() && in
[pos
] != '\n') {
2182 if (in
[pos
] == '\\' && !trailingSlash
) {
2183 trailingSlash
= true;
2185 while ((pos
< in
.size()) &&
2186 in
[pos
].isSpace() &&
2191 trailingSlash
= false;
2198 t
+= in
.mid(begin
, pos
- begin
).simplified();
2200 if (trailingSlash
) {
2204 if (pos
< in
.size())
2206 } while (pos
< in
.size() && trailingSlash
);
2212 The metacommand argument is normally the remaining text to
2213 the right of the metacommand itself. The extra blanks are
2214 stripped and the argument string is returned.
2216 QString
DocParser::getMetaCommandArgument(const QString
&cmdStr
)
2223 while (pos
< in
.size() && (in
[pos
] != '\n' || parenDepth
> 0)) {
2224 if (in
.at(pos
) == '(')
2226 else if (in
.at(pos
) == ')')
2231 if (pos
== in
.size() && parenDepth
> 0) {
2233 location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr
));
2236 QString t
= in
.mid(begin
, pos
- begin
).simplified();
2241 QString
DocParser::getUntilEnd(int cmd
)
2243 int endCmd
= endCmdFor(cmd
);
2244 QRegExp
rx("\\\\" + cmdName(endCmd
) + "\\b");
2246 int end
= rx
.indexIn(in
, pos
);
2249 location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd
)));
2253 t
= in
.mid(pos
, end
- pos
);
2254 pos
= end
+ rx
.matchedLength();
2259 QString
DocParser::getCode(int cmd
, CodeMarker
*marker
)
2261 QString code
= untabifyEtc(getUntilEnd(cmd
));
2262 int indent
= indentLevel(code
);
2263 if (indent
< minIndent
)
2265 code
= unindent(minIndent
, code
);
2266 marker
= CodeMarker::markerForCode(code
);
2267 return marker
->markedUpCode(code
, 0, "");
2271 Was used only for generating doxygen output.
2273 QString
DocParser::getUnmarkedCode(int cmd
)
2275 QString code
= getUntilEnd(cmd
);
2277 int indent
= indentLevel(code
);
2278 if (indent
< minIndent
)
2280 code
= unindent(minIndent
, code
);
2285 bool DocParser::isBlankLine()
2289 while (i
< len
&& in
[i
].isSpace()) {
2297 bool DocParser::isLeftBraceAhead()
2302 while (i
< len
&& in
[i
].isSpace() && numEndl
< 2) {
2303 // ### bug with '\\'
2308 return numEndl
< 2 && i
< len
&& in
[i
] == '{';
2311 void DocParser::skipSpacesOnLine()
2313 while ((pos
< in
.length()) &&
2314 in
[pos
].isSpace() &&
2315 (in
[pos
].unicode() != '\n'))
2319 void DocParser::skipSpacesOrOneEndl()
2322 while (pos
< (int) in
.length() && in
[pos
].isSpace()) {
2325 if (firstEndl
== -1) {
2337 void DocParser::skipAllSpaces()
2339 while (pos
< len
&& in
[pos
].isSpace())
2343 void DocParser::skipToNextPreprocessorCommand()
2345 QRegExp
rx("\\\\(?:" + cmdName(CMD_IF
) + "|" +
2346 cmdName(CMD_ELSE
) + "|" +
2347 cmdName(CMD_ENDIF
) + ")\\b");
2348 int end
= rx
.indexIn(in
, pos
+ 1); // ### + 1 necessary?
2356 int DocParser::endCmdFor(int cmd
)
2360 return CMD_ENDABSTRACT
;
2364 return CMD_ENDCHAPTER
;
2371 return CMD_ENDQMLTEXT
;
2374 return CMD_ENDFOOTNOTE
;
2376 return CMD_ENDLEGALESE
;
2390 return CMD_ENDQUOTATION
;
2394 return CMD_ENDSECTION1
;
2396 return CMD_ENDSECTION2
;
2398 return CMD_ENDSECTION3
;
2400 return CMD_ENDSECTION4
;
2402 return CMD_ENDSIDEBAR
;
2404 return CMD_ENDTABLE
;
2410 QString
DocParser::cmdName(int cmd
)
2412 return *cmds
[cmd
].alias
;
2415 QString
DocParser::endCmdName(int cmd
)
2417 return cmdName(endCmdFor(cmd
));
2420 QString
DocParser::untabifyEtc(const QString
& str
)
2423 result
.reserve(str
.length());
2426 for (int i
= 0; i
< str
.length(); i
++) {
2427 const QChar c
= str
.at(i
);
2428 if (c
== QLatin1Char('\r'))
2430 if (c
== QLatin1Char('\t')) {
2431 result
+= " " + (column
% tabSize
);
2432 column
= ((column
/ tabSize
) + 1) * tabSize
;
2435 if (c
== QLatin1Char('\n')) {
2436 while (result
.endsWith(QLatin1Char(' ')))
2446 while (result
.endsWith("\n\n"))
2447 result
.truncate(result
.length() - 1);
2448 while (result
.startsWith("\n"))
2449 result
= result
.mid(1);
2454 int DocParser::indentLevel(const QString
& str
)
2456 int minIndent
= INT_MAX
;
2459 for (int i
= 0; i
< (int) str
.length(); i
++) {
2460 if (str
[i
] == '\n') {
2464 if (str
[i
] != ' ' && column
< minIndent
)
2472 QString
DocParser::unindent(int level
, const QString
& str
)
2480 for (int i
= 0; i
< (int) str
.length(); i
++) {
2481 if (str
[i
] == QLatin1Char('\n')) {
2486 if (column
>= level
)
2494 QString
DocParser::slashed(const QString
& str
)
2496 QString result
= str
;
2497 result
.replace("/", "\\/");
2498 return "/" + result
+ "/";
2501 #define COMMAND_BRIEF Doc::alias("brief")
2504 #define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
2508 Doc::Doc(const Location
& start_loc
,
2509 const Location
& end_loc
,
2510 const QString
& source
,
2511 const QSet
<QString
>& metaCommandSet
)
2513 priv
= new DocPrivate(start_loc
,end_loc
,source
);
2515 parser
.parse(source
,priv
,metaCommandSet
);
2518 Doc::Doc(const Doc
& doc
)
2526 if (priv
&& priv
->deref())
2530 Doc
&Doc::operator=(const Doc
& doc
)
2534 if (priv
&& priv
->deref())
2540 void Doc::renameParameters(const QStringList
&oldNames
,
2541 const QStringList
&newNames
)
2543 if (priv
&& oldNames
!= newNames
) {
2546 priv
->params
= newNames
.toSet();
2548 Atom
*atom
= priv
->text
.firstAtom();
2550 if (atom
->type() == Atom::FormattingLeft
2551 && atom
->string() == ATOM_FORMATTING_PARAMETER
) {
2552 atom
= atom
->next();
2555 int index
= oldNames
.indexOf(atom
->string());
2556 if (index
!= -1 && index
< newNames
.count())
2557 atom
->setString(newNames
.at(index
));
2559 atom
= atom
->next();
2564 void Doc::simplifyEnumDoc()
2567 if (priv
->isEnumDocSimplifiable()) {
2572 Atom
*atom
= priv
->text
.firstAtom();
2574 if ((atom
->type() == Atom::ListLeft
) &&
2575 (atom
->string() == ATOM_LIST_VALUE
)) {
2576 while (atom
&& ((atom
->type() != Atom::ListRight
) ||
2577 (atom
->string() != ATOM_LIST_VALUE
)))
2578 atom
= atom
->next();
2580 atom
= atom
->next();
2584 atom
= atom
->next();
2587 priv
->text
= newText
;
2592 void Doc::setBody(const Text
&text
)
2599 Returns the starting location of a qdoc comment.
2601 const Location
&Doc::location() const
2603 static const Location dummy
;
2604 return priv
== 0 ? dummy
: priv
->start_loc
;
2607 const QString
&Doc::source() const
2609 static QString null
;
2610 return priv
== 0 ? null
: priv
->src
;
2613 bool Doc::isEmpty() const
2615 return priv
== 0 || priv
->src
.isEmpty();
2618 const Text
& Doc::body() const
2620 static const Text dummy
;
2621 return priv
== 0 ? dummy
: priv
->text
;
2624 Text
Doc::briefText() const
2626 return body().subText(Atom::BriefLeft
, Atom::BriefRight
);
2629 Text
Doc::trimmedBriefText(const QString
&className
) const
2631 QString classNameOnly
= className
;
2632 if (className
.contains("::"))
2633 classNameOnly
= className
.split("::").last();
2635 Text originalText
= briefText();
2637 const Atom
*atom
= originalText
.firstAtom();
2641 bool standardWording
= true;
2644 This code is really ugly. The entire \brief business
2645 should be rethought.
2648 if (atom
->type() == Atom::AutoLink
|| atom
->type() == Atom::String
) {
2649 briefStr
+= atom
->string();
2651 atom
= atom
->next();
2654 QStringList w
= briefStr
.split(" ");
2655 if (!w
.isEmpty() && w
.first() == "Returns") {
2658 if (!w
.isEmpty() && w
.first() == "The")
2662 tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
2663 .arg(COMMAND_BRIEF
).arg(className
));
2664 standardWording
= false;
2667 if (!w
.isEmpty() && (w
.first() == className
|| w
.first() == classNameOnly
))
2671 tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
2672 .arg(COMMAND_BRIEF
).arg(className
).arg(className
));
2673 standardWording
= false;
2676 if (!w
.isEmpty() && ((w
.first() == "class") ||
2677 (w
.first() == "function") ||
2678 (w
.first() == "macro") ||
2679 (w
.first() == "widget") ||
2680 (w
.first() == "namespace") ||
2681 (w
.first() == "header")))
2685 tr("Nonstandard wording in '\\%1' text for '%2' ("
2686 "expected 'class', 'function', 'macro', 'widget', "
2687 "'namespace' or 'header')")
2688 .arg(COMMAND_BRIEF
).arg(className
));
2689 standardWording
= false;
2692 if (!w
.isEmpty() && (w
.first() == "is" || w
.first() == "provides"))
2695 if (!w
.isEmpty() && (w
.first() == "a" || w
.first() == "an"))
2699 whats
= w
.join(" ");
2701 if (whats
.endsWith("."))
2702 whats
.truncate(whats
.length() - 1);
2704 if (whats
.isEmpty()) {
2706 tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
2707 .arg(COMMAND_BRIEF
).arg(className
));
2708 standardWording
= false;
2711 whats
[0] = whats
[0].toUpper();
2713 // ### move this once \brief is abolished for properties
2714 if (standardWording
)
2715 resultText
<< whats
;
2720 Text
Doc::legaleseText() const
2722 if (priv
== 0 || !priv
->hasLegalese
)
2725 return body().subText(Atom::LegaleseLeft
, Atom::LegaleseRight
);
2728 const QString
& Doc::baseName() const
2730 static QString null
;
2731 if (priv
== 0 || priv
->extra
== 0) {
2735 return priv
->extra
->baseName
;
2739 Doc::SectioningUnit
Doc::granularity() const
2741 if (priv
== 0 || priv
->extra
== 0) {
2742 return DocPrivateExtra().granularity
;
2745 return priv
->extra
->granularity
;
2750 Doc::SectioningUnit
Doc::sectioningUnit() const
2752 if (priv
== 0 || priv
->extra
== 0) {
2753 return DocPrivateExtra().sectioningUnit
;
2756 return priv
->extra
->sectioningUnit
;
2761 const QSet
<QString
> &Doc::parameterNames() const
2763 return priv
== 0 ? *null_Set_QString() : priv
->params
;
2766 const QStringList
&Doc::enumItemNames() const
2768 return priv
== 0 ? *null_QStringList() : priv
->enumItemList
;
2771 const QStringList
&Doc::omitEnumItemNames() const
2773 return priv
== 0 ? *null_QStringList() : priv
->omitEnumItemList
;
2776 const QSet
<QString
> &Doc::metaCommandsUsed() const
2778 return priv
== 0 ? *null_Set_QString() : priv
->metacommandsUsed
;
2781 QStringList
Doc::metaCommandArgs(const QString
& metacommand
) const
2783 return priv
== 0 ? QStringList() : priv
->metaCommandMap
.value(metacommand
);
2786 const QList
<Text
> &Doc::alsoList() const
2788 return priv
== 0 ? *null_QList_Text() : priv
->alsoList
;
2791 bool Doc::hasTableOfContents() const
2793 return priv
&& priv
->extra
&& !priv
->extra
->tableOfContents
.isEmpty();
2796 bool Doc::hasKeywords() const
2798 return priv
&& priv
->extra
&& !priv
->extra
->keywords
.isEmpty();
2801 bool Doc::hasTargets() const
2803 return priv
&& priv
->extra
&& !priv
->extra
->targets
.isEmpty();
2806 const QList
<Atom
*> &Doc::tableOfContents() const
2808 priv
->constructExtra();
2809 return priv
->extra
->tableOfContents
;
2812 const QList
<int> &Doc::tableOfContentsLevels() const
2814 priv
->constructExtra();
2815 return priv
->extra
->tableOfContentsLevels
;
2818 const QList
<Atom
*> &Doc::keywords() const
2820 priv
->constructExtra();
2821 return priv
->extra
->keywords
;
2824 const QList
<Atom
*> &Doc::targets() const
2826 priv
->constructExtra();
2827 return priv
->extra
->targets
;
2830 const QStringMap
&Doc::metaTagMap() const
2832 return priv
&& priv
->extra
? priv
->extra
->metaMap
: *null_QStringMap();
2835 void Doc::initialize(const Config
& config
)
2837 DocParser::tabSize
= config
.getInt(CONFIG_TABSIZE
);
2838 DocParser::exampleFiles
= config
.getStringList(CONFIG_EXAMPLES
);
2839 DocParser::exampleDirs
= config
.getStringList(CONFIG_EXAMPLEDIRS
);
2840 DocParser::sourceFiles
= config
.getStringList(CONFIG_SOURCES
);
2841 DocParser::sourceDirs
= config
.getStringList(CONFIG_SOURCEDIRS
);
2842 DocParser::quoting
= config
.getBool(CONFIG_QUOTINGINFORMATION
);
2845 QmlClassNode::qmlOnly
= config
.getBool(CONFIG_QMLONLY
);
2848 QStringMap reverseAliasMap
;
2850 QSet
<QString
> commands
= config
.subVars(CONFIG_ALIAS
);
2851 QSet
<QString
>::ConstIterator c
= commands
.begin();
2852 while (c
!= commands
.end()) {
2853 QString alias
= config
.getString(CONFIG_ALIAS
+ Config::dot
+ *c
);
2854 if (reverseAliasMap
.contains(alias
)) {
2855 config
.lastLocation().warning(tr("Command name '\\%1' cannot stand"
2856 " for both '\\%2' and '\\%3'")
2858 .arg(reverseAliasMap
[alias
])
2862 reverseAliasMap
.insert(alias
, *c
);
2864 aliasMap()->insert(*c
, alias
);
2869 while (cmds
[i
].english
) {
2870 cmds
[i
].alias
= new QString(alias(cmds
[i
].english
));
2871 cmdHash()->insert(*cmds
[i
].alias
, cmds
[i
].no
);
2873 if (cmds
[i
].no
!= i
)
2874 Location::internalError(tr("command %1 missing").arg(i
));
2878 QSet
<QString
> macroNames
= config
.subVars(CONFIG_MACRO
);
2879 QSet
<QString
>::ConstIterator n
= macroNames
.begin();
2880 while (n
!= macroNames
.end()) {
2881 QString macroDotName
= CONFIG_MACRO
+ Config::dot
+ *n
;
2883 macro
.numParams
= -1;
2884 macro
.defaultDef
= config
.getString(macroDotName
);
2885 if (!macro
.defaultDef
.isEmpty()) {
2886 macro
.defaultDefLocation
= config
.lastLocation();
2887 macro
.numParams
= Config::numParams(macro
.defaultDef
);
2889 bool silent
= false;
2891 QSet
<QString
> formats
= config
.subVars(macroDotName
);
2892 QSet
<QString
>::ConstIterator f
= formats
.begin();
2893 while (f
!= formats
.end()) {
2894 QString def
= config
.getString(macroDotName
+ Config::dot
+ *f
);
2895 if (!def
.isEmpty()) {
2896 macro
.otherDefs
.insert(*f
, def
);
2897 int m
= Config::numParams(macro
.defaultDef
);
2898 if (macro
.numParams
== -1) {
2899 macro
.numParams
= m
;
2901 else if (macro
.numParams
!= m
) {
2903 QString other
= tr("default");
2904 if (macro
.defaultDef
.isEmpty())
2905 other
= macro
.otherDefs
.begin().key();
2906 config
.lastLocation().warning(tr("Macro '\\%1' takes"
2907 " inconsistent number"
2914 .arg(macro
.numParams
));
2917 if (macro
.numParams
< m
)
2918 macro
.numParams
= m
;
2924 if (macro
.numParams
!= -1)
2925 macroHash()->insert(*n
, macro
);
2930 void Doc::terminate()
2932 DocParser::exampleFiles
.clear();
2933 DocParser::exampleDirs
.clear();
2934 DocParser::sourceFiles
.clear();
2935 DocParser::sourceDirs
.clear();
2936 aliasMap()->clear();
2938 macroHash()->clear();
2941 while (cmds
[i
].english
) {
2942 delete cmds
[i
].alias
;
2948 QString
Doc::alias(const QString
&english
)
2950 return aliasMap()->value(english
, english
);
2954 Trims the deadwood out of \a str. i.e., this function
2957 void Doc::trimCStyleComment(Location
& location
, QString
& str
)
2960 Location m
= location
;
2961 bool metAsterColumn
= true;
2962 int asterColumn
= location
.columnNo() + 1;
2965 for (i
= 0; i
< (int) str
.length(); i
++) {
2966 if (m
.columnNo() == asterColumn
) {
2970 metAsterColumn
= true;
2973 if (str
[i
] == '\n') {
2974 if (!metAsterColumn
)
2976 metAsterColumn
= false;
2982 if (cleaned
.length() == str
.length())
2985 for (int i
= 0; i
< 3; i
++)
2986 location
.advance(str
[i
]);
2987 str
= str
.mid(3, str
.length() - 5);
2990 CodeMarker
*Doc::quoteFromFile(const Location
&location
,
2992 const QString
&fileName
)
2998 QString userFriendlyFilePath
;
2999 QString filePath
= Config::findFile(location
,
3000 DocParser::exampleFiles
,
3001 DocParser::exampleDirs
,
3002 fileName
, userFriendlyFilePath
);
3003 if (filePath
.isEmpty()) {
3004 location
.warning(tr("Cannot find example file '%1'").arg(fileName
));
3007 QFile
inFile(filePath
);
3008 if (!inFile
.open(QFile::ReadOnly
)) {
3009 location
.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath
));
3012 QTextStream
inStream(&inFile
);
3013 code
= DocParser::untabifyEtc(inStream
.readAll());
3017 QString dirPath
= QFileInfo(filePath
).path();
3018 CodeMarker
*marker
= CodeMarker::markerForFileName(fileName
);
3019 quoter
.quoteFromFile(userFriendlyFilePath
,
3021 marker
->markedUpCode(code
, 0, dirPath
));
3025 QString
Doc::canonicalTitle(const QString
&title
)
3027 // The code below is equivalent to the following chunk, but _much_
3028 // faster (accounts for ~10% of total running time)
3030 // QRegExp attributeExpr("[^A-Za-z0-9]+");
3031 // QString result = title.toLower();
3032 // result.replace(attributeExpr, " ");
3033 // result = result.simplified();
3034 // result.replace(QLatin1Char(' '), QLatin1Char('-'));
3037 result
.reserve(title
.size());
3039 bool slurping
= false;
3042 for (int i
= 0; i
!= title
.size(); ++i
) {
3043 uint c
= title
.at(i
).unicode();
3044 if (c
>= 'A' && c
<= 'Z')
3046 bool alnum
= (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9');
3048 result
+= QLatin1Char(c
);
3051 lastAlnum
= result
.size();
3053 else if (!slurping
) {
3055 result
+= QLatin1Char('-');
3059 // !alnum && slurping -> nothin
3062 result
.truncate(lastAlnum
);
3069 priv
= new DocPrivate
;
3072 if (priv
->count
== 1)
3077 DocPrivate
*newPriv
= new DocPrivate(*priv
);
3080 newPriv
->extra
= new DocPrivateExtra(*priv
->extra
);