~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/tools/qdoc/doc.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the tools applications of the Qt Toolkit.
 
7
**
 
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.
 
16
**
 
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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "config.h"
 
43
#include "doc.h"
 
44
#include "codemarker.h"
 
45
#include "editdistance.h"
 
46
#include "openedlist.h"
 
47
#include "quoter.h"
 
48
#include "text.h"
 
49
#include "tokenizer.h"
 
50
#include <qdatetime.h>
 
51
#include <qfile.h>
 
52
#include <qfileinfo.h>
 
53
#include <qhash.h>
 
54
#include <qtextstream.h>
 
55
#include <qregexp.h>
 
56
#include <ctype.h>
 
57
#include <limits.h>
 
58
#include <qdebug.h>
 
59
 
 
60
QT_BEGIN_NAMESPACE
 
61
 
 
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)
 
68
 
 
69
struct Macro
 
70
{
 
71
    QString defaultDef;
 
72
    Location defaultDefLocation;
 
73
    QStringMap otherDefs;
 
74
    int numParams;
 
75
};
 
76
 
 
77
enum {
 
78
    CMD_A,
 
79
    CMD_ABSTRACT,
 
80
    CMD_ANNOTATEDLIST,
 
81
    CMD_B,
 
82
    CMD_BADCODE,
 
83
    CMD_BASENAME,
 
84
    CMD_BOLD,
 
85
    CMD_BR,
 
86
    CMD_BRIEF,
 
87
    CMD_C,
 
88
    CMD_CAPTION,
 
89
    CMD_CHAPTER,
 
90
    CMD_CODE,
 
91
    CMD_CODELINE,
 
92
    CMD_DIV,
 
93
    CMD_DOTS,
 
94
    CMD_E,
 
95
    CMD_ELSE,
 
96
    CMD_ENDABSTRACT,
 
97
    CMD_ENDCHAPTER,
 
98
    CMD_ENDCODE,
 
99
    CMD_ENDDIV,
 
100
    CMD_ENDFOOTNOTE,
 
101
    CMD_ENDIF,
 
102
    CMD_ENDLEGALESE,
 
103
    CMD_ENDLINK,
 
104
    CMD_ENDLIST,
 
105
    CMD_ENDMAPREF,
 
106
    CMD_ENDOMIT,
 
107
    CMD_ENDPART,
 
108
    CMD_ENDQUOTATION,
 
109
    CMD_ENDRAW,
 
110
    CMD_ENDSECTION1,
 
111
    CMD_ENDSECTION2,
 
112
    CMD_ENDSECTION3,
 
113
    CMD_ENDSECTION4,
 
114
    CMD_ENDSIDEBAR,
 
115
    CMD_ENDTABLE,
 
116
    CMD_ENDTOPICREF,
 
117
    CMD_FOOTNOTE,
 
118
    CMD_GENERATELIST,
 
119
    CMD_GRANULARITY,
 
120
    CMD_HEADER,
 
121
    CMD_HR,
 
122
    CMD_I,
 
123
    CMD_IF,
 
124
    CMD_IMAGE,
 
125
    CMD_IMPORTANT,
 
126
    CMD_INCLUDE,
 
127
    CMD_INLINEIMAGE,
 
128
    CMD_INDEX,
 
129
    CMD_KEYWORD,
 
130
    CMD_L,
 
131
    CMD_LEGALESE,
 
132
    CMD_LI,
 
133
    CMD_LINK,
 
134
    CMD_LIST,
 
135
    CMD_MAPREF,
 
136
    CMD_META,
 
137
    CMD_NEWCODE,
 
138
    CMD_NOTE,
 
139
    CMD_O,
 
140
    CMD_OLDCODE,
 
141
    CMD_OMIT,
 
142
    CMD_OMITVALUE,
 
143
    CMD_OVERLOAD,
 
144
    CMD_PART,
 
145
    CMD_PRINTLINE,
 
146
    CMD_PRINTTO,
 
147
    CMD_PRINTUNTIL,
 
148
    CMD_QUOTATION,
 
149
    CMD_QUOTEFILE,
 
150
    CMD_QUOTEFROMFILE,
 
151
    CMD_QUOTEFUNCTION,
 
152
    CMD_RAW,
 
153
    CMD_ROW,
 
154
    CMD_SA,
 
155
    CMD_SECTION1,
 
156
    CMD_SECTION2,
 
157
    CMD_SECTION3,
 
158
    CMD_SECTION4,
 
159
    CMD_SIDEBAR,
 
160
    CMD_SINCELIST,
 
161
    CMD_SKIPLINE,
 
162
    CMD_SKIPTO,
 
163
    CMD_SKIPUNTIL,
 
164
    CMD_SNIPPET,
 
165
    CMD_SPAN,
 
166
    CMD_SUB,
 
167
    CMD_SUP,
 
168
    CMD_TABLE,
 
169
    CMD_TABLEOFCONTENTS,
 
170
    CMD_TARGET,
 
171
    CMD_TOPICREF,
 
172
    CMD_TT,
 
173
    CMD_UICONTROL,
 
174
    CMD_UNDERLINE,
 
175
    CMD_UNICODE,
 
176
    CMD_VALUE,
 
177
    CMD_WARNING,
 
178
    CMD_QML,
 
179
    CMD_ENDQML,
 
180
    CMD_CPP,
 
181
    CMD_ENDCPP,
 
182
    CMD_QMLTEXT,
 
183
    CMD_ENDQMLTEXT,
 
184
    CMD_CPPTEXT,
 
185
    CMD_ENDCPPTEXT,
 
186
    CMD_JS,
 
187
    CMD_ENDJS,
 
188
    NOT_A_CMD
 
189
};
 
190
 
 
191
static struct {
 
192
    const char *english;
 
193
    int no;
 
194
    QString *alias;
 
195
} cmds[] = {
 
196
    { "a", CMD_A, 0 },
 
197
    { "abstract", CMD_ABSTRACT, 0 },
 
198
    { "annotatedlist", CMD_ANNOTATEDLIST, 0 },
 
199
    { "b", CMD_B, 0 },
 
200
    { "badcode", CMD_BADCODE, 0 },
 
201
    { "basename", CMD_BASENAME, 0 }, // ### don't document for now
 
202
    { "bold", CMD_BOLD, 0 },
 
203
    { "br", CMD_BR, 0 },
 
204
    { "brief", CMD_BRIEF, 0 },
 
205
    { "c", CMD_C, 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 },
 
212
    { "e", CMD_E, 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 },
 
239
    { "hr", CMD_HR, 0 },
 
240
    { "i", CMD_I, 0 },
 
241
    { "if", CMD_IF, 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 },
 
248
    { "l", CMD_L, 0 },
 
249
    { "legalese", CMD_LEGALESE, 0 },
 
250
    { "li", CMD_LI, 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 },
 
257
    { "o", CMD_O, 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 },
 
272
    { "sa", CMD_SA, 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 },
 
290
    { "tt", CMD_TT, 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 },
 
304
    { "js", CMD_JS, 0 },
 
305
    { "endjs", CMD_ENDJS, 0 },
 
306
    { 0, 0, 0 }
 
307
};
 
308
 
 
309
typedef QHash<QString, int> QHash_QString_int;
 
310
typedef QHash<QString, Macro> QHash_QString_Macro;
 
311
 
 
312
Q_GLOBAL_STATIC(QStringMap, aliasMap)
 
313
Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
 
314
Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
 
315
 
 
316
class DocPrivateExtra
 
317
{
 
318
public:
 
319
    QString             baseName;
 
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;
 
327
 
 
328
    DocPrivateExtra()
 
329
        : granularity(Doc::Part) { }
 
330
};
 
331
 
 
332
struct Shared // ### get rid of
 
333
{
 
334
    Shared()
 
335
        : count(1) { }
 
336
    void ref() { ++count; }
 
337
    bool deref() { return (--count == 0); }
 
338
 
 
339
    int count;
 
340
};
 
341
 
 
342
static QString cleanLink(const QString &link)
 
343
{
 
344
    int colonPos = link.indexOf(':');
 
345
    if ((colonPos == -1) ||
 
346
            (!link.startsWith("file:") && !link.startsWith("mailto:")))
 
347
        return link;
 
348
    return link.mid(colonPos + 1).simplified();
 
349
}
 
350
 
 
351
typedef QMap<QString, ArgList> CommandMap;
 
352
 
 
353
class DocPrivate : public Shared
 
354
{
 
355
public:
 
356
    DocPrivate(const Location& start = Location::null,
 
357
               const Location& end = Location::null,
 
358
               const QString& source = QString());
 
359
    ~DocPrivate();
 
360
 
 
361
    void addAlso(const Text& also);
 
362
    void constructExtra();
 
363
    bool isEnumDocSimplifiable() const;
 
364
 
 
365
    // ### move some of this in DocPrivateExtra
 
366
    Location start_loc;
 
367
    Location end_loc;
 
368
    QString src;
 
369
    Text text;
 
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;
 
379
    TopicList topics;
 
380
    DitaRefList ditamap_;
 
381
};
 
382
 
 
383
DocPrivate::DocPrivate(const Location& start,
 
384
                       const Location& end,
 
385
                       const QString& source)
 
386
    : start_loc(start),
 
387
      end_loc(end),
 
388
      src(source),
 
389
      hasLegalese(false),
 
390
      hasSectioningUnits(false),
 
391
      extra(0)
 
392
{
 
393
    // nothing.
 
394
}
 
395
 
 
396
/*!
 
397
  If the doc is a ditamap, the destructor deletes each element
 
398
  in the ditamap structure. These were allocated as needed.
 
399
 */
 
400
DocPrivate::~DocPrivate()
 
401
{
 
402
    delete extra;
 
403
    foreach (DitaRef* t, ditamap_) {
 
404
        delete t;
 
405
    }
 
406
}
 
407
 
 
408
void DocPrivate::addAlso(const Text& also)
 
409
{
 
410
    alsoList.append(also);
 
411
}
 
412
 
 
413
void DocPrivate::constructExtra()
 
414
{
 
415
    if (extra == 0)
 
416
        extra = new DocPrivateExtra;
 
417
}
 
418
 
 
419
bool DocPrivate::isEnumDocSimplifiable() const
 
420
{
 
421
    bool justMetColon = false;
 
422
    int numValueTables = 0;
 
423
 
 
424
    const Atom *atom = text.firstAtom();
 
425
    while (atom) {
 
426
        if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
 
427
            justMetColon = atom->string().endsWith(QLatin1Char(':'));
 
428
        }
 
429
        else if ((atom->type() == Atom::ListLeft) &&
 
430
                 (atom->string() == ATOM_LIST_VALUE)) {
 
431
            if (justMetColon || numValueTables > 0)
 
432
                return false;
 
433
            ++numValueTables;
 
434
        }
 
435
        atom = atom->next();
 
436
    }
 
437
    return true;
 
438
}
 
439
 
 
440
class DocParser
 
441
{
 
442
public:
 
443
    void parse(const QString &source,
 
444
               DocPrivate *docPrivate,
 
445
               const QSet<QString> &metaCommandSet,
 
446
               const QSet<QString>& possibleTopics);
 
447
 
 
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);
 
455
 
 
456
    static int tabSize;
 
457
    static QStringList exampleFiles;
 
458
    static QStringList exampleDirs;
 
459
    static QStringList sourceFiles;
 
460
    static QStringList sourceDirs;
 
461
    static bool quoting;
 
462
 
 
463
private:
 
464
    Location& location();
 
465
    QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
 
466
                                  const QString& str);
 
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);
 
475
    void parseAlso();
 
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);
 
482
    void startNewPara();
 
483
    void enterPara(Atom::Type leftType = Atom::ParaLeft,
 
484
                   Atom::Type rightType = Atom::ParaRight,
 
485
                   const QString& string = QString());
 
486
    void leavePara();
 
487
    void leaveValue();
 
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);
 
502
 
 
503
    bool isBlankLine();
 
504
    bool isLeftBraceAhead();
 
505
    void skipSpacesOnLine();
 
506
    void skipSpacesOrOneEndl();
 
507
    void skipAllSpaces();
 
508
    void skipToNextPreprocessorCommand();
 
509
 
 
510
    QStack<int> openedInputs;
 
511
 
 
512
    QString in;
 
513
    int pos;
 
514
    int len;
 
515
    Location cachedLoc;
 
516
    int cachedPos;
 
517
 
 
518
    DocPrivate* priv;
 
519
    enum ParagraphState {
 
520
        OutsideParagraph,
 
521
        InSingleLineParagraph,
 
522
        InMultiLineParagraph
 
523
    };
 
524
    ParagraphState paraState;
 
525
    bool inTableHeader;
 
526
    bool inTableRow;
 
527
    bool inTableItem;
 
528
    bool indexStartedPara; // ### rename
 
529
    Atom::Type pendingParaLeftType;
 
530
    Atom::Type pendingParaRightType;
 
531
    QString pendingParaString;
 
532
 
 
533
    int braceDepth;
 
534
    int minIndent;
 
535
    Doc::Sections currentSection;
 
536
    QMap<QString, Location> targetMap;
 
537
    QMap<int, QString> pendingFormats;
 
538
    QStack<int> openedCommands;
 
539
    QStack<OpenedList> openedLists;
 
540
    Quoter quoter;
 
541
    QStack<DitaRef*> ditarefs_;
 
542
};
 
543
 
 
544
int DocParser::tabSize;
 
545
QStringList DocParser::exampleFiles;
 
546
QStringList DocParser::exampleDirs;
 
547
QStringList DocParser::sourceFiles;
 
548
QStringList DocParser::sourceDirs;
 
