1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the tools applications of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
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 = QString());
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 = QString());
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 = QString());
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, QString()));
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();
2922
This code is really ugly. The entire \brief business
2923
should be rethought.
2926
if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
2927
briefStr += atom->string();
2929
atom = atom->next();
2932
QStringList w = briefStr.split(QLatin1Char(' '));
2933
if (!w.isEmpty() && w.first() == "Returns") {
2936
if (!w.isEmpty() && w.first() == "The")
2939
if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
2942
if (!w.isEmpty() && ((w.first() == "class") ||
2943
(w.first() == "function") ||
2944
(w.first() == "macro") ||
2945
(w.first() == "widget") ||
2946
(w.first() == "namespace") ||
2947
(w.first() == "header")))
2950
if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
2953
if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
2957
whats = w.join(' ');
2959
if (whats.endsWith(QLatin1Char('.')))
2960
whats.truncate(whats.length() - 1);
2962
if (!whats.isEmpty())
2963
whats[0] = whats[0].toUpper();
2965
// ### move this once \brief is abolished for properties
2966
resultText << whats;
2971
Text Doc::legaleseText() const
2973
if (priv == 0 || !priv->hasLegalese)
2976
return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
2979
const QString& Doc::baseName() const
2981
static QString null;
2982
if (priv == 0 || priv->extra == 0) {
2986
return priv->extra->baseName;
2990
Doc::Sections Doc::granularity() const
2992
if (priv == 0 || priv->extra == 0) {
2993
return DocPrivateExtra().granularity;
2996
return priv->extra->granularity;
3000
const QSet<QString> &Doc::parameterNames() const
3002
return priv == 0 ? *null_Set_QString() : priv->params;
3005
const QStringList &Doc::enumItemNames() const
3007
return priv == 0 ? *null_QStringList() : priv->enumItemList;
3010
const QStringList &Doc::omitEnumItemNames() const
3012
return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
3015
const QSet<QString> &Doc::metaCommandsUsed() const
3017
return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
3021
Returns a reference to the list of topic commands used in the
3022
current qdoc comment. Normally there is only one, but there
3023
can be multiple \e{qmlproperty} commands, for example.
3025
const TopicList& Doc::topicsUsed() const
3027
return priv == 0 ? *nullTopicList() : priv->topics;
3030
ArgList Doc::metaCommandArgs(const QString& metacommand) const
3032
return priv == 0 ? ArgList() : priv->metaCommandMap.value(metacommand);
3035
const QList<Text> &Doc::alsoList() const
3037
return priv == 0 ? *null_QList_Text() : priv->alsoList;
3040
bool Doc::hasTableOfContents() const
3042
return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
3045
bool Doc::hasKeywords() const
3047
return priv && priv->extra && !priv->extra->keywords.isEmpty();
3050
bool Doc::hasTargets() const
3052
return priv && priv->extra && !priv->extra->targets.isEmpty();
3055
const QList<Atom *> &Doc::tableOfContents() const
3057
priv->constructExtra();
3058
return priv->extra->tableOfContents;
3061
const QList<int> &Doc::tableOfContentsLevels() const
3063
priv->constructExtra();
3064
return priv->extra->tableOfContentsLevels;
3067
const QList<Atom *> &Doc::keywords() const
3069
priv->constructExtra();
3070
return priv->extra->keywords;
3073
const QList<Atom *> &Doc::targets() const
3075
priv->constructExtra();
3076
return priv->extra->targets;
3079
const QStringMultiMap &Doc::metaTagMap() const
3081
return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
3084
void Doc::initialize(const Config& config)
3086
DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
3087
DocParser::exampleFiles = config.getCleanPathList(CONFIG_EXAMPLES);
3088
DocParser::exampleDirs = config.getCleanPathList(CONFIG_EXAMPLEDIRS);
3089
DocParser::sourceFiles = config.getCleanPathList(CONFIG_SOURCES);
3090
DocParser::sourceDirs = config.getCleanPathList(CONFIG_SOURCEDIRS);
3091
DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
3093
QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
3095
QStringMap reverseAliasMap;
3097
QSet<QString> commands = config.subVars(CONFIG_ALIAS);
3098
QSet<QString>::ConstIterator c = commands.constBegin();
3099
while (c != commands.constEnd()) {
3100
QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
3101
if (reverseAliasMap.contains(alias)) {
3102
config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
3103
" for both '\\%2' and '\\%3'")
3105
.arg(reverseAliasMap[alias])
3109
reverseAliasMap.insert(alias, *c);
3111
aliasMap()->insert(*c, alias);
3116
while (cmds[i].english) {
3117
cmds[i].alias = new QString(alias(cmds[i].english));
3118
cmdHash()->insert(*cmds[i].alias, cmds[i].no);
3120
if (cmds[i].no != i)
3121
Location::internalError(tr("command %1 missing").arg(i));
3125
QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
3126
QSet<QString>::ConstIterator n = macroNames.constBegin();
3127
while (n != macroNames.constEnd()) {
3128
QString macroDotName = CONFIG_MACRO + Config::dot + *n;
3130
macro.numParams = -1;
3131
macro.defaultDef = config.getString(macroDotName);
3132
if (!macro.defaultDef.isEmpty()) {
3133
macro.defaultDefLocation = config.lastLocation();
3134
macro.numParams = Config::numParams(macro.defaultDef);
3136
bool silent = false;
3138
QSet<QString> formats = config.subVars(macroDotName);
3139
QSet<QString>::ConstIterator f = formats.constBegin();
3140
while (f != formats.constEnd()) {
3141
QString def = config.getString(macroDotName + Config::dot + *f);
3142
if (!def.isEmpty()) {
3143
macro.otherDefs.insert(*f, def);
3144
int m = Config::numParams(def);
3145
if (macro.numParams == -1) {
3146
macro.numParams = m;
3148
else if (macro.numParams != m) {
3150
QString other = tr("default");
3151
if (macro.defaultDef.isEmpty())
3152
other = macro.otherDefs.constBegin().key();
3153
config.lastLocation().warning(tr("Macro '\\%1' takes"
3154
" inconsistent number"
3161
.arg(macro.numParams));
3164
if (macro.numParams < m)
3165
macro.numParams = m;
3171
if (macro.numParams != -1)
3172
macroHash()->insert(*n, macro);
3177
void Doc::terminate()
3179
DocParser::exampleFiles.clear();
3180
DocParser::exampleDirs.clear();
3181
DocParser::sourceFiles.clear();
3182
DocParser::sourceDirs.clear();
3183
aliasMap()->clear();
3185
macroHash()->clear();
3188
while (cmds[i].english) {
3189
delete cmds[i].alias;
3195
QString Doc::alias(const QString &english)
3197
return aliasMap()->value(english, english);
3201
Trims the deadwood out of \a str. i.e., this function
3204
void Doc::trimCStyleComment(Location& location, QString& str)
3207
Location m = location;
3208
bool metAsterColumn = true;
3209
int asterColumn = location.columnNo() + 1;
3212
for (i = 0; i < (int) str.length(); i++) {
3213
if (m.columnNo() == asterColumn) {
3217
metAsterColumn = true;
3220
if (str[i] == '\n') {
3221
if (!metAsterColumn)
3223
metAsterColumn = false;
3229
if (cleaned.length() == str.length())
3232
for (int i = 0; i < 3; i++)
3233
location.advance(str[i]);
3234
str = str.mid(3, str.length() - 5);
3237
CodeMarker *Doc::quoteFromFile(const Location &location,
3239
const QString &fileName)
3245
QString userFriendlyFilePath;
3246
QString filePath = Config::findFile(location,
3247
DocParser::exampleFiles,
3248
DocParser::exampleDirs,
3249
fileName, userFriendlyFilePath);
3250
if (filePath.isEmpty()) {
3251
location.warning(tr("Cannot find file to quote from: '%1'").arg(fileName));
3254
QFile inFile(filePath);
3255
if (!inFile.open(QFile::ReadOnly)) {
3256
location.warning(tr("Cannot open file to quote from: '%1'").arg(userFriendlyFilePath));
3259
QTextStream inStream(&inFile);
3260
code = DocParser::untabifyEtc(inStream.readAll());
3264
QString dirPath = QFileInfo(filePath).path();
3265
CodeMarker *marker = CodeMarker::markerForFileName(fileName);
3266
quoter.quoteFromFile(userFriendlyFilePath,
3268
marker->markedUpCode(code, 0, location));
3272
QString Doc::canonicalTitle(const QString &title)
3274
// The code below is equivalent to the following chunk, but _much_
3275
// faster (accounts for ~10% of total running time)
3277
// QRegExp attributeExpr("[^A-Za-z0-9]+");
3278
// QString result = title.toLower();
3279
// result.replace(attributeExpr, " ");
3280
// result = result.simplified();
3281
// result.replace(QLatin1Char(' '), QLatin1Char('-'));
3284
result.reserve(title.size());
3286
bool dashAppended = false;
3289
for (int i = 0; i != title.size(); ++i) {
3290
uint c = title.at(i).unicode();
3291
if (c >= 'A' && c <= 'Z')
3293
bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
3295
result += QLatin1Char(c);
3297
dashAppended = false;
3298
lastAlnum = result.size();
3300
else if (!dashAppended) {
3302
result += QLatin1Char('-');
3303
dashAppended = true;
3306
result.truncate(lastAlnum);
3313
priv = new DocPrivate;
3316
if (priv->count == 1)
3321
DocPrivate *newPriv = new DocPrivate(*priv);
3324
newPriv->extra = new DocPrivateExtra(*priv->extra);
3330
The destructor deletes all the sub-TopicRefs.
3332
TopicRef::~TopicRef()
3334
foreach (DitaRef* t, subrefs_) {
3340
Returns a reference to the structure that will be used
3341
for generating a DITA mao.
3343
const DitaRefList& Doc::ditamap() const { return priv->ditamap_; }