549
bool DocParser::quoting;
 
550
 
 
551
/*!
 
552
  Parse the \a source string to build a Text data structure
 
553
  in \a docPrivate. The Text data structure is a linked list
 
554
  of Atoms.
 
555
 
 
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.
 
559
 */
 
560
void DocParser::parse(const QString& source,
 
561
                      DocPrivate *docPrivate,
 
562
                      const QSet<QString>& metaCommandSet,
 
563
                      const QSet<QString>& possibleTopics)
 
564
{
 
565
    in = source;
 
566
    pos = 0;
 
567
    len = in.length();
 
568
    cachedLoc = docPrivate->start_loc;
 
569
    cachedPos = 0;
 
570
    priv = docPrivate;
 
571
    priv->text << Atom::Nop;
 
572
    priv->topics.clear();
 
573
 
 
574
    paraState = OutsideParagraph;
 
575
    inTableHeader = false;
 
576
    inTableRow = false;
 
577
    inTableItem = false;
 
578
    indexStartedPara = false;
 
579
    pendingParaLeftType = Atom::Nop;
 
580
    pendingParaRightType = Atom::Nop;
 
581
 
 
582
    braceDepth = 0;
 
583
    minIndent = INT_MAX;
 
584
    currentSection = Doc::NoSection;
 
585
    openedCommands.push(CMD_OMIT);
 
586
    quoter.reset();
 
587
 
 
588
    CodeMarker *marker = 0;
 
589
    Atom *currentLinkAtom = 0;
 
590
    QString p1, p2;
 
591
    QStack<bool> preprocessorSkipping;
 
592
    int numPreprocessorSkipping = 0;
 
593
 
 
594
    while (pos < len) {
 
595
        QChar ch = in.at(pos);
 
596
 
 
597
        switch (ch.unicode()) {
 
598
        case '\\':
 
599
        {
 
600
            QString cmdStr;
 
601
            pos++;
 
602
            while (pos < len) {
 
603
                ch = in.at(pos);
 
604
                if (ch.isLetterOrNumber()) {
 
605
                    cmdStr += ch;
 
606
                    pos++;
 
607
                }
 
608
                else {
 
609
                    break;
 
610
                }
 
611
            }
 
612
            if (cmdStr.isEmpty()) {
 
613
                if (pos < len) {
 
614
                    enterPara();
 
615
                    if (in.at(pos).isSpace()) {
 
616
                        skipAllSpaces();
 
617
                        appendChar(QLatin1Char(' '));
 
618
                    }
 
619
                    else {
 
620
                        appendChar(in.at(pos++));
 
621
                    }
 
622
                }
 
623
            }
 
624
            else {
 
625
                int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
 
626
                switch (cmd) {
 
627
                case CMD_A:
 
628
                    enterPara();
 
629
                    p1 = getArgument();
 
630
                    append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
 
631
                    append(Atom::String, p1);
 
632
                    append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
 
633
                    priv->params.insert(p1);
 
634
                    break;
 
635
                case CMD_ABSTRACT:
 
636
                    if (openCommand(cmd)) {
 
637
                        leavePara();
 
638
                        append(Atom::AbstractLeft);
 
639
                    }
 
640
                    break;
 
641
                case CMD_BADCODE:
 
642
                    leavePara();
 
643
                    append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
 
644
                    break;
 
645
                case CMD_BASENAME:
 
646
                    leavePara();
 
647
                    insertBaseName(getArgument());
 
648
                    break;
 
649
                case CMD_BR:
 
650
                    leavePara();
 
651
                    append(Atom::BR);
 
652
                    break;
 
653
                case CMD_BOLD:
 
654
                    location().warning(tr("'\\bold' is deprecated. Use '\\b'"));
 
655
                case CMD_B:
 
656
                    startFormat(ATOM_FORMATTING_BOLD, cmd);
 
657
                    break;
 
658
                case CMD_BRIEF:
 
659
                    leavePara();
 
660
                    enterPara(Atom::BriefLeft, Atom::BriefRight);
 
661
                    break;
 
662
                case CMD_C:
 
663
                    enterPara();
 
664
                    p1 = untabifyEtc(getArgument(true));
 
665
                    marker = CodeMarker::markerForCode(p1);
 
666
                    append(Atom::C, marker->markedUpCode(p1, 0, location()));
 
667
                    break;
 
668
                case CMD_CAPTION:
 
669
                    leavePara();
 
670
                    enterPara(Atom::CaptionLeft, Atom::CaptionRight);
 
671
                    break;
 
672
                case CMD_CHAPTER:
 
673
                    startSection(Doc::Chapter, cmd);
 
674
                    break;
 
675
                case CMD_CODE:
 
676
                    leavePara();
 
677
                    append(Atom::Code, getCode(CMD_CODE, 0));
 
678
                    break;
 
679
                case CMD_QML:
 
680
                    leavePara();
 
681
                    append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
 
682
                    break;
 
683
                case CMD_QMLTEXT:
 
684
                    append(Atom::QmlText);
 
685
                    break;
 
686
                case CMD_JS:
 
687
                    leavePara();
 
688
                    append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
 
689
                    break;
 
690
                case CMD_DIV:
 
691
                    leavePara();
 
692
                    p1 = getArgument(true);
 
693
                    append(Atom::DivLeft, p1);
 
694
                    openedCommands.push(cmd);
 
695
                    break;
 
696
                case CMD_ENDDIV:
 
697
                    leavePara();
 
698
                    append(Atom::DivRight);
 
699
                    closeCommand(cmd);
 
700
                    break;
 
701
                case CMD_CODELINE:
 
702
                {
 
703
                    if (!quoting) {
 
704
                        if (priv->text.lastAtom()->type() == Atom::Code
 
705
                                && priv->text.lastAtom()->string().endsWith("\n\n"))
 
706
                            priv->text.lastAtom()->chopString();
 
707
                        appendToCode("\n");
 
708
                    }
 
709
                    else {
 
710
                        append(Atom::CodeQuoteCommand, cmdStr);
 
711
                        append(Atom::CodeQuoteArgument, " ");
 
712
                    }
 
713
                }
 
714
                    break;
 
715
                case CMD_DOTS:
 
716
                {
 
717
                    if (!quoting) {
 
718
                        if (priv->text.lastAtom()->type() == Atom::Code
 
719
                                && priv->text.lastAtom()->string().endsWith("\n\n"))
 
720
                            priv->text.lastAtom()->chopString();
 
721
 
 
722
                        QString arg = getOptionalArgument();
 
723
                        int indent = 4;
 
724
                        if (!arg.isEmpty())
 
725
                            indent = arg.toInt();
 
726
                        for (int i = 0; i < indent; ++i)
 
727
                            appendToCode(" ");
 
728
                        appendToCode("...\n");
 
729
                    }
 
730
                    else {
 
731
                        append(Atom::CodeQuoteCommand, cmdStr);
 
732
                        QString arg = getOptionalArgument();
 
733
                        if (arg.isEmpty())
 
734
                            arg = "4";
 
735
                        append(Atom::CodeQuoteArgument, arg);
 
736
                    }
 
737
                }
 
738
                    break;
 
739
                case CMD_ELSE:
 
740
                    if (preprocessorSkipping.size() > 0) {
 
741
                        if (preprocessorSkipping.top()) {
 
742
                            --numPreprocessorSkipping;
 
743
                        }
 
744
                        else {
 
745
                            ++numPreprocessorSkipping;
 
746
                        }
 
747
                        preprocessorSkipping.top() = !preprocessorSkipping.top();
 
748
                        (void)getRestOfLine(); // ### should ensure that it's empty
 
749
                        if (numPreprocessorSkipping)
 
750
                            skipToNextPreprocessorCommand();
 
751
                    }
 
752
                    else {
 
753
                        location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
 
754
                    }
 
755
                    break;
 
756
                case CMD_ENDABSTRACT:
 
757
                    if (closeCommand(cmd)) {
 
758
                        leavePara();
 
759
                        append(Atom::AbstractRight);
 
760
                    }
 
761
                    break;
 
762
                case CMD_ENDCHAPTER:
 
763
                    endSection(Doc::Chapter, cmd);
 
764
                    break;
 
765
                case CMD_ENDCODE:
 
766
                    closeCommand(cmd);
 
767
                    break;
 
768
                case CMD_ENDQML:
 
769
                    closeCommand(cmd);
 
770
                    break;
 
771
                case CMD_ENDQMLTEXT:
 
772
                    append(Atom::EndQmlText);
 
773
                    break;
 
774
                case CMD_ENDJS:
 
775
                    closeCommand(cmd);
 
776
                    break;
 
777
                case CMD_ENDFOOTNOTE:
 
778
                    if (closeCommand(cmd)) {
 
779
                        leavePara();
 
780
                        append(Atom::FootnoteRight);
 
781
                        paraState = InMultiLineParagraph; // ###
 
782
                    }
 
783
                    break;
 
784
                case CMD_ENDIF:
 
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();
 
791
                    }
 
792
                    else {
 
793
                        location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
 
794
                    }
 
795
                    break;
 
796
                case CMD_ENDLEGALESE:
 
797
                    if (closeCommand(cmd)) {
 
798
                        leavePara();
 
799
                        append(Atom::LegaleseRight);
 
800
                    }
 
801
                    break;
 
802
                case CMD_ENDLINK:
 
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);
 
808
                    }
 
809
                    break;
 
810
                case CMD_ENDLIST:
 
811
                    if (closeCommand(cmd)) {
 
812
                        leavePara();
 
813
                        if (openedLists.top().isStarted()) {
 
814
                            append(Atom::ListItemRight,
 
815
                                   openedLists.top().styleString());
 
816
                            append(Atom::ListRight,
 
817
                                   openedLists.top().styleString());
 
818
                        }
 
819
                        openedLists.pop();
 
820
                    }
 
821
                    break;
 
822
                case CMD_ENDMAPREF:
 
823
                case CMD_ENDTOPICREF:
 
824
                    if (closeCommand(cmd)) {
 
825
                        ditarefs_.pop(); // zzz
 
826
                    }
 
827
                    break;
 
828
                case CMD_ENDOMIT:
 
829
                    closeCommand(cmd);
 
830
                    break;
 
831
                case CMD_ENDPART:
 
832
                    endSection(Doc::Part, cmd);
 
833
                    break;
 
834
                case CMD_ENDQUOTATION:
 
835
                    if (closeCommand(cmd)) {
 
836
                        leavePara();
 
837
                        append(Atom::QuotationRight);
 
838
                    }
 
839
                    break;
 
840
                case CMD_ENDRAW:
 
841
                    location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
 
842
                    break;
 
843
                case CMD_ENDSECTION1:
 
844
                    endSection(Doc::Section1, cmd);
 
845
                    break;
 
846
                case CMD_ENDSECTION2:
 
847
                    endSection(Doc::Section2, cmd);
 
848
                    break;
 
849
                case CMD_ENDSECTION3:
 
850
                    endSection(Doc::Section3, cmd);
 
851
                    break;
 
852
                case CMD_ENDSECTION4:
 
853
                    endSection(Doc::Section4, cmd);
 
854
                    break;
 
855
                case CMD_ENDSIDEBAR:
 
856
                    if (closeCommand(cmd)) {
 
857
                        leavePara();
 
858
                        append(Atom::SidebarRight);
 
859
                    }
 
860
                    break;
 
861
                case CMD_ENDTABLE:
 
862
                    if (closeCommand(cmd)) {
 
863
                        leaveTableRow();
 
864
                        append(Atom::TableRight);
 
865
                    }
 
866
                    break;
 
867
                case CMD_FOOTNOTE:
 
868
                    if (openCommand(cmd)) {
 
869
                        enterPara();
 
870
                        append(Atom::FootnoteLeft);
 
871
                        paraState = OutsideParagraph; // ###
 
872
                    }
 
873
                    break;
 
874
                case CMD_ANNOTATEDLIST:
 
875
                    append(Atom::AnnotatedList, getArgument());
 
876
                    break;
 
877
                case CMD_SINCELIST:
 
878
                    append(Atom::SinceList, getRestOfLine().simplified());
 
879
                    break;
 
880
                case CMD_GENERATELIST:
 
881
                    append(Atom::GeneratedList, getArgument());
 
882
                    break;
 
883
                case CMD_GRANULARITY:
 
884
                    priv->constructExtra();
 
885
                    priv->extra->granularity = getSectioningUnit();
 
886
                    break;
 
887
                case CMD_HEADER:
 
888
                    if (openedCommands.top() == CMD_TABLE) {
 
889
                        leaveTableRow();
 
890
                        append(Atom::TableHeaderLeft);
 
891
                        inTableHeader = true;
 
892
                    }
 
893
                    else {
 
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())));
 
898
                        }
 
899
                        else {
 
900
                            location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
 
901
                                               .arg(cmdName(CMD_HEADER))
 
902
                                               .arg(cmdName(CMD_TABLE)));
 
903
                        }
 
904
                    }
 
905
                    break;
 
906
                case CMD_I:
 
907
                    location().warning(tr("'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
 
908
                case CMD_E:
 
909
                    startFormat(ATOM_FORMATTING_ITALIC, cmd);
 
910
                    break;
 
911
                case CMD_HR:
 
912
                    leavePara();
 
913
                    append(Atom::HR);
 
914
                    break;
 
915
                case CMD_IF:
 
916
                    preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
 
917
                    if (preprocessorSkipping.top())
 
918
                        ++numPreprocessorSkipping;
 
919
                    if (numPreprocessorSkipping)
 
920
                        skipToNextPreprocessorCommand();
 
921
                    break;
 
922
                case CMD_IMAGE:
 
923
                    leaveValueList();
 
924
                    append(Atom::Image, getArgument());
 
925
                    append(Atom::ImageText, getRestOfLine());
 
926
                    break;
 
927
                case CMD_IMPORTANT:
 
928
                    leavePara();
 
929
                    enterPara(Atom::ImportantLeft, Atom::ImportantRight);
 
930
                    break;
 
931
                case CMD_INCLUDE:
 
932
                {
 
933
                    QString fileName = getArgument();
 
934
                    QString identifier = getRestOfLine();
 
935
                    include(fileName, identifier);
 
936
                }
 
937
                    break;
 
938
                case CMD_INLINEIMAGE:
 
939
                    enterPara();
 
940
                    append(Atom::InlineImage, getArgument());
 
941
                    append(Atom::ImageText, getRestOfLine());
 
942
                    append(Atom::String, " ");
 
943
                    break;
 
944
                case CMD_INDEX:
 
945
                    if (paraState == OutsideParagraph) {
 
946
                        enterPara();
 
947
                        indexStartedPara = true;
 
948
                    }
 
949
                    else {
 
950
                        const Atom *last = priv->text.lastAtom();
 
951
                        if (indexStartedPara &&
 
952
                                (last->type() != Atom::FormattingRight ||
 
953
                                 last->string() != ATOM_FORMATTING_INDEX))
 
954
                            indexStartedPara = false;
 
955
                    }
 
956
                    startFormat(ATOM_FORMATTING_INDEX, cmd);
 
957
                    break;
 
958
                case CMD_KEYWORD:
 
959
                    insertTarget(getRestOfLine(),true);
 
960
                    break;
 
961
                case CMD_L:
 
962
                    enterPara();
 
963
                    if (isLeftBraceAhead()) {
 
964
                        p1 = getArgument();
 
965
                        append(Atom::Link, p1);
 
966
                        if (isLeftBraceAhead()) {
 
967
                            currentLinkAtom = priv->text.lastAtom();
 
968
                            startFormat(ATOM_FORMATTING_LINK, cmd);
 
969
                        }
 
970
                        else {
 
971
                            append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
 
972
                            append(Atom::String, cleanLink(p1));
 
973
                            append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
 
974
                        }
 
975
                    }
 
976
                    else {
 
977
                        p1 = getArgument();
 
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);
 
982
                    }
 
983
                    break;
 
984
                case CMD_LEGALESE:
 
985
                    leavePara();
 
986
                    if (openCommand(cmd))
 
987
                        append(Atom::LegaleseLeft);
 
988
                    docPrivate->hasLegalese = true;
 
989
                    break;
 
990
                case CMD_LINK:
 
991
                    if (openCommand(cmd)) {
 
992
                        enterPara();
 
993
                        p1 = getArgument();
 
994
                        append(Atom::Link, p1);
 
995
                        append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
 
996
                        skipSpacesOrOneEndl();
 
997
                    }
 
998
                    break;
 
999
                case CMD_LIST:
 
1000
                    if (openCommand(cmd)) {
 
1001
                        leavePara();
 
1002
                        openedLists.push(OpenedList(location(),
 
1003
                                                    getOptionalArgument()));
 
1004
                    }
 
1005
                    break;
 
1006
                case CMD_TOPICREF:
 
1007
                case CMD_MAPREF:
 
1008
                    if (openCommand(cmd)) {
 
1009
                        DitaRef* t = 0;
 
1010
                        if (cmd == CMD_MAPREF)
 
1011
                            t = new MapRef();
 
1012
                        else
 
1013
                            t = new TopicRef();
 
1014
                        t->setNavtitle(getArgument(true));
 
1015
                        if (cmd == CMD_MAPREF)
 
1016
                            t->setHref(getArgument());
 
1017
                        else
 
1018
                            t->setHref(getOptionalArgument());
 
1019
                        if (ditarefs_.isEmpty())
 
1020
                            priv->ditamap_.append(t);
 
1021
                        else
 
1022
                            ditarefs_.top()->appendSubref(t);
 
1023
                        ditarefs_.push(t);
 
1024
                    }
 
1025
                    break;
 
1026
                case CMD_META:
 
1027
                    priv->constructExtra();
 
1028
                    p1 = getArgument();
 
1029
                    priv->extra->metaMap.insert(p1, getArgument());
 
1030
                    break;
 
1031
                case CMD_NEWCODE:
 
1032
                    location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
 
1033
                    break;
 
1034
                case CMD_NOTE:
 
1035
                    leavePara();
 
1036
                    enterPara(Atom::NoteLeft, Atom::NoteRight);
 
1037
                    break;
 
1038
                case CMD_O:
 
1039
                    location().warning(tr("'\\o' is deprecated. Use '\\li'"));
 
1040
                case CMD_LI:
 
1041
                    leavePara();
 
1042
                    if (openedCommands.top() == CMD_LIST) {
 
1043
                        if (openedLists.top().isStarted()) {
 
1044
                            append(Atom::ListItemRight,
 
1045
                                   openedLists.top().styleString());
 
1046
                        }
 
1047
                        else {
 
1048
                            append(Atom::ListLeft,
 
1049
                                   openedLists.top().styleString());
 
1050
                        }
 
1051
                        openedLists.top().next();
 
1052
                        append(Atom::ListItemNumber,
 
1053
                               openedLists.top().numberString());
 
1054
                        append(Atom::ListItemLeft,
 
1055
                               openedLists.top().styleString());
 
1056
                        enterPara();
 
1057
                    }
 
1058
                    else if (openedCommands.top() == CMD_TABLE) {
 
1059
                        p1 = "1,1";
 
1060
                        p2.clear();
 
1061
                        if (isLeftBraceAhead()) {
 
1062
                            p1 = getArgument();
 
1063
                            if (isLeftBraceAhead()) {
 
1064
                                p2 = getArgument();
 
1065
                            }
 
1066
                        }
 
1067
 
 
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);
 
1074
                            inTableRow = true;
 
1075
                        }
 
1076
                        else if (inTableItem) {
 
1077
                            append(Atom::TableItemRight);
 
1078
                            inTableItem = false;
 
1079
                        }
 
1080
 
 
1081
                        append(Atom::TableItemLeft, p1, p2);
 
1082
                        inTableItem = true;
 
1083
                    }
 
1084
                    else {
 
1085
                        location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
 
1086
                                           .arg(cmdName(cmd))
 
1087
                                           .arg(cmdName(CMD_LIST))
 
1088
                                           .arg(cmdName(CMD_TABLE)));
 
1089
                    }
 
1090
                    break;
 
1091
                case CMD_OLDCODE:
 
1092
                    leavePara();
 
1093
                    append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
 
1094
                    append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
 
1095
                    break;
 
1096
                case CMD_OMIT:
 
1097
                    getUntilEnd(cmd);
 
1098
                    break;
 
1099
                case CMD_OMITVALUE:
 
1100
                    p1 = getArgument();
 
1101
                    if (!priv->enumItemList.contains(p1))
 
1102
                        priv->enumItemList.append(p1);
 
1103
                    if (!priv->omitEnumItemList.contains(p1))
 
1104
                        priv->omitEnumItemList.append(p1);
 
1105
                    break;
 
1106
                case CMD_PART:
 
1107
                    startSection(Doc::Part, cmd);
 
1108
                    break;
 
1109
                case CMD_PRINTLINE:
 
1110
                    leavePara();
 
1111
                    if (!quoting)
 
1112
                        appendToCode(quoter.quoteLine(location(), cmdStr,
 
1113
                                                      getRestOfLine()));
 
1114
                    else {
 
1115
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1116
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1117
                    }
 
1118
                    break;
 
1119
                case CMD_PRINTTO:
 
1120
                    leavePara();
 
1121
                    if (!quoting)
 
1122
                        appendToCode(quoter.quoteTo(location(), cmdStr,
 
1123
                                                    getRestOfLine()));
 
1124
                    else {
 
1125
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1126
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1127
                    }
 
1128
                    break;
 
1129
                case CMD_PRINTUNTIL:
 
1130
                    leavePara();
 
1131
                    if (!quoting)
 
1132
                        appendToCode(quoter.quoteUntil(location(), cmdStr,
 
1133
                                                       getRestOfLine()));
 
1134
                    else {
 
1135
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1136
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1137
                    }
 
1138
                    break;
 
1139
                case CMD_QUOTATION:
 
1140
                    if (openCommand(cmd)) {
 
1141
                        leavePara();
 
1142
                        append(Atom::QuotationLeft);
 
1143
                    }
 
1144
                    break;
 
1145
                case CMD_QUOTEFILE:
 
1146
                {
 
1147
                    leavePara();
 
1148
                    QString fileName = getArgument();
 
1149
                    Doc::quoteFromFile(location(), quoter, fileName);
 
1150
                    if (!quoting) {
 
1151
                        append(Atom::Code,
 
1152
                               quoter.quoteTo(location(), cmdStr, QString()));
 
1153
                        quoter.reset();
 
1154
                    }
 
1155
                    else {
 
1156
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1157
                        append(Atom::CodeQuoteArgument, fileName);
 
1158
                    }
 
1159
                    break;
 
1160
                }
 
1161
                case CMD_QUOTEFROMFILE:
 
1162
                    leavePara();
 
1163
                    if (!quoting)
 
1164
                        quoteFromFile();
 
1165
                    else {
 
1166
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1167
                        append(Atom::CodeQuoteArgument, getArgument());
 
1168
                    }
 
1169
                    break;
 
1170
                case CMD_QUOTEFUNCTION:
 
1171
                    leavePara();
 
1172
                    marker = quoteFromFile();
 
1173
                    p1 = getRestOfLine();
 
1174
                    if (!quoting) {
 
1175
                        quoter.quoteTo(location(), cmdStr,
 
1176
                                       slashed(marker->functionBeginRegExp(p1)));
 
1177
                        append(Atom::Code,
 
1178
                               quoter.quoteUntil(location(), cmdStr,
 
1179
                                                 slashed(marker->functionEndRegExp(p1))));
 
1180
                        quoter.reset();
 
1181
                    }
 
1182
                    else {
 
1183
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1184
                        append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
 
1185
                    }
 
1186
                    break;
 
1187
                case CMD_RAW:
 
1188
                    leavePara();
 
1189
                    p1 = getRestOfLine();
 
1190
                    if (p1.isEmpty())
 
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);
 
1197
                    break;
 
1198
                case CMD_ROW:
 
1199
                    if (openedCommands.top() == CMD_TABLE) {
 
1200
                        p1.clear();
 
1201
                        if (isLeftBraceAhead())
 
1202
                            p1 = getArgument(true);
 
1203
                        leaveTableRow();
 
1204
                        append(Atom::TableRowLeft,p1);
 
1205
                        inTableRow = true;
 
1206
                    }
 
1207
                    else {
 
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())));
 
1212
                        }
 
1213
                        else {
 
1214
                            location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
 
1215
                                               .arg(cmdName(CMD_ROW))
 
1216
                                               .arg(cmdName(CMD_TABLE)));
 
1217
                        }
 
1218
                    }
 
1219
                    break;
 
1220
                case CMD_SA:
 
1221
                    parseAlso();
 
1222
                    break;
 
1223
                case CMD_SECTION1:
 
1224
                    startSection(Doc::Section1, cmd);
 
1225
                    break;
 
1226
                case CMD_SECTION2:
 
1227
                    startSection(Doc::Section2, cmd);
 
1228
                    break;
 
1229
                case CMD_SECTION3:
 
1230
                    startSection(Doc::Section3, cmd);
 
1231
                    break;
 
1232
                case CMD_SECTION4:
 
1233
                    startSection(Doc::Section4, cmd);
 
1234
                    break;
 
1235
                case CMD_SIDEBAR:
 
1236
                    if (openCommand(cmd)) {
 
1237
                        leavePara();
 
1238
                        append(Atom::SidebarLeft);
 
1239
                    }
 
1240
                    break;
 
1241
                case CMD_SKIPLINE:
 
1242
                    leavePara();
 
1243
                    if (!quoting)
 
1244
                        quoter.quoteLine(location(),
 
1245
                                         cmdStr,
 
1246
                                         getRestOfLine());
 
1247
                    else {
 
1248
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1249
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1250
                    }
 
1251
                    break;
 
1252
                case CMD_SKIPTO:
 
1253
                    leavePara();
 
1254
                    if (!quoting)
 
1255
                        quoter.quoteTo(location(),
 
1256
                                       cmdStr,
 
1257
                                       getRestOfLine());
 
1258
                    else {
 
1259
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1260
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1261
                    }
 
1262
                    break;
 
1263
                case CMD_SKIPUNTIL:
 
1264
                    leavePara();
 
1265
                    if (!quoting)
 
1266
                        quoter.quoteUntil(location(),
 
1267
                                          cmdStr,
 
1268
                                          getRestOfLine());
 
1269
                    else {
 
1270
                        append(Atom::CodeQuoteCommand, cmdStr);
 
1271
                        append(Atom::CodeQuoteArgument, getRestOfLine());
 
1272
                    }
 
1273
                    break;
 
1274
                case CMD_SPAN:
 
1275
                    p1 = ATOM_FORMATTING_SPAN + getArgument(true);
 
1276
                    startFormat(p1, cmd);
 
1277
                    break;
 
1278
                case CMD_SNIPPET:
 
1279
                    leavePara();
 
1280
                {
 
1281
                    QString snippet = getArgument();
 
1282
                    QString identifier = getRestOfLine();
 
1283
                    if (quoting) {
 
1284
                        append(Atom::SnippetCommand, cmdStr);
 
1285
                        append(Atom::SnippetLocation, snippet);
 
1286
                        append(Atom::SnippetIdentifier, identifier);
 
1287
                    }
 
1288
                    else {
 
1289
                        marker = Doc::quoteFromFile(location(),quoter,snippet);
 
1290
                        appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
 
1291
                    }
 
1292
                }
 
1293
                    break;
 
1294
                case CMD_SUB:
 
1295
                    startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
 
1296
                    break;
 
1297
                case CMD_SUP:
 
1298
                    startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
 
1299
                    break;
 
1300
                case CMD_TABLE:
 
1301
                    //p1 = getRestOfLine();
 
1302
                    p1 = getOptionalArgument();
 
1303
                    p2 = getOptionalArgument();
 
1304
                    if (openCommand(cmd)) {
 
1305
                        leavePara();
 
1306
                        append(Atom::TableLeft, p1, p2);
 
1307
                        inTableHeader = false;
 
1308
                        inTableRow = false;
 
1309
                        inTableItem = false;
 
1310
                    }
 
1311
                    break;
 
1312
                case CMD_TABLEOFCONTENTS:
 
1313
                    p1 = "1";
 
1314
                    if (isLeftBraceAhead())
 
1315
                        p1 = getArgument();
 
1316
                    p1 += QLatin1Char(',');
 
1317
                    p1 += QString::number((int)getSectioningUnit());
 
1318
                    append(Atom::TableOfContents, p1);
 
1319
                    break;
 
1320
                case CMD_TARGET:
 
1321
                    insertTarget(getRestOfLine(),false);
 
1322
                    break;
 
1323
                case CMD_TT:
 
1324
                    startFormat(ATOM_FORMATTING_TELETYPE, cmd);
 
1325
                    break;
 
1326
                case CMD_UICONTROL:
 
1327
                    startFormat(ATOM_FORMATTING_UICONTROL, cmd);
 
1328
                    break;
 
1329
                case CMD_UNDERLINE:
 
1330
                    startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
 
1331
                    break;
 
1332
                case CMD_UNICODE:
 
1333
                    enterPara();
 
1334
                    p1 = getArgument();
 
1335
                {
 
1336
                    bool ok;
 
1337
                    uint unicodeChar = p1.toUInt(&ok, 0);
 
1338
                    if (!ok ||
 
1339
                            (unicodeChar == 0x0000) ||
 
1340
                            (unicodeChar > 0xFFFE)) {
 
1341
                        location().warning(tr("Invalid Unicode character '%1' specified "
 
1342
                                              "with '%2'")
 
1343
                                           .arg(p1, cmdName(CMD_UNICODE)));
 
1344
                    }
 
1345
                    else {
 
1346
                        append(Atom::String, QChar(unicodeChar));
 
1347
                    }
 
1348
                }
 
1349
                    break;
 
1350
                case CMD_VALUE:
 
1351
                    leaveValue();
 
1352
                    if (openedLists.top().style() == OpenedList::Value) {
 
1353
                        p1 = getArgument();
 
1354
                        if (!priv->enumItemList.contains(p1))
 
1355
                            priv->enumItemList.append(p1);
 
1356
 
 
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);
 
1362
 
 
1363
                        skipSpacesOrOneEndl();
 
1364
                        if (isBlankLine())
 
1365
                            append(Atom::Nop);
 
1366
                    }
 
1367
                    else {
 
1368
                        // ### problems
 
1369
                    }
 
1370
                    break;
 
1371
                case CMD_WARNING:
 
1372
                    leavePara();
 
1373
                    enterPara();
 
1374
                    append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
 
1375
                    append(Atom::String, "Warning:");
 
1376
                    append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
 
1377
                    append(Atom::String, " ");
 
1378
                    break;
 
1379
                case CMD_OVERLOAD:
 
1380
                    priv->metacommandsUsed.insert(cmdStr);
 
1381
                    p1.clear();
 
1382
                    if (!isBlankLine())
 
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);
 
1390
                    }
 
1391
                    else {
 
1392
                        append(Atom::ParaLeft);
 
1393
                        append(Atom::String,"This is an overloaded function.");
 
1394
                        append(Atom::ParaRight);
 
1395
                        p1 = getMetaCommandArgument(cmdStr);
 
1396
                    }
 
1397
                    priv->metaCommandMap[cmdStr].append(ArgLocPair(p1,location()));
 
1398
                    break;
 
1399
                case NOT_A_CMD:
 
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));
 
1406
                        }
 
1407
                    }
 
1408
                    else if (macroHash()->contains(cmdStr)) {
 
1409
                        const Macro &macro = 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);
 
1416
                            ++d;
 
1417
 
 
1418
                            if (d == macro.otherDefs.constEnd()) {
 
1419
                                append(Atom::FormatEndif);
 
1420
                            }
 
1421
                            else {
 
1422
                                append(Atom::FormatElse);
 
1423
                                numPendingFi++;
 
1424
                            }
 
1425
                        }
 
1426
                        while (numPendingFi-- > 0)
 
1427
                            append(Atom::FormatEndif);
 
1428
 
 
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"));
 
1435
                            }
 
1436
                            else {
 
1437
                                location().push(macro.defaultDefLocation.filePath());
 
1438
                                in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
 
1439
                                len = in.length();
 
1440
                                openedInputs.push(pos + macro.defaultDef.length());
 
1441
                            }
 
1442
                        }
 
1443
                    }
 
1444
                    else {
 
1445
                        location().warning(
 
1446
                                    tr("Unknown command '\\%1'").arg(cmdStr),
 
1447
                                    detailsUnknownCommand(metaCommandSet,cmdStr));
 
1448
                        enterPara();
 
1449
                        append(Atom::UnknownCommand, cmdStr);
 
1450
                    }
 
1451
                }
 
1452
            }
 
1453
        }
 
1454
            break;
 
1455
        case '{':
 
1456
            enterPara();
 
1457
            appendChar('{');
 
1458
            braceDepth++;
 
1459
            pos++;
 
1460
            break;
 
1461
        case '}':
 
1462
        {
 
1463
            braceDepth--;
 
1464
            pos++;
 
1465
 
 
1466
            QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth);
 
1467
            if (f == pendingFormats.end()) {
 
1468
                enterPara();
 
1469
                appendChar('}');
 
1470
            }
 
1471
            else {
 
1472
                append(Atom::FormattingRight, *f);
 
1473
                if (*f == ATOM_FORMATTING_INDEX) {
 
1474
                    if (indexStartedPara)
 
1475
                        skipAllSpaces();
 
1476
                }
 
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);
 
1485
                    }
 
1486
                    currentLinkAtom = 0;
 
1487
                }
 
1488
                pendingFormats.erase(f);
 
1489
            }
 
1490
        }
 
1491
            break;
 
1492
        default:
 
1493
        {
 
1494
            bool newWord;
 
1495
            switch (priv->text.lastAtom()->type()) {
 
1496
            case Atom::ParaLeft:
 
1497
                newWord = true;
 
1498
                break;
 
1499
            default:
 
1500
                newWord = false;
 
1501
            }
 
1502
 
 
1503
            if (paraState == OutsideParagraph) {
 
1504
                if (ch.isSpace()) {
 
1505
                    ++pos;
 
1506
                    newWord = false;
 
1507
                }
 
1508
                else {
 
1509
                    enterPara();
 
1510
                    newWord = true;
 
1511
                }
 
1512
            }
 
1513
            else {
 
1514
                if (ch.isSpace()) {
 
1515
                    ++pos;
 
1516
                    if ((ch == '\n') &&
 
1517
                            (paraState == InSingleLineParagraph ||
 
1518
                             isBlankLine())) {
 
1519
                        leavePara();
 
1520
                        newWord = false;
 
1521
                    }
 
1522
                    else {
 
1523
                        appendChar(' ');
 
1524
                        newWord = true;
 
1525
                    }
 
1526
                }
 
1527
                else {
 
1528
                    newWord = true;
 
1529
                }
 
1530
            }
 
1531
 
 
1532
            if (newWord) {
 
1533
                int startPos = pos;
 
1534
                int numInternalUppercase = 0;
 
1535
                int numLowercase = 0;
 
1536
                int numStrangeSymbols = 0;
 
1537
 
 
1538
                while (pos < len) {
 
1539
                    unsigned char latin1Ch = in.at(pos).toLatin1();
 
1540
                    if (islower(latin1Ch)) {
 
1541
                        ++numLowercase;
 
1542
                        ++pos;
 
1543
                    }
 
1544
                    else if (isupper(latin1Ch)) {
 
1545
                        if (pos > startPos)
 
1546
                            ++numInternalUppercase;
 
1547
                        ++pos;
 
1548
                    }
 
1549
                    else if (isdigit(latin1Ch)) {
 
1550
                        if (pos > startPos) {
 
1551
                            ++pos;
 
1552
                        }
 
1553
                        else {
 
1554
                            break;
 
1555
                        }
 
1556
                    }
 
1557
                    else if (latin1Ch == '_' || latin1Ch == '@') {
 
1558
                        ++numStrangeSymbols;
 
1559
                        ++pos;
 
1560
                    }
 
1561
                    else if (latin1Ch == ':' && pos < len - 1
 
1562
                             && in.at(pos + 1) == QLatin1Char(':')) {
 
1563
                        ++numStrangeSymbols;
 
1564
                        pos += 2;
 
1565
                    }
 
1566
                    else if (latin1Ch == '(') {
 
1567
                        if (pos > startPos) {
 
1568
                            if (pos < len - 1 &&
 
1569
                                    in.at(pos + 1) == QLatin1Char(')')) {
 
1570
                                ++numStrangeSymbols;
 
1571
                                pos += 2;
 
1572
                                break;
 
1573
                            }
 
1574
                            else {
 
1575
                                // ### handle functions with signatures
 
1576
                                // and function calls
 
1577
                                break;
 
1578
                            }
 
1579
                        }
 
1580
                        else {
 
1581
                            break;
 
1582
                        }
 
1583
                    }
 
1584
                    else {
 
1585
                        break;
 
1586
                    }
 
1587
                }
 
1588
 
 
1589
                if (pos == startPos) {
 
1590
                    if (!ch.isSpace()) {
 
1591
                        appendChar(ch);
 
1592
                        ++pos;
 
1593
                    }
 
1594
                }
 
1595
                else {
 
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);
 
1601
                    }
 
1602
                    else {
 
1603
                        appendWord(word);
 
1604
                    }
 
1605
                }
 
1606
            }
 
1607
        }
 
1608
        }
 
1609
    }
 
1610
    leaveValueList();
 
1611
 
 
1612
    // for compatibility
 
1613
    if (openedCommands.top() == CMD_LEGALESE) {
 
1614
        append(Atom::LegaleseRight);
 
1615
        openedCommands.pop();
 
1616
    }
 
1617
 
 
1618
    if (openedCommands.top() != CMD_OMIT) {
 
1619
        location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
 
1620
    }
 
1621
    else if (preprocessorSkipping.count() > 0) {
 
1622
        location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
 
1623
    }
 
1624
 
 
1625
    if (currentSection > Doc::NoSection) {
 
1626
        append(Atom::SectionRight, QString::number(currentSection));
 
1627
        currentSection = Doc::NoSection;
 
1628
    }
 
1629
 
 
1630
    if (priv->extra && priv->extra->granularity < priv->extra->section)
 
1631
        priv->extra->granularity = priv->extra->section;
 
1632
    priv->text.stripFirstAtom();
 
1633
}
 
1634
 
 
1635
/*!
 
1636
  Returns the current location.
 
1637
 */
 
1638
Location &DocParser::location()
 
1639
{
 
1640
    while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
 
1641
        cachedLoc.pop();
 
1642
        cachedPos = openedInputs.pop();
 
1643
    }
 
1644
    while (cachedPos < pos)
 
1645
        cachedLoc.advance(in.at(cachedPos++));
 
1646
    return cachedLoc;
 
1647
}
 
1648
 
 
1649
QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
 
1650
                                         const QString &str)
 
1651
{
 
1652
    QSet<QString> commandSet = metaCommandSet;
 
1653
    int i = 0;
 
1654
    while (cmds[i].english != 0) {
 
1655
        commandSet.insert(*cmds[i].alias);
 
1656
        i++;
 
1657
    }
 
1658
 
 
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]);
 
1663
 
 
1664
    QString best = nearestName(str, commandSet);
 
1665
    if (best.isEmpty())
 
1666
        return QString();
 
1667
    return tr("Maybe you meant '\\%1'?").arg(best);
 
1668
}
 
1669
 
 
1670
void DocParser::insertBaseName(const QString &baseName)
 
1671
{
 
1672
    priv->constructExtra();
 
1673
    if (currentSection == priv->extra->section) {
 
1674
        priv->extra->baseName = baseName;
 
1675
    }
 
1676
    else {
 
1677
        Atom *atom = priv->text.firstAtom();
 
1678
        Atom *sectionLeft = 0;
 
1679
 
 
1680
        int delta = currentSection - priv->extra->section;
 
1681
 
 
1682
        while (atom != 0) {
 
1683
            if (atom->type() == Atom::SectionLeft &&
 
1684
                    atom->string().toInt() == delta)
 
1685
                sectionLeft = atom;
 
1686
            atom = atom->next();
 
1687
        }
 
1688
        if (sectionLeft != 0)
 
1689
            (void) new Atom(sectionLeft, Atom::BaseName, baseName);
 
1690
    }
 
1691
}
 
1692
 
 
1693
void DocParser::insertTarget(const QString &target, bool keyword)
 
1694
{
 
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)"));
 
1698
    }
 
1699
    else {
 
1700
        targetMap.insert(target, location());
 
1701
        append(Atom::Target, target);
 
1702
        priv->constructExtra();
 
1703
        if (keyword)
 
1704
            priv->extra->keywords.append(priv->text.lastAtom());
 
1705
        else
 
1706
            priv->extra->targets.append(priv->text.lastAtom());
 
1707
    }
 
1708
}
 
1709
 
 
1710
void DocParser::include(const QString& fileName, const QString& identifier)
 
1711
{
 
1712
    if (location().depth() > 16)
 
1713
        location().fatal(tr("Too many nested '\\%1's")
 
1714
                         .arg(cmdName(CMD_INCLUDE)));
 
1715
 
 
1716
    QString userFriendlyFilePath;
 
1717
    // ### use current directory?
 
1718
    QString filePath = Config::findFile(location(),
 
1719
                                        sourceFiles,
 
1720
                                        sourceDirs,
 
1721
                                        fileName,
 
1722
                                        userFriendlyFilePath);
 
1723
    if (filePath.isEmpty()) {
 
1724
        location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName));
 
1725
    }
 
1726
    else {
 
1727
        QFile inFile(filePath);
 
1728
        if (!inFile.open(QFile::ReadOnly)) {
 
1729
            location().warning(tr("Cannot open qdoc include file '%1'")
 
1730
                               .arg(userFriendlyFilePath));
 
1731
        }
 
1732
        else {
 
1733
            location().push(userFriendlyFilePath);
 
1734
 
 
1735
            QTextStream inStream(&inFile);
 
1736
            QString includedStuff = inStream.readAll();
 
1737
            inFile.close();
 
1738
 
 
1739
            if (identifier.isEmpty()) {
 
1740
                in.insert(pos, includedStuff);
 
1741
                len = in.length();
 
1742
                openedInputs.push(pos + includedStuff.length());
 
1743
            }
 
1744
            else {
 
1745
                QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
 
1746
                int i = 0;
 
1747
                int startLine = -1;
 
1748
                while (i < lineBuffer.size()) {
 
1749
                    if (lineBuffer[i].startsWith("//!")) {
 
1750
                        if (lineBuffer[i].contains(identifier)) {
 
1751
                            startLine = i+1;
 
1752
                            break;
 
1753
                        }
 
1754
                    }
 
1755
                    ++i;
 
1756
                }
 
1757
                if (startLine < 0) {
 
1758
                    location().warning(tr("Cannot find '%1' in '%2'")
 
1759
                                       .arg(identifier)
 
1760
                                       .arg(userFriendlyFilePath));
 
1761
                    return;
 
1762
 
 
1763
                }
 
1764
                QString result;
 
1765
                i = startLine;
 
1766
                do {
 
1767
                    if (lineBuffer[i].startsWith("//!")) {
 
1768
                        if (i<lineBuffer.size()) {
 
1769
                            if (lineBuffer[i].contains(identifier)) {
 
1770
                                break;
 
1771
                            }
 
1772
                        }
 
1773
                    }
 
1774
                    else
 
1775
                        result += lineBuffer[i] + QLatin1Char('\n');
 
1776
                    ++i;
 
1777
                } while (i < lineBuffer.size());
 
1778
                if (result.isEmpty()) {
 
1779
                    location().warning(tr("Empty qdoc snippet '%1' in '%2'")
 
1780
                                       .arg(identifier)
 
1781
                                       .arg(userFriendlyFilePath));
 
1782
                }
 
1783
                else {
 
1784
                    in.insert(pos, result);
 
1785
                    len = in.length();
 
1786
                    openedInputs.push(pos + result.length());
 
1787
                }
 
1788
            }
 
1789
        }
 
1790
    }
 
1791
}
 
1792
 
 
1793
void DocParser::startFormat(const QString& format, int cmd)
 
1794
{
 
1795
    enterPara();
 
1796
 
 
1797
    QMap<int, QString>::ConstIterator f = pendingFormats.constBegin();
 
1798
    while (f != pendingFormats.constEnd()) {
 
1799
        if (*f == format) {
 
1800
            location().warning(tr("Cannot nest '\\%1' commands")
 
1801
                               .arg(cmdName(cmd)));
 
1802
            return;
 
1803
        }
 
1804
        ++f;
 
1805
    }
 
1806
 
 
1807
    append(Atom::FormattingLeft, format);
 
1808
 
 
1809
    if (isLeftBraceAhead()) {
 
1810
        skipSpacesOrOneEndl();
 
1811
        pendingFormats.insert(braceDepth, format);
 
1812
        ++braceDepth;
 
1813
        ++pos;
 
1814
    }
 
1815
    else {
 
1816
        append(Atom::String, getArgument());
 
1817
        append(Atom::FormattingRight, format);
 
1818
        if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
 
1819
            skipAllSpaces();
 
1820
            indexStartedPara = false;
 
1821
        }
 
1822
    }
 
1823
}
 
1824
 
 
1825
bool DocParser::openCommand(int cmd)
 
1826
{
 
1827
    int outer = openedCommands.top();
 
1828
    bool ok = true;
 
1829
 
 
1830
    if (cmd != CMD_LINK) {
 
1831
        if (outer == CMD_LIST) {
 
1832
            ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
 
1833
        }
 
1834
        else if (outer == CMD_ABSTRACT) {
 
1835
            ok = (cmd == CMD_LIST ||
 
1836
                  cmd == CMD_QUOTATION ||
 
1837
                  cmd == CMD_TABLE);
 
1838
        }
 
1839
        else if (outer == CMD_SIDEBAR) {
 
1840
            ok = (cmd == CMD_LIST ||
 
1841
                  cmd == CMD_QUOTATION ||
 
1842
                  cmd == CMD_SIDEBAR);
 
1843
        }
 
1844
        else if (outer == CMD_QUOTATION) {
 
1845
            ok = (cmd == CMD_LIST);
 
1846
        }
 
1847
        else if (outer == CMD_TABLE) {
 
1848
            ok = (cmd == CMD_LIST ||
 
1849
                  cmd == CMD_FOOTNOTE ||
 
1850
                  cmd == CMD_QUOTATION);
 
1851
        }
 
1852
        else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
 
1853
            ok = false;
 
1854
        }
 
1855
        else if (outer == CMD_TOPICREF)
 
1856
            ok = (cmd == CMD_TOPICREF || cmd == CMD_MAPREF);
 
1857
        else if (outer == CMD_MAPREF)
 
1858
            ok = false;
 
1859
    }
 
1860
 
 
1861
    if (ok) {
 
1862
        openedCommands.push(cmd);
 
1863
    }
 
1864
    else {
 
1865
        location().warning(tr("Can't use '\\%1' in '\\%2'").arg(cmdName(cmd)).arg(cmdName(outer)));
 
1866
    }
 
1867
    return ok;
 
1868
}
 
1869
 
 
1870
bool DocParser::closeCommand(int endCmd)
 
1871
{
 
1872
    if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
 
1873
        openedCommands.pop();
 
1874
        return true;
 
1875
    }
 
1876
    else {
 
1877
        bool contains = false;
 
1878
        QStack<int> opened2 = openedCommands;
 
1879
        while (opened2.size() > 1) {
 
1880
            if (endCmdFor(opened2.top()) == endCmd) {
 
1881
                contains = true;
 
1882
                break;
 
1883
            }
 
1884
            opened2.pop();
 
1885
        }
 
1886
 
 
1887
        if (contains) {
 
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();
 
1893
            }
 
1894
        }
 
1895
        else {
 
1896
            location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd)));
 
1897
        }
 
1898
        return false;
 
1899
    }
 
1900
}
 
1901
 
 
1902
void DocParser::startSection(Doc::Sections unit, int cmd)
 
1903
{
 
1904
    leaveValueList();
 
1905
 
 
1906
    if (currentSection == Doc::NoSection) {
 
1907
        currentSection = (Doc::Sections) (unit);
 
1908
        priv->constructExtra();
 
1909
        priv->extra->section = currentSection;
 
1910
    }
 
1911
    else
 
1912
        endSection(unit,cmd);
 
1913
 
 
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;
 
1922
 
 
1923
}
 
1924
 
 
1925
void DocParser::endSection(int , int) // (int unit, int endCmd)
 
1926
{
 
1927
    leavePara();
 
1928
    append(Atom::SectionRight, QString::number(currentSection));
 
1929
    currentSection = (Doc::NoSection);
 
1930
}
 
1931
 
 
1932
void DocParser::parseAlso()
 
1933
{
 
1934
    leavePara();
 
1935
    skipSpacesOnLine();
 
1936
    while (pos < len && in[pos] != '\n') {
 
1937
        QString target;
 
1938
        QString str;
 
1939
 
 
1940
        if (in[pos] == '{') {
 
1941
            target = getArgument();
 
1942
            skipSpacesOnLine();
 
1943
            if (in[pos] == '{') {
 
1944
                str = getArgument();
 
1945
 
 
1946
                // hack for C++ to support links like \l{QString::}{count()}
 
1947
                if (target.endsWith("::"))
 
1948
                    target += str;
 
1949
            }
 
1950
            else {
 
1951
                str = target;
 
1952
            }
 
1953
#ifdef QDOC2_COMPAT
 
1954
        }
 
1955
        else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
 
1956
            pos += 6;
 
1957
            target = getArgument();
 
1958
            int endPos = in.indexOf("\\endlink", pos);
 
1959
            if (endPos != -1) {
 
1960
                str = in.mid(pos, endPos - pos).trimmed();
 
1961
                pos = endPos + 8;
 
1962
            }
 
1963
#endif
 
1964
        }
 
1965
        else {
 
1966
            target = getArgument();
 
1967
            str = cleanLink(target);
 
1968
        }
 
1969
 
 
1970
        Text also;
 
1971
        also << Atom(Atom::Link, target)
 
1972
             << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
 
1973
             << str
 
1974
             << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
 
1975
        priv->addAlso(also);
 
1976
 
 
1977
        skipSpacesOnLine();
 
1978
        if (pos < len && in[pos] == ',') {
 
1979
            pos++;
 
1980
            skipSpacesOrOneEndl();
 
1981
        }
 
1982
        else if (in[pos] != '\n') {
 
1983
            location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
 
1984
        }
 
1985
    }
 
1986
}
 
1987
 
 
1988
void DocParser::append(Atom::Type type, const QString &string)
 
1989
{
 
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);
 
1994
}
 
1995
 
 
1996
void DocParser::append(Atom::Type type, const QString& p1, const QString& p2)
 
1997
{
 
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);
 
2002
}
 
2003
 
 
2004
void DocParser::appendChar(QChar ch)
 
2005
{
 
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(' '));
 
2012
    }
 
2013
    else
 
2014
        atom->appendChar(ch);
 
2015
}
 
2016
 
 
2017
void DocParser::appendWord(const QString &word)
 
2018
{
 
2019
    if (priv->text.lastAtom()->type() != Atom::String) {
 
2020
        append(Atom::String, word);
 
2021
    }
 
2022
    else
 
2023
        priv->text.lastAtom()->appendString(word);
 
2024
}
 
2025
 
 
2026
void DocParser::appendToCode(const QString& markedCode)
 
2027
{
 
2028
    Atom::Type lastType = priv->text.lastAtom()->type();
 
2029
    if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
 
2030
        append(Atom::Qml);
 
2031
    priv->text.lastAtom()->appendString(markedCode);
 
2032
}
 
2033
 
 
2034
void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType)
 
2035
{
 
2036
    Atom::Type lastType = priv->text.lastAtom()->type();
 
2037
    if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
 
2038
        append(defaultType, markedCode);
 
2039
    else
 
2040
        priv->text.lastAtom()->appendString(markedCode);
 
2041
}
 
2042
 
 
2043
void DocParser::startNewPara()
 
2044
{
 
2045
    leavePara();
 
2046
    enterPara();
 
2047
}
 
2048
 
 
2049
void DocParser::enterPara(Atom::Type leftType,
 
2050
                          Atom::Type rightType,
 
2051
                          const QString& string)
 
2052
{
 
2053
    if (paraState == OutsideParagraph) {
 
2054
 
 
2055
        if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
 
2056
                (priv->text.lastAtom()->type() != Atom::DivLeft)) {
 
2057
            leaveValueList();
 
2058
        }
 
2059
 
 
2060
        append(leftType, string);
 
2061
        indexStartedPara = false;
 
2062
        pendingParaLeftType = leftType;
 
2063
        pendingParaRightType = rightType;
 
2064
        pendingParaString = string;
 
2065
        if (leftType == Atom::SectionHeadingLeft) {
 
2066
            paraState = InSingleLineParagraph;
 
2067
        }
 
2068
        else {
 
2069
            paraState = InMultiLineParagraph;
 
2070
        }
 
2071
        skipSpacesOrOneEndl();
 
2072
    }
 
2073
}
 
2074
 
 
2075
void DocParser::leavePara()
 
2076
{
 
2077
    if (paraState != OutsideParagraph) {
 
2078
        if (!pendingFormats.isEmpty()) {
 
2079
            location().warning(tr("Missing '}'"));
 
2080
            pendingFormats.clear();
 
2081
        }
 
2082
 
 
2083
        if (priv->text.lastAtom()->type() == pendingParaLeftType) {
 
2084
            priv->text.stripLastAtom();
 
2085
        }
 
2086
        else {
 
2087
            if (priv->text.lastAtom()->type() == Atom::String &&
 
2088
                    priv->text.lastAtom()->string().endsWith(QLatin1Char(' '))) {
 
2089
                priv->text.lastAtom()->chopString();
 
2090
            }
 
2091
            append(pendingParaRightType, pendingParaString);
 
2092
        }
 
2093
        paraState = OutsideParagraph;
 
2094
        indexStartedPara = false;
 
2095
        pendingParaRightType = Atom::Nop;
 
2096
        pendingParaString.clear();
 
2097
    }
 
2098
}
 
2099
 
 
2100
void DocParser::leaveValue()
 
2101
{
 
2102
    leavePara();
 
2103
    if (openedLists.isEmpty()) {
 
2104
        openedLists.push(OpenedList(OpenedList::Value));
 
2105
        append(Atom::ListLeft, ATOM_LIST_VALUE);
 
2106
    }
 
2107
    else {
 
2108
        if (priv->text.lastAtom()->type() == Atom::Nop)
 
2109
            priv->text.stripLastAtom();
 
2110
        append(Atom::ListItemRight, ATOM_LIST_VALUE);
 
2111
    }
 
2112
}
 
2113
 
 
2114
void DocParser::leaveValueList()
 
2115
{
 
2116
    leavePara();
 
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);
 
2123
        openedLists.pop();
 
2124
    }
 
2125
}
 
2126
 
 
2127
void DocParser::leaveTableRow()
 
2128
{
 
2129
    if (inTableItem) {
 
2130
        leavePara();
 
2131
        append(Atom::TableItemRight);
 
2132
        inTableItem = false;
 
2133
    }
 
2134
    if (inTableHeader) {
 
2135
        append(Atom::TableHeaderRight);
 
2136
        inTableHeader = false;
 
2137
    }
 
2138
    if (inTableRow) {
 
2139
        append(Atom::TableRowRight);
 
2140
        inTableRow = false;
 
2141
    }
 
2142
}
 
2143
 
 
2144
CodeMarker *DocParser::quoteFromFile()
 
2145
{
 
2146
    return Doc::quoteFromFile(location(), quoter, getArgument());
 
2147
}
 
2148
 
 
2149
void DocParser::expandMacro(const QString &name,
 
2150
                            const QString &def,
 
2151
                            int numParams)
 
2152
{
 
2153
    if (numParams == 0) {
 
2154
        append(Atom::RawString, def);
 
2155
    }
 
2156
    else {
 
2157
        QStringList args;
 
2158
        QString rawString;
 
2159
 
 
2160
        for (int i = 0; i < numParams; i++) {
 
2161
            if (numParams == 1 || isLeftBraceAhead()) {
 
2162
                args << getArgument(true);
 
2163
            }
 
2164
            else {
 
2165
                location().warning(tr("Macro '\\%1' invoked with too few"
 
2166
                                      " arguments (expected %2, got %3)")
 
2167
                                   .arg(name).arg(numParams).arg(i));
 
2168
                break;
 
2169
            }
 
2170
        }
 
2171
 
 
2172
        int j = 0;
 
2173
        while (j < def.size()) {
 
2174
            int paramNo;
 
2175
            if (((paramNo = def[j].unicode()) >= 1) &&
 
2176
                    (paramNo <= numParams)) {
 
2177
                if (!rawString.isEmpty()) {
 
2178
                    append(Atom::RawString, rawString);
 
2179
                    rawString.clear();
 
2180
                }
 
2181
                append(Atom::String, args[paramNo - 1]);
 
2182
                j += 1;
 
2183
            }
 
2184
            else {
 
2185
                rawString += def[j++];
 
2186
            }
 
2187
        }
 
2188
        if (!rawString.isEmpty())
 
2189
            append(Atom::RawString, rawString);
 
2190
    }
 
2191
}
 
2192
 
 
2193
QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
 
2194
{
 
2195
    if (numParams == 0) {
 
2196
        return def;
 
2197
    }
 
2198
    else {
 
2199
        QStringList args;
 
2200
        QString rawString;
 
2201
 
 
2202
        for (int i = 0; i < numParams; i++) {
 
2203
            if (numParams == 1 || isLeftBraceAhead()) {
 
2204
                args << getArgument(true);
 
2205
            }
 
2206
            else {
 
2207
                location().warning(tr("Macro '\\%1' invoked with too few"
 
2208
                                      " arguments (expected %2, got %3)")
 
2209
                                   .arg(name).arg(numParams).arg(i));
 
2210
                break;
 
2211
            }
 
2212
        }
 
2213
 
 
2214
        int j = 0;
 
2215
        while (j < def.size()) {
 
2216
            int paramNo;
 
2217
            if (((paramNo = def[j].unicode()) >= 1) &&
 
2218
                    (paramNo <= numParams)) {
 
2219
                rawString += args[paramNo - 1];
 
2220
                j += 1;
 
2221
            }
 
2222
            else {
 
2223
                rawString += def[j++];
 
2224
            }
 
2225
        }
 
2226
        return rawString;
 
2227
    }
 
2228
}
 
2229
 
 
2230
Doc::Sections DocParser::getSectioningUnit()
 
2231
{
 
2232
    QString name = getOptionalArgument();
 
2233
 
 
2234
    if (name == "part") {
 
2235
        return Doc::Part;
 
2236
    }
 
2237
    else if (name == "chapter") {
 
2238
        return Doc::Chapter;
 
2239
    }
 
2240
    else if (name == "section1") {
 
2241
        return Doc::Section1;
 
2242
    }
 
2243
    else if (name == "section2") {
 
2244
        return Doc::Section2;
 
2245
    }
 
2246
    else if (name == "section3") {
 
2247
        return Doc::Section3;
 
2248
    }
 
2249
    else if (name == "section4") {
 
2250
        return Doc::Section4;
 
2251
    }
 
2252
    else if (name.isEmpty()) {
 
2253
        return Doc::NoSection;
 
2254
    }
 
2255
    else {
 
2256
        location().warning(tr("Invalid section '%1'").arg(name));
 
2257
        return Doc::NoSection;
 
2258
    }
 
2259
}
 
2260
 
 
2261
/*!
 
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.
 
2266
 
 
2267
  If \a verbatim is true, extra whitespace is retained in the
 
2268
  returned string. Otherwise, extr whitespace is removed.
 
2269
 */
 
2270
QString DocParser::getBracedArgument(bool verbatim)
 
2271
{
 
2272
    QString arg;
 
2273
    int delimDepth = 0;
 
2274
    if (pos < (int) in.length() && in[pos] == '{') {
 
2275
        pos++;
 
2276
        while (pos < (int) in.length() && delimDepth >= 0) {
 
2277
            switch (in[pos].unicode()) {
 
2278
            case '{':
 
2279
                delimDepth++;
 
2280
                arg += QLatin1Char('{');
 
2281
                pos++;
 
2282
                break;
 
2283
            case '}':
 
2284
                delimDepth--;
 
2285
                if (delimDepth >= 0)
 
2286
                    arg += QLatin1Char('}');
 
2287
                pos++;
 
2288
                break;
 
2289
            case '\\':
 
2290
                if (verbatim) {
 
2291
                    arg += in[pos];
 
2292
                    pos++;
 
2293
                }
 
2294
                else {
 
2295
                    pos++;
 
2296
                    if (pos < (int) in.length()) {
 
2297
                        if (in[pos].isLetterOrNumber())
 
2298
                            break;
 
2299
                        arg += in[pos];
 
2300
                        if (in[pos].isSpace()) {
 
2301
                            skipAllSpaces();
 
2302
                        }
 
2303
                        else {
 
2304
                            pos++;
 
2305
                        }
 
2306
                    }
 
2307
                }
 
2308
                break;
 
2309
            default:
 
2310
                arg += in[pos];
 
2311
                pos++;
 
2312
            }
 
2313
        }
 
2314
        if (delimDepth > 0)
 
2315
            location().warning(tr("Missing '}'"));
 
2316
    }
 
2317
    return arg;
 
2318
}
 
2319
 
 
2320
/*!
 
2321
  Typically, an argument ends at the next white-space. However,
 
2322
  braces can be used to group words:
 
2323
 
 
2324
  {a few words}
 
2325
 
 
2326
  Also, opening and closing parentheses have to match. Thus,
 
2327
 
 
2328
  printf("%d\n", x)
 
2329
 
 
2330
  is an argument too, although it contains spaces. Finally,
 
2331
  trailing punctuation is not included in an argument, nor is 's.
 
2332
*/
 
2333
QString DocParser::getArgument(bool verbatim)
 
2334
{
 
2335
    skipSpacesOrOneEndl();
 
2336
 
 
2337
    int delimDepth = 0;
 
2338
    int startPos = pos;
 
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()) {
 
2344
            case '(':
 
2345
            case '[':
 
2346
            case '{':
 
2347
                delimDepth++;
 
2348
                arg += in[pos];
 
2349
                pos++;
 
2350
                break;
 
2351
            case ')':
 
2352
            case ']':
 
2353
            case '}':
 
2354
                delimDepth--;
 
2355
                if (pos == startPos || delimDepth >= 0) {
 
2356
                    arg += in[pos];
 
2357
                    pos++;
 
2358
                }
 
2359
                break;
 
2360
            case '\\':
 
2361
                if (verbatim) {
 
2362
                    arg += in[pos];
 
2363
                    pos++;
 
2364
                }
 
2365
                else {
 
2366
                    pos++;
 
2367
                    if (pos < (int) in.length()) {
 
2368
                        if (in[pos].isLetterOrNumber())
 
2369
                            break;
 
2370
                        arg += in[pos];
 
2371
                        if (in[pos].isSpace()) {
 
2372
                            skipAllSpaces();
 
2373
                        }
 
2374
                        else {
 
2375
                            pos++;
 
2376
                        }
 
2377
                    }
 
2378
                }
 
2379
                break;
 
2380
            default:
 
2381
                arg += in[pos];
 
2382
                pos++;
 
2383
            }
 
2384
        }
 
2385
        if ((arg.length() > 1) &&
 
2386
                (QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
 
2387
                !arg.endsWith("...")) {
 
2388
            arg.truncate(arg.length() - 1);
 
2389
            pos--;
 
2390
        }
 
2391
        if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
 
2392
            arg.truncate(arg.length() - 2);
 
2393
            pos -= 2;
 
2394
        }
 
2395
    }
 
2396
    return arg.simplified();
 
2397
}
 
2398
 
 
2399
QString DocParser::getOptionalArgument()
 
2400
{
 
2401
    skipSpacesOrOneEndl();
 
2402
    if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
 
2403
            in[pos + 1].isLetterOrNumber()) {
 
2404
        return QString();
 
2405
    }
 
2406
    else {
 
2407
        return getArgument();
 
2408
    }
 
2409
}
 
2410
 
 
2411
QString DocParser::getRestOfLine()
 
2412
{
 
2413
    QString t;
 
2414
 
 
2415
    skipSpacesOnLine();
 
2416
 
 
2417
    bool trailingSlash = false;
 
2418
 
 
2419
    do {
 
2420
        int begin = pos;
 
2421
 
 
2422
        while (pos < in.size() && in[pos] != '\n') {
 
2423
            if (in[pos] == '\\' && !trailingSlash) {
 
2424
                trailingSlash = true;
 
2425
                ++pos;
 
2426
                while ((pos < in.size()) &&
 
2427
                       in[pos].isSpace() &&
 
2428
                       (in[pos] != '\n'))
 
2429
                    ++pos;
 
2430
            }
 
2431
            else {
 
2432
                trailingSlash = false;
 
2433
                ++pos;
 
2434
            }
 
2435
        }
 
2436
 
 
2437
        if (!t.isEmpty())
 
2438
            t += QLatin1Char(' ');
 
2439
        t += in.mid(begin, pos - begin).simplified();
 
2440
 
 
2441
        if (trailingSlash) {
 
2442
            t.chop(1);
 
2443
            t = t.simplified();
 
2444
        }
 
2445
        if (pos < in.size())
 
2446
            ++pos;
 
2447
    } while (pos < in.size() && trailingSlash);
 
2448
 
 
2449
    return t;
 
2450
}
 
2451
 
 
2452
/*!
 
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.
 
2456
 */
 
2457
QString DocParser::getMetaCommandArgument(const QString &cmdStr)
 
2458
{
 
2459
    skipSpacesOnLine();
 
2460
 
 
2461
    int begin = pos;
 
2462
    int parenDepth = 0;
 
2463
 
 
2464
    while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
 
2465
        if (in.at(pos) == '(')
 
2466
            ++parenDepth;
 
2467
        else if (in.at(pos) == ')')
 
2468
            --parenDepth;
 
2469
 
 
2470
        ++pos;
 
2471
    }
 
2472
    if (pos == in.size() && parenDepth > 0) {
 
2473
        pos = begin;
 
2474
        location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
 
2475
    }
 
2476
 
 
2477
    QString t = in.mid(begin, pos - begin).simplified();
 
2478
    skipSpacesOnLine();
 
2479
    return t;
 
2480
}
 
2481
 
 
2482
QString DocParser::getUntilEnd(int cmd)
 
2483
{
 
2484
    int endCmd = endCmdFor(cmd);
 
2485
    QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
 
2486
    QString t;
 
2487
    int end = rx.indexIn(in, pos);
 
2488
 
 
2489
    if (end == -1) {
 
2490
        location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
 
2491
        pos = in.length();
 
2492
    }
 
2493
    else {
 
2494
        t = in.mid(pos, end - pos);
 
2495
        pos = end + rx.matchedLength();
 
2496
    }
 
2497
    return t;
 
2498
}
 
2499
 
 
2500
QString DocParser::getCode(int cmd, CodeMarker *marker)
 
2501
{
 
2502
    QString code = untabifyEtc(getUntilEnd(cmd));
 
2503
    int indent = indentLevel(code);
 
2504
    if (indent < minIndent)
 
2505
        minIndent = indent;
 
2506
    code = unindent(minIndent, code);
 
2507
    if (!marker)
 
2508
        marker = CodeMarker::markerForCode(code);
 
2509
    return marker->markedUpCode(code, 0, location());
 
2510
}
 
2511
 
 
2512
/*!
 
2513
  Was used only for generating doxygen output.
 
2514
 */
 
2515
QString DocParser::getUnmarkedCode(int cmd)
 
2516
{
 
2517
    QString code = getUntilEnd(cmd);
 
2518
    return code;
 
2519
}
 
2520
 
 
2521
bool DocParser::isBlankLine()
 
2522
{
 
2523
    int i = pos;
 
2524
 
 
2525
    while (i < len && in[i].isSpace()) {
 
2526
        if (in[i] == '\n')
 
2527
            return true;
 
2528
        i++;
 
2529
    }
 
2530
    return false;
 
2531
}
 
2532
 
 
2533
bool DocParser::isLeftBraceAhead()
 
2534
{
 
2535
    int numEndl = 0;
 
2536
    int i = pos;
 
2537
 
 
2538
    while (i < len && in[i].isSpace() && numEndl < 2) {
 
2539
        // ### bug with '\\'
 
2540
        if (in[i] == '\n')
 
2541
            numEndl++;
 
2542
        i++;
 
2543
    }
 
2544
    return numEndl < 2 && i < len && in[i] == '{';
 
2545
}
 
2546
 
 
2547
/*!
 
2548
  Skips to the next non-space character or EOL.
 
2549
 */
 
2550
void DocParser::skipSpacesOnLine()
 
2551
{
 
2552
    while ((pos < in.length()) &&
 
2553
           in[pos].isSpace() &&
 
2554
           (in[pos].unicode() != '\n'))
 
2555
        ++pos;
 
2556
}
 
2557
 
 
2558
/*!
 
2559
  Skips spaces and on EOL.
 
2560
 */
 
2561
void DocParser::skipSpacesOrOneEndl()
 
2562
{
 
2563
    int firstEndl = -1;
 
2564
    while (pos < (int) in.length() && in[pos].isSpace()) {
 
2565
        QChar ch = in[pos];
 
2566
        if (ch == '\n') {
 
2567
            if (firstEndl == -1) {
 
2568
                firstEndl = pos;
 
2569
            }
 
2570
            else {
 
2571
                pos = firstEndl;
 
2572
                break;
 
2573
            }
 
2574
        }
 
2575
        pos++;
 
2576
    }
 
2577
}
 
2578
 
 
2579
void DocParser::skipAllSpaces()
 
2580
{
 
2581
    while (pos < len && in[pos].isSpace())
 
2582
        pos++;
 
2583
}
 
2584
 
 
2585
void DocParser::skipToNextPreprocessorCommand()
 
2586
{
 
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?
 
2591
 
 
2592
    if (end == -1)
 
2593
        pos = in.length();
 
2594
    else
 
2595
        pos = end;
 
2596
}
 
2597
 
 
2598
int DocParser::endCmdFor(int cmd)
 
2599
{
 
2600
    switch (cmd) {
 
2601
    case CMD_ABSTRACT:
 
2602
        return CMD_ENDABSTRACT;
 
2603
    case CMD_BADCODE:
 
2604
        return CMD_ENDCODE;
 
2605
    case CMD_CHAPTER:
 
2606
        return CMD_ENDCHAPTER;
 
2607
    case CMD_CODE:
 
2608
        return CMD_ENDCODE;
 
2609
    case CMD_DIV:
 
2610
        return CMD_ENDDIV;
 
2611
    case CMD_QML:
 
2612
        return CMD_ENDQML;
 
2613
    case CMD_QMLTEXT:
 
2614
        return CMD_ENDQMLTEXT;
 
2615
    case CMD_JS:
 
2616
        return CMD_ENDJS;
 
2617
    case CMD_FOOTNOTE:
 
2618
        return CMD_ENDFOOTNOTE;
 
2619
    case CMD_LEGALESE:
 
2620
        return CMD_ENDLEGALESE;
 
2621
    case CMD_LINK:
 
2622
        return CMD_ENDLINK;
 
2623
    case CMD_LIST:
 
2624
        return CMD_ENDLIST;
 
2625
    case CMD_NEWCODE:
 
2626
        return CMD_ENDCODE;
 
2627
    case CMD_OLDCODE:
 
2628
        return CMD_NEWCODE;
 
2629
    case CMD_OMIT:
 
2630
        return CMD_ENDOMIT;
 
2631
    case CMD_PART:
 
2632
        return CMD_ENDPART;
 
2633
    case CMD_QUOTATION:
 
2634
        return CMD_ENDQUOTATION;
 
2635
    case CMD_RAW:
 
2636
        return CMD_ENDRAW;
 
2637
    case CMD_SECTION1:
 
2638
        return CMD_ENDSECTION1;
 
2639
    case CMD_SECTION2:
 
2640
        return CMD_ENDSECTION2;
 
2641
    case CMD_SECTION3:
 
2642
        return CMD_ENDSECTION3;
 
2643
    case CMD_SECTION4:
 
2644
        return CMD_ENDSECTION4;
 
2645
    case CMD_SIDEBAR:
 
2646
        return CMD_ENDSIDEBAR;
 
2647
    case CMD_TABLE:
 
2648
        return CMD_ENDTABLE;
 
2649
    case CMD_TOPICREF:
 
2650
        return CMD_ENDTOPICREF;
 
2651
    case CMD_MAPREF:
 
2652
        return CMD_ENDMAPREF;
 
2653
    default:
 
2654
        return cmd;
 
2655
    }
 
2656
}
 
2657
 
 
2658
QString DocParser::cmdName(int cmd)
 
2659
{
 
2660
    return *cmds[cmd].alias;
 
2661
}
 
2662
 
 
2663
QString DocParser::endCmdName(int cmd)
 
2664
{
 
2665
    return cmdName(endCmdFor(cmd));
 
2666
}
 
2667
 
 
2668
QString DocParser::untabifyEtc(const QString& str)
 
2669
{
 
2670
    QString result;
 
2671
    result.reserve(str.length());
 
2672
    int column = 0;
 
2673
 
 
2674
    for (int i = 0; i < str.length(); i++) {
 
2675
        const QChar c = str.at(i);
 
2676
        if (c == QLatin1Char('\r'))
 
2677
            continue;
 
2678
        if (c == QLatin1Char('\t')) {
 
2679
            result += "        " + (column % tabSize);
 
2680
            column = ((column / tabSize) + 1) * tabSize;
 
2681
            continue;
 
2682
        }
 
2683
        if (c == QLatin1Char('\n')) {
 
2684
            while (result.endsWith(QLatin1Char(' ')))
 
2685
                result.chop(1);
 
2686
            result += c;
 
2687
            column = 0;
 
2688
            continue;
 
2689
        }
 
2690
        result += c;
 
2691
        column++;
 
2692
    }
 
2693
 
 
2694
    while (result.endsWith("\n\n"))
 
2695
        result.truncate(result.length() - 1);
 
2696
    while (result.startsWith(QLatin1Char('\n')))
 
2697
        result = result.mid(1);
 
2698
 
 
2699
    return result;
 
2700
}
 
2701
 
 
2702
int DocParser::indentLevel(const QString& str)
 
2703
{
 
2704
    int minIndent = INT_MAX;
 
2705
    int column = 0;
 
2706
 
 
2707
    for (int i = 0; i < (int) str.length(); i++) {
 
2708
        if (str[i] == '\n') {
 
2709
            column = 0;
 
2710
        }
 
2711
        else {
 
2712
            if (str[i] != ' ' && column < minIndent)
 
2713
                minIndent = column;
 
2714
            column++;
 
2715
        }
 
2716
    }
 
2717
    return minIndent;
 
2718
}
 
2719
 
 
2720
QString DocParser::unindent(int level, const QString& str)
 
2721
{
 
2722
    if (level == 0)
 
2723
        return str;
 
2724
 
 
2725
    QString t;
 
2726
    int column = 0;
 
2727
 
 
2728
    for (int i = 0; i < (int) str.length(); i++) {
 
2729
        if (str[i] == QLatin1Char('\n')) {
 
2730
            t += '\n';
 
2731
            column = 0;
 
2732
        }
 
2733
        else {
 
2734
            if (column >= level)
 
2735
                t += str[i];
 
2736
            column++;
 
2737
        }
 
2738
    }
 
2739
    return t;
 
2740
}
 
2741
 
 
2742
QString DocParser::slashed(const QString& str)
 
2743
{
 
2744
    QString result = str;
 
2745
    result.replace(QLatin1Char('/'), "\\/");
 
2746
    return QLatin1Char('/') + result + QLatin1Char('/');
 
2747
}
 
2748
 
 
2749
#define COMMAND_BRIEF                   Doc::alias("brief")
 
2750
#define COMMAND_QMLBRIEF                Doc::alias("qmlbrief")
 
2751
 
 
2752
Doc::Doc(const Location& start_loc,
 
2753
         const Location& end_loc,
 
2754
         const QString& source,
 
2755
         const QSet<QString>& metaCommandSet)
 
2756
{
 
2757
    priv = new DocPrivate(start_loc,end_loc,source);
 
2758
    DocParser parser;
 
2759
    parser.parse(source,priv,metaCommandSet,QSet<QString>());
 
2760
}
 
2761
 
 
2762
/*!
 
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.
 
2769
 */
 
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)
 
2775
{
 
2776
    priv = new DocPrivate(start_loc,end_loc,source);
 
2777
    DocParser parser;
 
2778
    parser.parse(source,priv,metaCommandSet,topics);
 
2779
}
 
2780
 
 
2781
Doc::Doc(const Doc& doc)
 
2782
    : priv(0)
 
2783
{
 
2784
    operator=(doc);
 
2785
}
 
2786
 
 
2787
Doc::~Doc()
 
2788
{
 
2789
    if (priv && priv->deref())
 
2790
        delete priv;
 
2791
}
 
2792
 
 
2793
Doc &Doc::operator=(const Doc& doc)
 
2794
{
 
2795
    if (doc.priv)
 
2796
        doc.priv->ref();
 
2797
    if (priv && priv->deref())
 
2798
        delete priv;
 
2799
    priv = doc.priv;
 
2800
    return *this;
 
2801
}
 
2802
 
 
2803
void Doc::renameParameters(const QStringList &oldNames,
 
2804
                           const QStringList &newNames)
 
2805
{
 
2806
    if (priv && oldNames != newNames) {
 
2807
        detach();
 
2808
 
 
2809
        priv->params = newNames.toSet();
 
2810
 
 
2811
        Atom *atom = priv->text.firstAtom();
 
2812
        while (atom) {
 
2813
            if (atom->type() == Atom::FormattingLeft
 
2814
                    && atom->string() == ATOM_FORMATTING_PARAMETER) {
 
2815
                atom = atom->next();
 
2816
                if (!atom)
 
2817
                    return;
 
2818
                int index = oldNames.indexOf(atom->string());
 
2819
                if (index != -1 && index < newNames.count())
 
2820
                    atom->setString(newNames.at(index));
 
2821
            }
 
2822
            atom = atom->next();
 
2823
        }
 
2824
    }
 
2825
}
 
2826
 
 
2827
void Doc::simplifyEnumDoc()
 
2828
{
 
2829
    if (priv) {
 
2830
        if (priv->isEnumDocSimplifiable()) {
 
2831
            detach();
 
2832
 
 
2833
            Text newText;
 
2834
 
 
2835
            Atom *atom = priv->text.firstAtom();
 
2836
            while (atom) {
 
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();
 
2842
                    if (atom)
 
2843
                        atom = atom->next();
 
2844
                }
 
2845
                else {
 
2846
                    newText << *atom;
 
2847
                    atom = atom->next();
 
2848
                }
 
2849
            }
 
2850
            priv->text = newText;
 
2851
        }
 
2852
    }
 
2853
}
 
2854
 
 
2855
void Doc::setBody(const Text &text)
 
2856
{
 
2857
    detach();
 
2858
    priv->text = text;
 
2859
}
 
2860
 
 
2861
/*!
 
2862
  Returns the starting location of a qdoc comment.
 
2863
 */
 
2864
const Location &Doc::location() const
 
2865
{
 
2866
    static const Location dummy;
 
2867
    return priv == 0 ? dummy : priv->start_loc;
 
2868
}
 
2869
 
 
2870
/*!
 
2871
  Returns the starting location of a qdoc comment.
 
2872
 */
 
2873
const Location& Doc::startLocation() const
 
2874
{
 
2875
    return location();
 
2876
}
 
2877
 
 
2878
/*!
 
2879
  Returns the ending location of a qdoc comment.
 
2880
 */
 
2881
const Location& Doc::endLocation() const
 
2882
{
 
2883
    static const Location dummy;
 
2884
    return priv == 0 ? dummy : priv->end_loc;
 
2885
}
 
2886
 
 
2887
const QString &Doc::source() const
 
2888
{
 
2889
    static QString null;
 
2890
    return priv == 0 ? null : priv->src;
 
2891
}
 
2892
 
 
2893
bool Doc::isEmpty() const
 
2894
{
 
2895
    return priv == 0 || priv->src.isEmpty();
 
2896
}
 
2897
 
 
2898
const Text& Doc::body() const
 
2899
{
 
2900
    static const Text dummy;
 
2901
    return priv == 0 ? dummy : priv->text;
 
2902
}
 
2903
 
 
2904
Text Doc::briefText(bool inclusive) const
 
2905
{
 
2906
    return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
 
2907
}
 
2908
 
 
2909
Text Doc::trimmedBriefText(const QString &className) const
 
2910
{
 
2911
    QString classNameOnly = className;
 
2912
    if (className.contains("::"))
 
2913
        classNameOnly = className.split("::").last();
 
2914
 
 
2915
    Text originalText = briefText();
 
2916
    Text resultText;
 
2917
    const Atom *atom = originalText.firstAtom();
 
2918
    if (atom) {
 
2919
        QString briefStr;
 
2920
        QString whats;
 
2921
        /*
 
2922
          This code is really ugly. The entire \brief business
 
2923
          should be rethought.
 
2924
        */
 
2925
        while (atom) {
 
2926
            if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
 
2927
                briefStr += atom->string();
 
2928
            }
 
2929
            atom = atom->next();
 
2930
        }
 
2931
 
 
2932
        QStringList w = briefStr.split(QLatin1Char(' '));
 
2933
        if (!w.isEmpty() && w.first() == "Returns") {
 
2934
        }
 
2935
        else {
 
2936
            if (!w.isEmpty() && w.first() == "The")
 
2937
                w.removeFirst();
 
2938
 
 
2939
            if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
 
2940
                w.removeFirst();
 
2941
 
 
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")))
 
2948
                w.removeFirst();
 
2949
 
 
2950
            if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
 
2951
                w.removeFirst();
 
2952
 
 
2953
            if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
 
2954
                w.removeFirst();
 
2955
        }
 
2956
 
 
2957
        whats = w.join(' ');
 
2958
 
 
2959
        if (whats.endsWith(QLatin1Char('.')))
 
2960
            whats.truncate(whats.length() - 1);
 
2961
 
 
2962
        if (!whats.isEmpty())
 
2963
            whats[0] = whats[0].toUpper();
 
2964
 
 
2965
        // ### move this once \brief is abolished for properties
 
2966
        resultText << whats;
 
2967
    }
 
2968
    return resultText;
 
2969
}
 
2970
 
 
2971
Text Doc::legaleseText() const
 
2972
{
 
2973
    if (priv == 0 || !priv->hasLegalese)
 
2974
        return Text();
 
2975
    else
 
2976
        return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
 
2977
}
 
2978
 
 
2979
const QString& Doc::baseName() const
 
2980
{
 
2981
    static QString null;
 
2982
    if (priv == 0 || priv->extra == 0) {
 
2983
        return null;
 
2984
    }
 
2985
    else {
 
2986
        return priv->extra->baseName;
 
2987
    }
 
2988
}
 
2989
 
 
2990
Doc::Sections Doc::granularity() const
 
2991
{
 
2992
    if (priv == 0 || priv->extra == 0) {
 
2993
        return DocPrivateExtra().granularity;
 
2994
    }
 
2995
    else {
 
2996
        return priv->extra->granularity;
 
2997
    }
 
2998
}
 
2999
 
 
3000
const QSet<QString> &Doc::parameterNames() const
 
3001
{
 
3002
    return priv == 0 ? *null_Set_QString() : priv->params;
 
3003
}
 
3004
 
 
3005
const QStringList &Doc::enumItemNames() const
 
3006
{
 
3007
    return priv == 0 ? *null_QStringList() : priv->enumItemList;
 
3008
}
 
3009
 
 
3010
const QStringList &Doc::omitEnumItemNames() const
 
3011
{
 
3012
    return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
 
3013
}
 
3014
 
 
3015
const QSet<QString> &Doc::metaCommandsUsed() const
 
3016
{
 
3017
    return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
 
3018
}
 
3019
 
 
3020
/*!
 
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.
 
3024
 */
 
3025
const TopicList& Doc::topicsUsed() const
 
3026
{
 
3027
    return priv == 0 ? *nullTopicList() : priv->topics;
 
3028
}
 
3029
 
 
3030
ArgList Doc::metaCommandArgs(const QString& metacommand) const
 
3031
{
 
3032
    return priv == 0 ? ArgList() : priv->metaCommandMap.value(metacommand);
 
3033
}
 
3034
 
 
3035
const QList<Text> &Doc::alsoList() const
 
3036
{
 
3037
    return priv == 0 ? *null_QList_Text() : priv->alsoList;
 
3038
}
 
3039
 
 
3040
bool Doc::hasTableOfContents() const
 
3041
{
 
3042
    return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
 
3043
}
 
3044
 
 
3045
bool Doc::hasKeywords() const
 
3046
{
 
3047
    return priv && priv->extra && !priv->extra->keywords.isEmpty();
 
3048
}
 
3049
 
 
3050
bool Doc::hasTargets() const
 
3051
{
 
3052
    return priv && priv->extra && !priv->extra->targets.isEmpty();
 
3053
}
 
3054
 
 
3055
const QList<Atom *> &Doc::tableOfContents() const
 
3056
{
 
3057
    priv->constructExtra();
 
3058
    return priv->extra->tableOfContents;
 
3059
}
 
3060
 
 
3061
const QList<int> &Doc::tableOfContentsLevels() const
 
3062
{
 
3063
    priv->constructExtra();
 
3064
    return priv->extra->tableOfContentsLevels;
 
3065
}
 
3066
 
 
3067
const QList<Atom *> &Doc::keywords() const
 
3068
{
 
3069
    priv->constructExtra();
 
3070
    return priv->extra->keywords;
 
3071
}
 
3072
 
 
3073
const QList<Atom *> &Doc::targets() const
 
3074
{
 
3075
    priv->constructExtra();
 
3076
    return priv->extra->targets;
 
3077
}
 
3078
 
 
3079
const QStringMultiMap &Doc::metaTagMap() const
 
3080
{
 
3081
    return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
 
3082
}
 
3083
 
 
3084
void Doc::initialize(const Config& config)
 
3085
{
 
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);
 
3092
 
 
3093
    QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
 
3094
 
 
3095
    QStringMap reverseAliasMap;
 
3096
 
 
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'")
 
3104
                                          .arg(alias)
 
3105
                                          .arg(reverseAliasMap[alias])
 
3106
                                          .arg(*c));
 
3107
        }
 
3108
        else {
 
3109
            reverseAliasMap.insert(alias, *c);
 
3110
        }
 
3111
        aliasMap()->insert(*c, alias);
 
3112
        ++c;
 
3113
    }
 
3114
 
 
3115
    int i = 0;
 
3116
    while (cmds[i].english) {
 
3117
        cmds[i].alias = new QString(alias(cmds[i].english));
 
3118
        cmdHash()->insert(*cmds[i].alias, cmds[i].no);
 
3119
 
 
3120
        if (cmds[i].no != i)
 
3121
            Location::internalError(tr("command %1 missing").arg(i));
 
3122
        i++;
 
3123
    }
 
3124
 
 
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;
 
3129
        Macro macro;
 
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);
 
3135
        }
 
3136
        bool silent = false;
 
3137
 
 
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;
 
3147
                }
 
3148
                else if (macro.numParams != m) {
 
3149
                    if (!silent) {
 
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"
 
3155
                                                         " of arguments (%2"
 
3156
                                                         " %3, %4 %5)")
 
3157
                                                      .arg(*n)
 
3158
                                                      .arg(*f)
 
3159
                                                      .arg(m)
 
3160
                                                      .arg(other)
 
3161
                                                      .arg(macro.numParams));
 
3162
                        silent = true;
 
3163
                    }
 
3164
                    if (macro.numParams < m)
 
3165
                        macro.numParams = m;
 
3166
                }
 
3167
            }
 
3168
            ++f;
 
3169
        }
 
3170
 
 
3171
        if (macro.numParams != -1)
 
3172
            macroHash()->insert(*n, macro);
 
3173
        ++n;
 
3174
    }
 
3175
}
 
3176
 
 
3177
void Doc::terminate()
 
3178
{
 
3179
    DocParser::exampleFiles.clear();
 
3180
    DocParser::exampleDirs.clear();
 
3181
    DocParser::sourceFiles.clear();
 
3182
    DocParser::sourceDirs.clear();
 
3183
    aliasMap()->clear();
 
3184
    cmdHash()->clear();
 
3185
    macroHash()->clear();
 
3186
 
 
3187
    int i = 0;
 
3188
    while (cmds[i].english) {
 
3189
        delete cmds[i].alias;
 
3190
        cmds[i].alias = 0;
 
3191
        ++i;
 
3192
    }
 
3193
}
 
3194
 
 
3195
QString Doc::alias(const QString &english)
 
3196
{
 
3197
    return aliasMap()->value(english, english);
 
3198
}
 
3199
 
 
3200
/*!
 
3201
  Trims the deadwood out of \a str. i.e., this function
 
3202
  cleans up \a str.
 
3203
 */
 
3204
void Doc::trimCStyleComment(Location& location, QString& str)
 
3205
{
 
3206
    QString cleaned;
 
3207
    Location m = location;
 
3208
    bool metAsterColumn = true;
 
3209
    int asterColumn = location.columnNo() + 1;
 
3210
    int i;
 
3211
 
 
3212
    for (i = 0; i < (int) str.length(); i++) {
 
3213
        if (m.columnNo() == asterColumn) {
 
3214
            if (str[i] != '*')
 
3215
                break;
 
3216
            cleaned += ' ';
 
3217
            metAsterColumn = true;
 
3218
        }
 
3219
        else {
 
3220
            if (str[i] == '\n') {
 
3221
                if (!metAsterColumn)
 
3222
                    break;
 
3223
                metAsterColumn = false;
 
3224
            }
 
3225
            cleaned += str[i];
 
3226
        }
 
3227
        m.advance(str[i]);
 
3228
    }
 
3229
    if (cleaned.length() == str.length())
 
3230
        str = cleaned;
 
3231
 
 
3232
    for (int i = 0; i < 3; i++)
 
3233
        location.advance(str[i]);
 
3234
    str = str.mid(3, str.length() - 5);
 
3235
}
 
3236
 
 
3237
CodeMarker *Doc::quoteFromFile(const Location &location,
 
3238
                               Quoter &quoter,
 
3239
                               const QString &fileName)
 
3240
{
 
3241
    quoter.reset();
 
3242
 
 
3243
    QString code;
 
3244
 
 
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));
 
3252
    }
 
3253
    else {
 
3254
        QFile inFile(filePath);
 
3255
        if (!inFile.open(QFile::ReadOnly)) {
 
3256
            location.warning(tr("Cannot open file to quote from: '%1'").arg(userFriendlyFilePath));
 
3257
        }
 
3258
        else {
 
3259
            QTextStream inStream(&inFile);
 
3260
            code = DocParser::untabifyEtc(inStream.readAll());
 
3261
        }
 
3262
    }
 
3263
 
 
3264
    QString dirPath = QFileInfo(filePath).path();
 
3265
    CodeMarker *marker = CodeMarker::markerForFileName(fileName);
 
3266
    quoter.quoteFromFile(userFriendlyFilePath,
 
3267
                         code,
 
3268
                         marker->markedUpCode(code, 0, location));
 
3269
    return marker;
 
3270
}
 
3271
 
 
3272
QString Doc::canonicalTitle(const QString &title)
 
3273
{
 
3274
    // The code below is equivalent to the following chunk, but _much_
 
3275
    // faster (accounts for ~10% of total running time)
 
3276
    //
 
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('-'));
 
3282
 
 
3283
    QString result;
 
3284
    result.reserve(title.size());
 
3285
 
 
3286
    bool dashAppended = false;
 
3287
    bool begun = false;
 
3288
    int lastAlnum = 0;
 
3289
    for (int i = 0; i != title.size(); ++i) {
 
3290
        uint c = title.at(i).unicode();
 
3291
        if (c >= 'A' && c <= 'Z')
 
3292
            c -= 'A' - 'a';
 
3293
        bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
 
3294
        if (alnum) {
 
3295
            result += QLatin1Char(c);
 
3296
            begun = true;
 
3297
            dashAppended = false;
 
3298
            lastAlnum = result.size();
 
3299
        }
 
3300
        else if (!dashAppended) {
 
3301
            if (begun)
 
3302
                result += QLatin1Char('-');
 
3303
            dashAppended = true;
 
3304
        }
 
3305
    }
 
3306
    result.truncate(lastAlnum);
 
3307
    return result;
 
3308
}
 
3309
 
 
3310
void Doc::detach()
 
3311
{
 
3312
    if (!priv) {
 
3313
        priv = new DocPrivate;
 
3314
        return;
 
3315
    }
 
3316
    if (priv->count == 1)
 
3317
        return;
 
3318
 
 
3319
    --priv->count;
 
3320
 
 
3321
    DocPrivate *newPriv = new DocPrivate(*priv);
 
3322
    newPriv->count = 1;
 
3323
    if (priv->extra)
 
3324
        newPriv->extra = new DocPrivateExtra(*priv->extra);
 
3325
 
 
3326
    priv = newPriv;
 
3327
}
 
3328
 
 
3329
/*!
 
3330
  The destructor deletes all the sub-TopicRefs.
 
3331
 */
 
3332
TopicRef::~TopicRef()
 
3333
{
 
3334
    foreach (DitaRef* t, subrefs_) {
 
3335
        delete t;
 
3336
    }
 
3337
}
 
3338
 
 
3339
/*!
 
3340
  Returns a reference to the structure that will be used
 
3341
  for generating a DITA mao.
 
3342
 */
 
3343
const DitaRefList& Doc::ditamap() const { return priv->ditamap_; }
 
3344
 
 
3345
QT_END_NAMESPACE