Files correlati : cg0.exe cg0700a.msk cg0700b.msk cg3.exe cg4.exe Bug : Commento: Merge 1.0 libraries
3249 lines
105 KiB
C++
3249 lines
105 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the tools applications of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
** following information to ensure the GNU Lesser General Public License
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "config.h"
|
|
#include "doc.h"
|
|
#include "codemarker.h"
|
|
#include "editdistance.h"
|
|
#include "openedlist.h"
|
|
#include "quoter.h"
|
|
#include "text.h"
|
|
#include "tokenizer.h"
|
|
#include <qdatetime.h>
|
|
#include <qfile.h>
|
|
#include <qfileinfo.h>
|
|
#include <qhash.h>
|
|
#include <qtextstream.h>
|
|
#include <qregexp.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <qdebug.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
Q_GLOBAL_STATIC(QSet<QString>, null_Set_QString)
|
|
Q_GLOBAL_STATIC(QStringList, null_QStringList)
|
|
Q_GLOBAL_STATIC(QList<Text>, null_QList_Text)
|
|
//Q_GLOBAL_STATIC(QStringMap, null_QStringMap)
|
|
Q_GLOBAL_STATIC(QStringMultiMap, null_QStringMultiMap)
|
|
|
|
struct Macro
|
|
{
|
|
QString defaultDef;
|
|
Location defaultDefLocation;
|
|
QStringMap otherDefs;
|
|
int numParams;
|
|
};
|
|
|
|
enum {
|
|
CMD_A,
|
|
CMD_ABSTRACT,
|
|
CMD_ANNOTATEDLIST,
|
|
CMD_BADCODE,
|
|
CMD_BASENAME,
|
|
CMD_BOLD,
|
|
CMD_BRIEF,
|
|
CMD_C,
|
|
CMD_CAPTION,
|
|
CMD_CHAPTER, // 9
|
|
CMD_CODE,
|
|
CMD_CODELINE,
|
|
CMD_DIV,
|
|
CMD_DOTS,
|
|
CMD_ELSE,
|
|
CMD_ENDABSTRACT,
|
|
CMD_ENDCHAPTER,
|
|
CMD_ENDCODE,
|
|
CMD_ENDDIV,
|
|
CMD_ENDFOOTNOTE,
|
|
CMD_ENDIF,
|
|
CMD_ENDLEGALESE,
|
|
CMD_ENDLINK,
|
|
CMD_ENDLIST,
|
|
CMD_ENDOMIT,
|
|
CMD_ENDPART,
|
|
CMD_ENDQUOTATION,
|
|
CMD_ENDRAW,
|
|
CMD_ENDSECTION1,
|
|
CMD_ENDSECTION2,
|
|
CMD_ENDSECTION3,
|
|
CMD_ENDSECTION4,
|
|
CMD_ENDSIDEBAR,
|
|
CMD_ENDTABLE,
|
|
CMD_EXPIRE,
|
|
CMD_FOOTNOTE,
|
|
CMD_GENERATELIST,
|
|
CMD_GRANULARITY,
|
|
CMD_HEADER,
|
|
CMD_I,
|
|
CMD_IF,
|
|
CMD_IMAGE,
|
|
CMD_INCLUDE,
|
|
CMD_INLINEIMAGE,
|
|
CMD_INDEX,
|
|
CMD_KEYWORD,
|
|
CMD_L,
|
|
CMD_LEGALESE,
|
|
CMD_LINK,
|
|
CMD_LIST,
|
|
CMD_META,
|
|
CMD_NEWCODE,
|
|
CMD_O,
|
|
CMD_OLDCODE,
|
|
CMD_OMIT,
|
|
CMD_OMITVALUE,
|
|
CMD_OVERLOAD,
|
|
CMD_PART,
|
|
CMD_PRINTLINE,
|
|
CMD_PRINTTO,
|
|
CMD_PRINTUNTIL,
|
|
CMD_QUOTATION,
|
|
CMD_QUOTEFILE,
|
|
CMD_QUOTEFROMFILE,
|
|
CMD_QUOTEFUNCTION,
|
|
CMD_RAW,
|
|
CMD_ROW,
|
|
CMD_SA,
|
|
CMD_SECTION1, // 68
|
|
CMD_SECTION2, // 69
|
|
CMD_SECTION3, // 70
|
|
CMD_SECTION4, // 71
|
|
CMD_SIDEBAR,
|
|
CMD_SINCELIST,
|
|
CMD_SKIPLINE,
|
|
CMD_SKIPTO,
|
|
CMD_SKIPUNTIL,
|
|
CMD_SNIPPET,
|
|
CMD_SPAN,
|
|
CMD_SUB,
|
|
CMD_SUP,
|
|
CMD_TABLE,
|
|
CMD_TABLEOFCONTENTS,
|
|
CMD_TARGET,
|
|
CMD_TT,
|
|
CMD_UNDERLINE,
|
|
CMD_UNICODE,
|
|
CMD_VALUE,
|
|
CMD_WARNING,
|
|
CMD_QML,
|
|
CMD_ENDQML,
|
|
CMD_CPP,
|
|
CMD_ENDCPP,
|
|
CMD_QMLTEXT,
|
|
CMD_ENDQMLTEXT,
|
|
CMD_CPPTEXT,
|
|
CMD_ENDCPPTEXT,
|
|
CMD_JS,
|
|
CMD_ENDJS,
|
|
NOT_A_CMD
|
|
};
|
|
|
|
static struct {
|
|
const char *english;
|
|
int no;
|
|
QString *alias;
|
|
} cmds[] = {
|
|
{ "a", CMD_A, 0 },
|
|
{ "abstract", CMD_ABSTRACT, 0 },
|
|
{ "annotatedlist", CMD_ANNOTATEDLIST, 0 },
|
|
{ "badcode", CMD_BADCODE, 0 },
|
|
{ "basename", CMD_BASENAME, 0 }, // ### don't document for now
|
|
{ "bold", CMD_BOLD, 0 },
|
|
{ "brief", CMD_BRIEF, 0 },
|
|
{ "c", CMD_C, 0 },
|
|
{ "caption", CMD_CAPTION, 0 },
|
|
{ "chapter", CMD_CHAPTER, 0 },
|
|
{ "code", CMD_CODE, 0 },
|
|
{ "codeline", CMD_CODELINE, 0},
|
|
{ "div", CMD_DIV, 0 },
|
|
{ "dots", CMD_DOTS, 0 },
|
|
{ "else", CMD_ELSE, 0 },
|
|
{ "endabstract", CMD_ENDABSTRACT, 0 },
|
|
{ "endchapter", CMD_ENDCHAPTER, 0 },
|
|
{ "endcode", CMD_ENDCODE, 0 },
|
|
{ "enddiv", CMD_ENDDIV, 0 },
|
|
{ "endfootnote", CMD_ENDFOOTNOTE, 0 },
|
|
{ "endif", CMD_ENDIF, 0 },
|
|
{ "endlegalese", CMD_ENDLEGALESE, 0 },
|
|
{ "endlink", CMD_ENDLINK, 0 },
|
|
{ "endlist", CMD_ENDLIST, 0 },
|
|
{ "endomit", CMD_ENDOMIT, 0 },
|
|
{ "endpart", CMD_ENDPART, 0 },
|
|
{ "endquotation", CMD_ENDQUOTATION, 0 },
|
|
{ "endraw", CMD_ENDRAW, 0 },
|
|
{ "endsection1", CMD_ENDSECTION1, 0 }, // ### don't document for now
|
|
{ "endsection2", CMD_ENDSECTION2, 0 }, // ### don't document for now
|
|
{ "endsection3", CMD_ENDSECTION3, 0 }, // ### don't document for now
|
|
{ "endsection4", CMD_ENDSECTION4, 0 }, // ### don't document for now
|
|
{ "endsidebar", CMD_ENDSIDEBAR, 0 },
|
|
{ "endtable", CMD_ENDTABLE, 0 },
|
|
{ "expire", CMD_EXPIRE, 0 },
|
|
{ "footnote", CMD_FOOTNOTE, 0 },
|
|
{ "generatelist", CMD_GENERATELIST, 0 },
|
|
{ "granularity", CMD_GRANULARITY, 0 }, // ### don't document for now
|
|
{ "header", CMD_HEADER, 0 },
|
|
{ "i", CMD_I, 0 },
|
|
{ "if", CMD_IF, 0 },
|
|
{ "image", CMD_IMAGE, 0 },
|
|
{ "include", CMD_INCLUDE, 0 },
|
|
{ "inlineimage", CMD_INLINEIMAGE, 0 },
|
|
{ "index", CMD_INDEX, 0 }, // ### don't document for now
|
|
{ "keyword", CMD_KEYWORD, 0 },
|
|
{ "l", CMD_L, 0 },
|
|
{ "legalese", CMD_LEGALESE, 0 },
|
|
{ "link", CMD_LINK, 0 },
|
|
{ "list", CMD_LIST, 0 },
|
|
{ "meta", CMD_META, 0 },
|
|
{ "newcode", CMD_NEWCODE, 0 },
|
|
{ "o", CMD_O, 0 },
|
|
{ "oldcode", CMD_OLDCODE, 0 },
|
|
{ "omit", CMD_OMIT, 0 },
|
|
{ "omitvalue", CMD_OMITVALUE, 0 },
|
|
{ "overload", CMD_OVERLOAD, 0 },
|
|
{ "part", CMD_PART, 0 },
|
|
{ "printline", CMD_PRINTLINE, 0 },
|
|
{ "printto", CMD_PRINTTO, 0 },
|
|
{ "printuntil", CMD_PRINTUNTIL, 0 },
|
|
{ "quotation", CMD_QUOTATION, 0 },
|
|
{ "quotefile", CMD_QUOTEFILE, 0 },
|
|
{ "quotefromfile", CMD_QUOTEFROMFILE, 0 },
|
|
{ "quotefunction", CMD_QUOTEFUNCTION, 0 }, // ### don't document for now
|
|
{ "raw", CMD_RAW, 0 },
|
|
{ "row", CMD_ROW, 0 },
|
|
{ "sa", CMD_SA, 0 },
|
|
{ "section1", CMD_SECTION1, 0 },
|
|
{ "section2", CMD_SECTION2, 0 },
|
|
{ "section3", CMD_SECTION3, 0 },
|
|
{ "section4", CMD_SECTION4, 0 },
|
|
{ "sidebar", CMD_SIDEBAR, 0 }, // ### don't document for now
|
|
{ "sincelist", CMD_SINCELIST, 0 },
|
|
{ "skipline", CMD_SKIPLINE, 0 },
|
|
{ "skipto", CMD_SKIPTO, 0 },
|
|
{ "skipuntil", CMD_SKIPUNTIL, 0 },
|
|
{ "snippet", CMD_SNIPPET, 0 },
|
|
{ "span", CMD_SPAN, 0 },
|
|
{ "sub", CMD_SUB, 0 },
|
|
{ "sup", CMD_SUP, 0 },
|
|
{ "table", CMD_TABLE, 0 },
|
|
{ "tableofcontents", CMD_TABLEOFCONTENTS, 0 },
|
|
{ "target", CMD_TARGET, 0 },
|
|
{ "tt", CMD_TT, 0 },
|
|
{ "underline", CMD_UNDERLINE, 0 },
|
|
{ "unicode", CMD_UNICODE, 0 },
|
|
{ "value", CMD_VALUE, 0 },
|
|
{ "warning", CMD_WARNING, 0 },
|
|
{ "qml", CMD_QML, 0 },
|
|
{ "endqml", CMD_ENDQML, 0 },
|
|
{ "cpp", CMD_CPP, 0 },
|
|
{ "endcpp", CMD_ENDCPP, 0 },
|
|
{ "qmltext", CMD_QMLTEXT, 0 },
|
|
{ "endqmltext", CMD_ENDQMLTEXT, 0 },
|
|
{ "cpptext", CMD_CPPTEXT, 0 },
|
|
{ "endcpptext", CMD_ENDCPPTEXT, 0 },
|
|
{ "js", CMD_JS, 0 },
|
|
{ "endjs", CMD_ENDJS, 0 },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
typedef QHash<QString, int> QHash_QString_int;
|
|
typedef QHash<QString, Macro> QHash_QString_Macro;
|
|
|
|
Q_GLOBAL_STATIC(QStringMap, aliasMap)
|
|
Q_GLOBAL_STATIC(QHash_QString_int, cmdHash)
|
|
Q_GLOBAL_STATIC(QHash_QString_Macro, macroHash)
|
|
|
|
class DocPrivateExtra
|
|
{
|
|
public:
|
|
QString baseName;
|
|
Doc::Sections granularity;
|
|
Doc::Sections section; // ###
|
|
QList<Atom*> tableOfContents;
|
|
QList<int> tableOfContentsLevels;
|
|
QList<Atom*> keywords;
|
|
QList<Atom*> targets;
|
|
QStringMultiMap metaMap;
|
|
|
|
DocPrivateExtra()
|
|
: granularity(Doc::Part) { }
|
|
};
|
|
|
|
struct Shared // ### get rid of
|
|
{
|
|
Shared()
|
|
: count(1) { }
|
|
void ref() { ++count; }
|
|
bool deref() { return (--count == 0); }
|
|
|
|
int count;
|
|
};
|
|
|
|
static QString cleanLink(const QString &link)
|
|
{
|
|
int colonPos = link.indexOf(':');
|
|
if ((colonPos == -1) ||
|
|
(!link.startsWith("file:") && !link.startsWith("mailto:")))
|
|
return link;
|
|
return link.mid(colonPos + 1).simplified();
|
|
}
|
|
|
|
class DocPrivate : public Shared
|
|
{
|
|
public:
|
|
DocPrivate(const Location& start = Location::null,
|
|
const Location& end = Location::null,
|
|
const QString& source = "");
|
|
~DocPrivate();
|
|
|
|
void addAlso(const Text& also);
|
|
void constructExtra();
|
|
bool isEnumDocSimplifiable() const;
|
|
|
|
// ### move some of this in DocPrivateExtra
|
|
Location start_loc;
|
|
Location end_loc;
|
|
QString src;
|
|
Text text;
|
|
QSet<QString> params;
|
|
QList<Text> alsoList;
|
|
QStringList enumItemList;
|
|
QStringList omitEnumItemList;
|
|
QSet<QString> metacommandsUsed;
|
|
QCommandMap metaCommandMap;
|
|
bool hasLegalese : 1;
|
|
bool hasSectioningUnits : 1;
|
|
DocPrivateExtra *extra;
|
|
};
|
|
|
|
DocPrivate::DocPrivate(const Location& start,
|
|
const Location& end,
|
|
const QString& source)
|
|
: start_loc(start),
|
|
end_loc(end),
|
|
src(source),
|
|
hasLegalese(false),
|
|
hasSectioningUnits(false),
|
|
extra(0)
|
|
{
|
|
// nothing.
|
|
}
|
|
|
|
DocPrivate::~DocPrivate()
|
|
{
|
|
delete extra;
|
|
}
|
|
|
|
void DocPrivate::addAlso(const Text& also)
|
|
{
|
|
alsoList.append(also);
|
|
}
|
|
|
|
void DocPrivate::constructExtra()
|
|
{
|
|
if (extra == 0)
|
|
extra = new DocPrivateExtra;
|
|
}
|
|
|
|
bool DocPrivate::isEnumDocSimplifiable() const
|
|
{
|
|
bool justMetColon = false;
|
|
int numValueTables = 0;
|
|
|
|
const Atom *atom = text.firstAtom();
|
|
while (atom) {
|
|
if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
|
|
justMetColon = atom->string().endsWith(":");
|
|
}
|
|
else if ((atom->type() == Atom::ListLeft) &&
|
|
(atom->string() == ATOM_LIST_VALUE)) {
|
|
if (justMetColon || numValueTables > 0)
|
|
return false;
|
|
++numValueTables;
|
|
}
|
|
atom = atom->next();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
class DocParser
|
|
{
|
|
public:
|
|
void parse(const QString &source,
|
|
DocPrivate *docPrivate,
|
|
const QSet<QString> &metaCommandSet);
|
|
|
|
static int endCmdFor(int cmd);
|
|
static QString cmdName(int cmd);
|
|
static QString endCmdName(int cmd);
|
|
static QString untabifyEtc(const QString& str);
|
|
static int indentLevel(const QString& str);
|
|
static QString unindent(int level, const QString& str);
|
|
static QString slashed(const QString& str);
|
|
|
|
static int tabSize;
|
|
static QStringList exampleFiles;
|
|
static QStringList exampleDirs;
|
|
static QStringList sourceFiles;
|
|
static QStringList sourceDirs;
|
|
static bool quoting;
|
|
|
|
private:
|
|
Location& location();
|
|
QString detailsUnknownCommand(const QSet<QString>& metaCommandSet,
|
|
const QString& str);
|
|
void checkExpiry(const QString& date);
|
|
void insertBaseName(const QString &baseName);
|
|
void insertTarget(const QString& target, bool keyword);
|
|
void include(const QString& fileName, const QString& identifier);
|
|
void startFormat(const QString& format, int cmd);
|
|
bool openCommand(int cmd);
|
|
bool closeCommand(int endCmd);
|
|
void startSection(Doc::Sections unit, int cmd);
|
|
void endSection(int unit, int endCmd);
|
|
void parseAlso();
|
|
void append(Atom::Type type, const QString& string = "");
|
|
void append(Atom::Type type, const QString& p1, const QString& p2);
|
|
void appendChar(QChar ch);
|
|
void appendWord(const QString &word);
|
|
void appendToCode(const QString &code);
|
|
void appendToCode(const QString &code, Atom::Type defaultType);
|
|
void startNewPara();
|
|
void enterPara(Atom::Type leftType = Atom::ParaLeft,
|
|
Atom::Type rightType = Atom::ParaRight,
|
|
const QString& string = "");
|
|
void leavePara();
|
|
void leaveValue();
|
|
void leaveValueList();
|
|
void leaveTableRow();
|
|
CodeMarker *quoteFromFile();
|
|
void expandMacro(const QString& name, const QString& def, int numParams);
|
|
QString expandMacroToString(const QString &name, const QString &def, int numParams);
|
|
Doc::Sections getSectioningUnit();
|
|
QString getArgument(bool verbatim = false);
|
|
QString getOptionalArgument();
|
|
QString getRestOfLine();
|
|
QString getMetaCommandArgument(const QString &cmdStr);
|
|
QString getUntilEnd(int cmd);
|
|
QString getCode(int cmd, CodeMarker *marker);
|
|
QString getUnmarkedCode(int cmd);
|
|
|
|
bool isBlankLine();
|
|
bool isLeftBraceAhead();
|
|
void skipSpacesOnLine();
|
|
void skipSpacesOrOneEndl();
|
|
void skipAllSpaces();
|
|
void skipToNextPreprocessorCommand();
|
|
|
|
QStack<int> openedInputs;
|
|
|
|
QString in;
|
|
int pos;
|
|
int len;
|
|
Location cachedLoc;
|
|
int cachedPos;
|
|
|
|
DocPrivate* priv;
|
|
enum ParagraphState {
|
|
OutsideParagraph,
|
|
InSingleLineParagraph,
|
|
InMultiLineParagraph
|
|
};
|
|
ParagraphState paraState;
|
|
bool inTableHeader;
|
|
bool inTableRow;
|
|
bool inTableItem;
|
|
bool indexStartedPara; // ### rename
|
|
Atom::Type pendingParaLeftType;
|
|
Atom::Type pendingParaRightType;
|
|
QString pendingParaString;
|
|
|
|
int braceDepth;
|
|
int minIndent;
|
|
Doc::Sections currentSection;
|
|
QMap<QString, Location> targetMap;
|
|
QMap<int, QString> pendingFormats;
|
|
QStack<int> openedCommands;
|
|
QStack<OpenedList> openedLists;
|
|
Quoter quoter;
|
|
};
|
|
|
|
int DocParser::tabSize;
|
|
QStringList DocParser::exampleFiles;
|
|
QStringList DocParser::exampleDirs;
|
|
QStringList DocParser::sourceFiles;
|
|
QStringList DocParser::sourceDirs;
|
|
bool DocParser::quoting;
|
|
|
|
/*!
|
|
Parse the \a source string to build a Text data structure
|
|
in \a docPrivate. The Text data structure is a linked list
|
|
of Atoms.
|
|
|
|
\a metaCommandSet is the set of metacommands that may be
|
|
found in \a source. These metacommands are not markup text
|
|
commands. They are topic commands and related metacommands.
|
|
*/
|
|
void DocParser::parse(const QString& source,
|
|
DocPrivate *docPrivate,
|
|
const QSet<QString>& metaCommandSet)
|
|
{
|
|
in = source;
|
|
pos = 0;
|
|
len = in.length();
|
|
cachedLoc = docPrivate->start_loc;
|
|
cachedPos = 0;
|
|
priv = docPrivate;
|
|
priv->text << Atom::Nop;
|
|
|
|
paraState = OutsideParagraph;
|
|
inTableHeader = false;
|
|
inTableRow = false;
|
|
inTableItem = false;
|
|
indexStartedPara = false;
|
|
pendingParaLeftType = Atom::Nop;
|
|
pendingParaRightType = Atom::Nop;
|
|
|
|
braceDepth = 0;
|
|
minIndent = INT_MAX;
|
|
currentSection = Doc::NoSection;
|
|
openedCommands.push(CMD_OMIT);
|
|
quoter.reset();
|
|
|
|
CodeMarker *marker = 0;
|
|
Atom *currentLinkAtom = 0;
|
|
QString p1, p2;
|
|
QStack<bool> preprocessorSkipping;
|
|
int numPreprocessorSkipping = 0;
|
|
|
|
while (pos < len) {
|
|
QChar ch = in.at(pos);
|
|
|
|
switch (ch.unicode()) {
|
|
case '\\':
|
|
{
|
|
QString cmdStr;
|
|
pos++;
|
|
while (pos < len) {
|
|
ch = in.at(pos);
|
|
if (ch.isLetterOrNumber()) {
|
|
cmdStr += ch;
|
|
pos++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
if (cmdStr.isEmpty()) {
|
|
if (pos < len) {
|
|
enterPara();
|
|
if (in.at(pos).isSpace()) {
|
|
skipAllSpaces();
|
|
appendChar(QLatin1Char(' '));
|
|
}
|
|
else {
|
|
appendChar(in.at(pos++));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int cmd = cmdHash()->value(cmdStr,NOT_A_CMD);
|
|
switch (cmd) {
|
|
case CMD_A:
|
|
enterPara();
|
|
p1 = getArgument();
|
|
append(Atom::FormattingLeft,ATOM_FORMATTING_PARAMETER);
|
|
append(Atom::String, p1);
|
|
append(Atom::FormattingRight,ATOM_FORMATTING_PARAMETER);
|
|
priv->params.insert(p1);
|
|
break;
|
|
case CMD_ABSTRACT:
|
|
if (openCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::AbstractLeft);
|
|
}
|
|
break;
|
|
case CMD_BADCODE:
|
|
leavePara();
|
|
append(Atom::CodeBad,getCode(CMD_BADCODE, marker));
|
|
break;
|
|
case CMD_BASENAME:
|
|
leavePara();
|
|
insertBaseName(getArgument());
|
|
break;
|
|
case CMD_BOLD:
|
|
startFormat(ATOM_FORMATTING_BOLD, cmd);
|
|
break;
|
|
case CMD_BRIEF:
|
|
leavePara();
|
|
enterPara(Atom::BriefLeft, Atom::BriefRight);
|
|
break;
|
|
case CMD_C:
|
|
enterPara();
|
|
p1 = untabifyEtc(getArgument(true));
|
|
marker = CodeMarker::markerForCode(p1);
|
|
append(Atom::C, marker->markedUpCode(p1, 0, location()));
|
|
break;
|
|
case CMD_CAPTION:
|
|
leavePara();
|
|
enterPara(Atom::CaptionLeft, Atom::CaptionRight);
|
|
break;
|
|
case CMD_CHAPTER:
|
|
startSection(Doc::Chapter, cmd);
|
|
break;
|
|
case CMD_CODE:
|
|
leavePara();
|
|
append(Atom::Code, getCode(CMD_CODE, 0));
|
|
break;
|
|
case CMD_QML:
|
|
leavePara();
|
|
append(Atom::Qml, getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String("QML"))));
|
|
break;
|
|
case CMD_QMLTEXT:
|
|
append(Atom::QmlText);
|
|
break;
|
|
case CMD_JS:
|
|
leavePara();
|
|
append(Atom::JavaScript, getCode(CMD_JS, CodeMarker::markerForLanguage(QLatin1String("JavaScript"))));
|
|
break;
|
|
case CMD_DIV:
|
|
leavePara();
|
|
p1 = getArgument(true);
|
|
append(Atom::DivLeft, p1);
|
|
openedCommands.push(cmd);
|
|
break;
|
|
case CMD_ENDDIV:
|
|
leavePara();
|
|
append(Atom::DivRight);
|
|
closeCommand(cmd);
|
|
break;
|
|
case CMD_CODELINE:
|
|
{
|
|
if (!quoting) {
|
|
if (priv->text.lastAtom()->type() == Atom::Code
|
|
&& priv->text.lastAtom()->string().endsWith("\n\n"))
|
|
priv->text.lastAtom()->chopString();
|
|
appendToCode("\n");
|
|
}
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, " ");
|
|
}
|
|
}
|
|
break;
|
|
case CMD_DOTS:
|
|
{
|
|
if (!quoting) {
|
|
if (priv->text.lastAtom()->type() == Atom::Code
|
|
&& priv->text.lastAtom()->string().endsWith("\n\n"))
|
|
priv->text.lastAtom()->chopString();
|
|
|
|
QString arg = getOptionalArgument();
|
|
int indent = 4;
|
|
if (!arg.isEmpty())
|
|
indent = arg.toInt();
|
|
for (int i = 0; i < indent; ++i)
|
|
appendToCode(" ");
|
|
appendToCode("...\n");
|
|
}
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
QString arg = getOptionalArgument();
|
|
if (arg.isEmpty())
|
|
arg = "4";
|
|
append(Atom::CodeQuoteArgument, arg);
|
|
}
|
|
}
|
|
break;
|
|
case CMD_ELSE:
|
|
if (preprocessorSkipping.size() > 0) {
|
|
if (preprocessorSkipping.top()) {
|
|
--numPreprocessorSkipping;
|
|
}
|
|
else {
|
|
++numPreprocessorSkipping;
|
|
}
|
|
preprocessorSkipping.top() = !preprocessorSkipping.top();
|
|
(void)getRestOfLine(); // ### should ensure that it's empty
|
|
if (numPreprocessorSkipping)
|
|
skipToNextPreprocessorCommand();
|
|
}
|
|
else {
|
|
location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ELSE)));
|
|
}
|
|
break;
|
|
case CMD_ENDABSTRACT:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::AbstractRight);
|
|
}
|
|
break;
|
|
case CMD_ENDCHAPTER:
|
|
endSection(Doc::Chapter, cmd);
|
|
break;
|
|
case CMD_ENDCODE:
|
|
closeCommand(cmd);
|
|
break;
|
|
case CMD_ENDQML:
|
|
closeCommand(cmd);
|
|
break;
|
|
case CMD_ENDQMLTEXT:
|
|
append(Atom::EndQmlText);
|
|
break;
|
|
case CMD_ENDJS:
|
|
closeCommand(cmd);
|
|
break;
|
|
case CMD_ENDFOOTNOTE:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::FootnoteRight);
|
|
paraState = InMultiLineParagraph; // ###
|
|
}
|
|
break;
|
|
case CMD_ENDIF:
|
|
if (preprocessorSkipping.count() > 0) {
|
|
if (preprocessorSkipping.pop())
|
|
--numPreprocessorSkipping;
|
|
(void)getRestOfLine(); // ### should ensure that it's empty
|
|
if (numPreprocessorSkipping)
|
|
skipToNextPreprocessorCommand();
|
|
}
|
|
else {
|
|
location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDIF)));
|
|
}
|
|
break;
|
|
case CMD_ENDLEGALESE:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::LegaleseRight);
|
|
}
|
|
break;
|
|
case CMD_ENDLINK:
|
|
if (closeCommand(cmd)) {
|
|
if (priv->text.lastAtom()->type() == Atom::String
|
|
&& priv->text.lastAtom()->string().endsWith(" "))
|
|
priv->text.lastAtom()->chopString();
|
|
append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
|
|
}
|
|
break;
|
|
case CMD_ENDLIST:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
if (openedLists.top().isStarted()) {
|
|
append(Atom::ListItemRight,
|
|
openedLists.top().styleString());
|
|
append(Atom::ListRight,
|
|
openedLists.top().styleString());
|
|
}
|
|
openedLists.pop();
|
|
}
|
|
break;
|
|
case CMD_ENDOMIT:
|
|
closeCommand(cmd);
|
|
break;
|
|
case CMD_ENDPART:
|
|
endSection(Doc::Part, cmd);
|
|
break;
|
|
case CMD_ENDQUOTATION:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::QuotationRight);
|
|
}
|
|
break;
|
|
case CMD_ENDRAW:
|
|
location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_ENDRAW)));
|
|
break;
|
|
case CMD_ENDSECTION1:
|
|
endSection(Doc::Section1, cmd);
|
|
break;
|
|
case CMD_ENDSECTION2:
|
|
endSection(Doc::Section2, cmd);
|
|
break;
|
|
case CMD_ENDSECTION3:
|
|
endSection(Doc::Section3, cmd);
|
|
break;
|
|
case CMD_ENDSECTION4:
|
|
endSection(Doc::Section4, cmd);
|
|
break;
|
|
case CMD_ENDSIDEBAR:
|
|
if (closeCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::SidebarRight);
|
|
}
|
|
break;
|
|
case CMD_ENDTABLE:
|
|
if (closeCommand(cmd)) {
|
|
leaveTableRow();
|
|
append(Atom::TableRight);
|
|
}
|
|
break;
|
|
case CMD_EXPIRE:
|
|
checkExpiry(getArgument());
|
|
break;
|
|
case CMD_FOOTNOTE:
|
|
if (openCommand(cmd)) {
|
|
enterPara();
|
|
append(Atom::FootnoteLeft);
|
|
paraState = OutsideParagraph; // ###
|
|
}
|
|
break;
|
|
case CMD_ANNOTATEDLIST:
|
|
append(Atom::AnnotatedList, getArgument());
|
|
break;
|
|
case CMD_SINCELIST:
|
|
append(Atom::SinceList, getRestOfLine().simplified());
|
|
break;
|
|
case CMD_GENERATELIST:
|
|
append(Atom::GeneratedList, getArgument());
|
|
break;
|
|
case CMD_GRANULARITY:
|
|
priv->constructExtra();
|
|
priv->extra->granularity = getSectioningUnit();
|
|
break;
|
|
case CMD_HEADER:
|
|
if (openedCommands.top() == CMD_TABLE) {
|
|
leaveTableRow();
|
|
append(Atom::TableHeaderLeft);
|
|
inTableHeader = true;
|
|
}
|
|
else {
|
|
if (openedCommands.contains(CMD_TABLE)) {
|
|
location().warning(tr("Cannot use '\\%1' within '\\%2'")
|
|
.arg(cmdName(CMD_HEADER))
|
|
.arg(cmdName(openedCommands.top())));
|
|
}
|
|
else {
|
|
location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
|
|
.arg(cmdName(CMD_HEADER))
|
|
.arg(cmdName(CMD_TABLE)));
|
|
}
|
|
}
|
|
break;
|
|
case CMD_I:
|
|
startFormat(ATOM_FORMATTING_ITALIC, cmd);
|
|
break;
|
|
case CMD_IF:
|
|
preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
|
|
if (preprocessorSkipping.top())
|
|
++numPreprocessorSkipping;
|
|
if (numPreprocessorSkipping)
|
|
skipToNextPreprocessorCommand();
|
|
break;
|
|
case CMD_IMAGE:
|
|
leaveValueList();
|
|
append(Atom::Image, getArgument());
|
|
append(Atom::ImageText, getRestOfLine());
|
|
break;
|
|
case CMD_INCLUDE:
|
|
{
|
|
QString fileName = getArgument();
|
|
QString identifier = getRestOfLine();
|
|
include(fileName, identifier);
|
|
}
|
|
break;
|
|
case CMD_INLINEIMAGE:
|
|
enterPara();
|
|
append(Atom::InlineImage, getArgument());
|
|
append(Atom::ImageText, getRestOfLine());
|
|
append(Atom::String, " ");
|
|
break;
|
|
case CMD_INDEX:
|
|
if (paraState == OutsideParagraph) {
|
|
enterPara();
|
|
indexStartedPara = true;
|
|
}
|
|
else {
|
|
const Atom *last = priv->text.lastAtom();
|
|
if (indexStartedPara &&
|
|
(last->type() != Atom::FormattingRight ||
|
|
last->string() != ATOM_FORMATTING_INDEX))
|
|
indexStartedPara = false;
|
|
}
|
|
startFormat(ATOM_FORMATTING_INDEX, cmd);
|
|
break;
|
|
case CMD_KEYWORD:
|
|
insertTarget(getRestOfLine(),true);
|
|
break;
|
|
case CMD_L:
|
|
enterPara();
|
|
if (isLeftBraceAhead()) {
|
|
p1 = getArgument();
|
|
append(Atom::Link, p1);
|
|
if (isLeftBraceAhead()) {
|
|
currentLinkAtom = priv->text.lastAtom();
|
|
startFormat(ATOM_FORMATTING_LINK, cmd);
|
|
}
|
|
else {
|
|
append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
|
|
append(Atom::String, cleanLink(p1));
|
|
append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
|
|
}
|
|
}
|
|
else {
|
|
p1 = getArgument();
|
|
append(Atom::Link, p1);
|
|
append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
|
|
append(Atom::String, cleanLink(p1));
|
|
append(Atom::FormattingRight, ATOM_FORMATTING_LINK);
|
|
}
|
|
break;
|
|
case CMD_LEGALESE:
|
|
leavePara();
|
|
if (openCommand(cmd))
|
|
append(Atom::LegaleseLeft);
|
|
docPrivate->hasLegalese = true;
|
|
break;
|
|
case CMD_LINK:
|
|
if (openCommand(cmd)) {
|
|
enterPara();
|
|
p1 = getArgument();
|
|
append(Atom::Link, p1);
|
|
append(Atom::FormattingLeft, ATOM_FORMATTING_LINK);
|
|
skipSpacesOrOneEndl();
|
|
}
|
|
break;
|
|
case CMD_LIST:
|
|
if (openCommand(cmd)) {
|
|
leavePara();
|
|
openedLists.push(OpenedList(location(),
|
|
getOptionalArgument()));
|
|
}
|
|
break;
|
|
case CMD_META:
|
|
priv->constructExtra();
|
|
p1 = getArgument();
|
|
priv->extra->metaMap.insert(p1, getArgument());
|
|
break;
|
|
case CMD_NEWCODE:
|
|
location().warning(tr("Unexpected '\\%1'").arg(cmdName(CMD_NEWCODE)));
|
|
break;
|
|
case CMD_O:
|
|
leavePara();
|
|
if (openedCommands.top() == CMD_LIST) {
|
|
if (openedLists.top().isStarted()) {
|
|
append(Atom::ListItemRight,
|
|
openedLists.top().styleString());
|
|
}
|
|
else {
|
|
append(Atom::ListLeft,
|
|
openedLists.top().styleString());
|
|
}
|
|
openedLists.top().next();
|
|
append(Atom::ListItemNumber,
|
|
openedLists.top().numberString());
|
|
append(Atom::ListItemLeft,
|
|
openedLists.top().styleString());
|
|
enterPara();
|
|
}
|
|
else if (openedCommands.top() == CMD_TABLE) {
|
|
p1 = "1,1";
|
|
if (isLeftBraceAhead()) {
|
|
p1 = getArgument();
|
|
if (isLeftBraceAhead()) {
|
|
p2 = getArgument();
|
|
}
|
|
}
|
|
|
|
if (!inTableHeader && !inTableRow) {
|
|
location().warning(tr("Missing '\\%1' or '\\%1' before '\\%3'")
|
|
.arg(cmdName(CMD_HEADER))
|
|
.arg(cmdName(CMD_ROW))
|
|
.arg(cmdName(CMD_O)));
|
|
append(Atom::TableRowLeft);
|
|
inTableRow = true;
|
|
}
|
|
else if (inTableItem) {
|
|
append(Atom::TableItemRight);
|
|
inTableItem = false;
|
|
}
|
|
|
|
append(Atom::TableItemLeft, p1, p2);
|
|
inTableItem = true;
|
|
}
|
|
else {
|
|
location().warning(tr("Command '\\%1' outside of '\\%2' and '\\%3'")
|
|
.arg(cmdName(cmd))
|
|
.arg(cmdName(CMD_LIST))
|
|
.arg(cmdName(CMD_TABLE)));
|
|
}
|
|
break;
|
|
case CMD_OLDCODE:
|
|
leavePara();
|
|
append(Atom::CodeOld, getCode(CMD_OLDCODE, marker));
|
|
append(Atom::CodeNew, getCode(CMD_NEWCODE, marker));
|
|
break;
|
|
case CMD_OMIT:
|
|
getUntilEnd(cmd);
|
|
break;
|
|
case CMD_OMITVALUE:
|
|
p1 = getArgument();
|
|
if (!priv->enumItemList.contains(p1))
|
|
priv->enumItemList.append(p1);
|
|
if (!priv->omitEnumItemList.contains(p1))
|
|
priv->omitEnumItemList.append(p1);
|
|
break;
|
|
case CMD_PART:
|
|
startSection(Doc::Part, cmd);
|
|
break;
|
|
case CMD_PRINTLINE:
|
|
leavePara();
|
|
if (!quoting)
|
|
appendToCode(quoter.quoteLine(location(), cmdStr,
|
|
getRestOfLine()));
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_PRINTTO:
|
|
leavePara();
|
|
if (!quoting)
|
|
appendToCode(quoter.quoteTo(location(), cmdStr,
|
|
getRestOfLine()));
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_PRINTUNTIL:
|
|
leavePara();
|
|
if (!quoting)
|
|
appendToCode(quoter.quoteUntil(location(), cmdStr,
|
|
getRestOfLine()));
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_QUOTATION:
|
|
if (openCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::QuotationLeft);
|
|
}
|
|
break;
|
|
case CMD_QUOTEFILE:
|
|
{
|
|
leavePara();
|
|
QString fileName = getArgument();
|
|
Doc::quoteFromFile(location(), quoter, fileName);
|
|
if (!quoting) {
|
|
append(Atom::Code,
|
|
quoter.quoteTo(location(), cmdStr, ""));
|
|
quoter.reset();
|
|
}
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, fileName);
|
|
}
|
|
break;
|
|
}
|
|
case CMD_QUOTEFROMFILE:
|
|
leavePara();
|
|
if (!quoting)
|
|
quoteFromFile();
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getArgument());
|
|
}
|
|
break;
|
|
case CMD_QUOTEFUNCTION:
|
|
leavePara();
|
|
marker = quoteFromFile();
|
|
p1 = getRestOfLine();
|
|
if (!quoting) {
|
|
quoter.quoteTo(location(), cmdStr,
|
|
slashed(marker->functionBeginRegExp(p1)));
|
|
append(Atom::Code,
|
|
quoter.quoteUntil(location(), cmdStr,
|
|
slashed(marker->functionEndRegExp(p1))));
|
|
quoter.reset();
|
|
}
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1)));
|
|
}
|
|
break;
|
|
case CMD_RAW:
|
|
leavePara();
|
|
p1 = getRestOfLine();
|
|
if (p1.isEmpty())
|
|
location().warning(tr("Missing format name after '\\%1")
|
|
.arg(cmdName(CMD_RAW)));
|
|
append(Atom::FormatIf, p1);
|
|
append(Atom::RawString, untabifyEtc(getUntilEnd(cmd)));
|
|
append(Atom::FormatElse);
|
|
append(Atom::FormatEndif);
|
|
break;
|
|
case CMD_ROW:
|
|
if (openedCommands.top() == CMD_TABLE) {
|
|
p1.clear();
|
|
if (isLeftBraceAhead())
|
|
p1 = getArgument(true);
|
|
leaveTableRow();
|
|
append(Atom::TableRowLeft,p1);
|
|
inTableRow = true;
|
|
}
|
|
else {
|
|
if (openedCommands.contains(CMD_TABLE)) {
|
|
location().warning(tr("Cannot use '\\%1' within '\\%2'")
|
|
.arg(cmdName(CMD_ROW))
|
|
.arg(cmdName(openedCommands.top())));
|
|
}
|
|
else {
|
|
location().warning(tr("Cannot use '\\%1' outside of '\\%2'")
|
|
.arg(cmdName(CMD_ROW))
|
|
.arg(cmdName(CMD_TABLE)));
|
|
}
|
|
}
|
|
break;
|
|
case CMD_SA:
|
|
parseAlso();
|
|
break;
|
|
case CMD_SECTION1:
|
|
startSection(Doc::Section1, cmd);
|
|
break;
|
|
case CMD_SECTION2:
|
|
startSection(Doc::Section2, cmd);
|
|
break;
|
|
case CMD_SECTION3:
|
|
startSection(Doc::Section3, cmd);
|
|
break;
|
|
case CMD_SECTION4:
|
|
startSection(Doc::Section4, cmd);
|
|
break;
|
|
case CMD_SIDEBAR:
|
|
if (openCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::SidebarLeft);
|
|
}
|
|
break;
|
|
case CMD_SKIPLINE:
|
|
leavePara();
|
|
if (!quoting)
|
|
quoter.quoteLine(location(),
|
|
cmdStr,
|
|
getRestOfLine());
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_SKIPTO:
|
|
leavePara();
|
|
if (!quoting)
|
|
quoter.quoteTo(location(),
|
|
cmdStr,
|
|
getRestOfLine());
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_SKIPUNTIL:
|
|
leavePara();
|
|
if (!quoting)
|
|
quoter.quoteUntil(location(),
|
|
cmdStr,
|
|
getRestOfLine());
|
|
else {
|
|
append(Atom::CodeQuoteCommand, cmdStr);
|
|
append(Atom::CodeQuoteArgument, getRestOfLine());
|
|
}
|
|
break;
|
|
case CMD_SPAN:
|
|
p1 = ATOM_FORMATTING_SPAN + getArgument(true);
|
|
startFormat(p1, cmd);
|
|
break;
|
|
case CMD_SNIPPET:
|
|
leavePara();
|
|
{
|
|
QString snippet = getArgument();
|
|
QString identifier = getRestOfLine();
|
|
if (quoting) {
|
|
append(Atom::SnippetCommand, cmdStr);
|
|
append(Atom::SnippetLocation, snippet);
|
|
append(Atom::SnippetIdentifier, identifier);
|
|
}
|
|
else {
|
|
marker = Doc::quoteFromFile(location(),quoter,snippet);
|
|
appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType());
|
|
}
|
|
}
|
|
break;
|
|
case CMD_SUB:
|
|
startFormat(ATOM_FORMATTING_SUBSCRIPT, cmd);
|
|
break;
|
|
case CMD_SUP:
|
|
startFormat(ATOM_FORMATTING_SUPERSCRIPT, cmd);
|
|
break;
|
|
case CMD_TABLE:
|
|
p1 = getRestOfLine();
|
|
if (openCommand(cmd)) {
|
|
leavePara();
|
|
append(Atom::TableLeft, p1);
|
|
inTableHeader = false;
|
|
inTableRow = false;
|
|
inTableItem = false;
|
|
}
|
|
break;
|
|
case CMD_TABLEOFCONTENTS:
|
|
p1 = "1";
|
|
if (isLeftBraceAhead())
|
|
p1 = getArgument();
|
|
p1 += ",";
|
|
p1 += QString::number((int)getSectioningUnit());
|
|
append(Atom::TableOfContents, p1);
|
|
break;
|
|
case CMD_TARGET:
|
|
insertTarget(getRestOfLine(),false);
|
|
break;
|
|
case CMD_TT:
|
|
startFormat(ATOM_FORMATTING_TELETYPE, cmd);
|
|
break;
|
|
case CMD_UNDERLINE:
|
|
startFormat(ATOM_FORMATTING_UNDERLINE, cmd);
|
|
break;
|
|
case CMD_UNICODE:
|
|
enterPara();
|
|
p1 = getArgument();
|
|
{
|
|
bool ok;
|
|
uint unicodeChar = p1.toUInt(&ok, 0);
|
|
if (!ok ||
|
|
(unicodeChar == 0x0000) ||
|
|
(unicodeChar > 0xFFFE)) {
|
|
location().warning(tr("Invalid Unicode character '%1' specified "
|
|
"with '%2'")
|
|
.arg(p1, cmdName(CMD_UNICODE)));
|
|
}
|
|
else {
|
|
append(Atom::String, QChar(unicodeChar));
|
|
}
|
|
}
|
|
break;
|
|
case CMD_VALUE:
|
|
leaveValue();
|
|
if (openedLists.top().style() == OpenedList::Value) {
|
|
p1 = getArgument();
|
|
if (!priv->enumItemList.contains(p1))
|
|
priv->enumItemList.append(p1);
|
|
|
|
openedLists.top().next();
|
|
append(Atom::ListTagLeft, ATOM_LIST_VALUE);
|
|
append(Atom::String, p1);
|
|
append(Atom::ListTagRight, ATOM_LIST_VALUE);
|
|
append(Atom::ListItemLeft, ATOM_LIST_VALUE);
|
|
|
|
skipSpacesOrOneEndl();
|
|
if (isBlankLine())
|
|
append(Atom::Nop);
|
|
}
|
|
else {
|
|
// ### problems
|
|
}
|
|
break;
|
|
case CMD_WARNING:
|
|
leavePara();
|
|
enterPara();
|
|
append(Atom::FormattingLeft, ATOM_FORMATTING_BOLD);
|
|
append(Atom::String, "Warning:");
|
|
append(Atom::FormattingRight, ATOM_FORMATTING_BOLD);
|
|
append(Atom::String, " ");
|
|
break;
|
|
case CMD_OVERLOAD:
|
|
priv->metacommandsUsed.insert(cmdStr);
|
|
p1.clear();
|
|
if (!isBlankLine())
|
|
p1 = getRestOfLine();
|
|
if (!p1.isEmpty()) {
|
|
append(Atom::ParaLeft);
|
|
append(Atom::String, "This function overloads ");
|
|
append(Atom::AutoLink,p1);
|
|
append(Atom::String, ".");
|
|
append(Atom::ParaRight);
|
|
}
|
|
else {
|
|
append(Atom::ParaLeft);
|
|
append(Atom::String,"This is an overloaded function.");
|
|
append(Atom::ParaRight);
|
|
p1 = getMetaCommandArgument(cmdStr);
|
|
}
|
|
priv->metaCommandMap[cmdStr].append(p1);
|
|
break;
|
|
case NOT_A_CMD:
|
|
if (metaCommandSet.contains(cmdStr)) {
|
|
priv->metacommandsUsed.insert(cmdStr);
|
|
QString xxx = getMetaCommandArgument(cmdStr);
|
|
priv->metaCommandMap[cmdStr].append(xxx);
|
|
}
|
|
else if (macroHash()->contains(cmdStr)) {
|
|
const Macro ¯o = macroHash()->value(cmdStr);
|
|
int numPendingFi = 0;
|
|
QStringMap::ConstIterator d;
|
|
d = macro.otherDefs.begin();
|
|
while (d != macro.otherDefs.end()) {
|
|
append(Atom::FormatIf, d.key());
|
|
expandMacro(cmdStr, *d, macro.numParams);
|
|
++d;
|
|
|
|
if (d == macro.otherDefs.end()) {
|
|
append(Atom::FormatEndif);
|
|
}
|
|
else {
|
|
append(Atom::FormatElse);
|
|
numPendingFi++;
|
|
}
|
|
}
|
|
while (numPendingFi-- > 0)
|
|
append(Atom::FormatEndif);
|
|
|
|
if (!macro.defaultDef.isEmpty()) {
|
|
if (!macro.otherDefs.isEmpty()) {
|
|
macro.defaultDefLocation.warning(
|
|
tr("Macro cannot have both "
|
|
"format-specific and qdoc- "
|
|
"syntax definitions"));
|
|
}
|
|
else {
|
|
location().push(macro.defaultDefLocation.filePath());
|
|
in.insert(pos, expandMacroToString(cmdStr, macro.defaultDef, macro.numParams));
|
|
len = in.length();
|
|
openedInputs.push(pos + macro.defaultDef.length());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
location().warning(
|
|
tr("Unknown command '\\%1'").arg(cmdStr),
|
|
detailsUnknownCommand(metaCommandSet,cmdStr));
|
|
enterPara();
|
|
append(Atom::UnknownCommand, cmdStr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case '{':
|
|
enterPara();
|
|
appendChar('{');
|
|
braceDepth++;
|
|
pos++;
|
|
break;
|
|
case '}':
|
|
{
|
|
braceDepth--;
|
|
pos++;
|
|
|
|
QMap<int, QString>::Iterator f = pendingFormats.find(braceDepth);
|
|
if (f == pendingFormats.end()) {
|
|
enterPara();
|
|
appendChar('}');
|
|
}
|
|
else {
|
|
append(Atom::FormattingRight, *f);
|
|
if (*f == ATOM_FORMATTING_INDEX) {
|
|
if (indexStartedPara)
|
|
skipAllSpaces();
|
|
}
|
|
else if (*f == ATOM_FORMATTING_LINK) {
|
|
// hack for C++ to support links like
|
|
// \l{QString::}{count()}
|
|
if (currentLinkAtom &&
|
|
currentLinkAtom->string().endsWith("::")) {
|
|
QString suffix = Text::subText(currentLinkAtom,
|
|
priv->text.lastAtom()).toString();
|
|
currentLinkAtom->appendString(suffix);
|
|
}
|
|
currentLinkAtom = 0;
|
|
}
|
|
pendingFormats.erase(f);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
bool newWord;
|
|
switch (priv->text.lastAtom()->type()) {
|
|
case Atom::ParaLeft:
|
|
newWord = true;
|
|
break;
|
|
default:
|
|
newWord = false;
|
|
}
|
|
|
|
if (paraState == OutsideParagraph) {
|
|
if (ch.isSpace()) {
|
|
++pos;
|
|
newWord = false;
|
|
}
|
|
else {
|
|
enterPara();
|
|
newWord = true;
|
|
}
|
|
}
|
|
else {
|
|
if (ch.isSpace()) {
|
|
++pos;
|
|
if ((ch == '\n') &&
|
|
(paraState == InSingleLineParagraph ||
|
|
isBlankLine())) {
|
|
leavePara();
|
|
newWord = false;
|
|
}
|
|
else {
|
|
appendChar(' ');
|
|
newWord = true;
|
|
}
|
|
}
|
|
else {
|
|
newWord = true;
|
|
}
|
|
}
|
|
|
|
if (newWord) {
|
|
int startPos = pos;
|
|
int numInternalUppercase = 0;
|
|
int numLowercase = 0;
|
|
int numStrangeSymbols = 0;
|
|
|
|
while (pos < len) {
|
|
unsigned char latin1Ch = in.at(pos).toLatin1();
|
|
if (islower(latin1Ch)) {
|
|
++numLowercase;
|
|
++pos;
|
|
}
|
|
else if (isupper(latin1Ch)) {
|
|
if (pos > startPos)
|
|
++numInternalUppercase;
|
|
++pos;
|
|
}
|
|
else if (isdigit(latin1Ch)) {
|
|
if (pos > startPos) {
|
|
++pos;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else if (latin1Ch == '_' || latin1Ch == '@') {
|
|
++numStrangeSymbols;
|
|
++pos;
|
|
}
|
|
else if (latin1Ch == ':' && pos < len - 1
|
|
&& in.at(pos + 1) == QLatin1Char(':')) {
|
|
++numStrangeSymbols;
|
|
pos += 2;
|
|
}
|
|
else if (latin1Ch == '(') {
|
|
if (pos > startPos) {
|
|
if (pos < len - 1 &&
|
|
in.at(pos + 1) == QLatin1Char(')')) {
|
|
++numStrangeSymbols;
|
|
pos += 2;
|
|
break;
|
|
}
|
|
else {
|
|
// ### handle functions with signatures
|
|
// and function calls
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pos == startPos) {
|
|
if (!ch.isSpace()) {
|
|
appendChar(ch);
|
|
++pos;
|
|
}
|
|
}
|
|
else {
|
|
QString word = in.mid(startPos, pos - startPos);
|
|
// is word a C++ symbol or an English word?
|
|
if ((numInternalUppercase >= 1 && numLowercase >= 2)
|
|
|| numStrangeSymbols >= 1) {
|
|
append(Atom::AutoLink, word);
|
|
}
|
|
else {
|
|
appendWord(word);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
leaveValueList();
|
|
|
|
// for compatibility
|
|
if (openedCommands.top() == CMD_LEGALESE) {
|
|
append(Atom::LegaleseRight);
|
|
openedCommands.pop();
|
|
}
|
|
|
|
if (openedCommands.top() != CMD_OMIT) {
|
|
location().warning(tr("Missing '\\%1'").arg(endCmdName(openedCommands.top())));
|
|
}
|
|
else if (preprocessorSkipping.count() > 0) {
|
|
location().warning(tr("Missing '\\%1'").arg(cmdName(CMD_ENDIF)));
|
|
}
|
|
|
|
if (currentSection > Doc::NoSection) {
|
|
append(Atom::SectionRight, QString::number(currentSection));
|
|
currentSection = Doc::NoSection;
|
|
}
|
|
|
|
if (priv->extra && priv->extra->granularity < priv->extra->section)
|
|
priv->extra->granularity = priv->extra->section;
|
|
priv->text.stripFirstAtom();
|
|
}
|
|
|
|
Location &DocParser::location()
|
|
{
|
|
while (!openedInputs.isEmpty() && openedInputs.top() <= pos) {
|
|
cachedLoc.pop();
|
|
cachedPos = openedInputs.pop();
|
|
}
|
|
while (cachedPos < pos)
|
|
cachedLoc.advance(in.at(cachedPos++));
|
|
return cachedLoc;
|
|
}
|
|
|
|
QString DocParser::detailsUnknownCommand(const QSet<QString> &metaCommandSet,
|
|
const QString &str)
|
|
{
|
|
QSet<QString> commandSet = metaCommandSet;
|
|
int i = 0;
|
|
while (cmds[i].english != 0) {
|
|
commandSet.insert(*cmds[i].alias);
|
|
i++;
|
|
}
|
|
|
|
if (aliasMap()->contains(str))
|
|
return tr("The command '\\%1' was renamed '\\%2' by the configuration"
|
|
" file. Use the new name.")
|
|
.arg(str).arg((*aliasMap())[str]);
|
|
|
|
QString best = nearestName(str, commandSet);
|
|
if (best.isEmpty())
|
|
return QString();
|
|
return tr("Maybe you meant '\\%1'?").arg(best);
|
|
}
|
|
|
|
void DocParser::checkExpiry(const QString& date)
|
|
{
|
|
QRegExp ymd("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})))");
|
|
|
|
if (ymd.exactMatch(date)) {
|
|
int y = ymd.cap(1).toInt();
|
|
int m = ymd.cap(2).toInt();
|
|
int d = ymd.cap(3).toInt();
|
|
|
|
if (m == 0)
|
|
m = 1;
|
|
if (d == 0)
|
|
d = 1;
|
|
QDate expiryDate(y, m, d);
|
|
if (expiryDate.isValid()) {
|
|
int days = expiryDate.daysTo(QDate::currentDate());
|
|
if (days == 0) {
|
|
location().warning(tr("Documentation expires today"));
|
|
}
|
|
else if (days == 1) {
|
|
location().warning(tr("Documentation expired yesterday"));
|
|
}
|
|
else if (days >= 2) {
|
|
location().warning(tr("Documentation expired %1 days ago")
|
|
.arg(days));
|
|
}
|
|
}
|
|
else {
|
|
location().warning(tr("Date '%1' invalid").arg(date));
|
|
}
|
|
}
|
|
else {
|
|
location().warning(tr("Date '%1' not in YYYY-MM-DD format")
|
|
.arg(date));
|
|
}
|
|
}
|
|
|
|
void DocParser::insertBaseName(const QString &baseName)
|
|
{
|
|
priv->constructExtra();
|
|
if (currentSection == priv->extra->section) {
|
|
priv->extra->baseName = baseName;
|
|
}
|
|
else {
|
|
Atom *atom = priv->text.firstAtom();
|
|
Atom *sectionLeft = 0;
|
|
|
|
int delta = currentSection - priv->extra->section;
|
|
|
|
while (atom != 0) {
|
|
if (atom->type() == Atom::SectionLeft &&
|
|
atom->string().toInt() == delta)
|
|
sectionLeft = atom;
|
|
atom = atom->next();
|
|
}
|
|
if (sectionLeft != 0)
|
|
(void) new Atom(sectionLeft, Atom::BaseName, baseName);
|
|
}
|
|
}
|
|
|
|
void DocParser::insertTarget(const QString &target, bool keyword)
|
|
{
|
|
if (targetMap.contains(target)) {
|
|
location().warning(tr("Duplicate target name '%1'").arg(target));
|
|
targetMap[target].warning(tr("(The previous occurrence is here)"));
|
|
}
|
|
else {
|
|
targetMap.insert(target, location());
|
|
append(Atom::Target, target);
|
|
priv->constructExtra();
|
|
if (keyword)
|
|
priv->extra->keywords.append(priv->text.lastAtom());
|
|
else
|
|
priv->extra->targets.append(priv->text.lastAtom());
|
|
}
|
|
}
|
|
|
|
void DocParser::include(const QString& fileName, const QString& identifier)
|
|
{
|
|
if (location().depth() > 16)
|
|
location().fatal(tr("Too many nested '\\%1's")
|
|
.arg(cmdName(CMD_INCLUDE)));
|
|
|
|
QString userFriendlyFilePath;
|
|
// ### use current directory?
|
|
QString filePath = Config::findFile(location(),
|
|
sourceFiles,
|
|
sourceDirs,
|
|
fileName,
|
|
userFriendlyFilePath);
|
|
if (filePath.isEmpty()) {
|
|
location().warning(tr("Cannot find qdoc include file '%1'").arg(fileName));
|
|
}
|
|
else {
|
|
QFile inFile(filePath);
|
|
if (!inFile.open(QFile::ReadOnly)) {
|
|
location().warning(tr("Cannot open qdoc include file '%1'")
|
|
.arg(userFriendlyFilePath));
|
|
}
|
|
else {
|
|
location().push(userFriendlyFilePath);
|
|
|
|
QTextStream inStream(&inFile);
|
|
QString includedStuff = inStream.readAll();
|
|
inFile.close();
|
|
|
|
if (identifier.isEmpty()) {
|
|
in.insert(pos, includedStuff);
|
|
len = in.length();
|
|
openedInputs.push(pos + includedStuff.length());
|
|
}
|
|
else {
|
|
QStringList lineBuffer = includedStuff.split(QLatin1Char('\n'));
|
|
int i = 0;
|
|
int startLine = -1;
|
|
while (i < lineBuffer.size()) {
|
|
if (lineBuffer[i].startsWith("//!")) {
|
|
if (lineBuffer[i].contains(identifier)) {
|
|
startLine = i+1;
|
|
break;
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
if (startLine < 0) {
|
|
location().warning(tr("Cannot find '%1' in '%2'")
|
|
.arg(identifier)
|
|
.arg(userFriendlyFilePath));
|
|
return;
|
|
|
|
}
|
|
QString result;
|
|
i = startLine;
|
|
do {
|
|
if (lineBuffer[i].startsWith("//!")) {
|
|
if (i<lineBuffer.size()) {
|
|
if (lineBuffer[i].contains(identifier)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
result += lineBuffer[i] + "\n";
|
|
++i;
|
|
} while (i < lineBuffer.size());
|
|
if (result.isEmpty()) {
|
|
location().warning(tr("Empty qdoc snippet '%1' in '%2'")
|
|
.arg(identifier)
|
|
.arg(userFriendlyFilePath));
|
|
}
|
|
else {
|
|
in.insert(pos, result);
|
|
len = in.length();
|
|
openedInputs.push(pos + result.length());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DocParser::startFormat(const QString& format, int cmd)
|
|
{
|
|
enterPara();
|
|
|
|
QMap<int, QString>::ConstIterator f = pendingFormats.begin();
|
|
while (f != pendingFormats.end()) {
|
|
if (*f == format) {
|
|
location().warning(tr("Cannot nest '\\%1' commands")
|
|
.arg(cmdName(cmd)));
|
|
return;
|
|
}
|
|
++f;
|
|
}
|
|
|
|
append(Atom::FormattingLeft, format);
|
|
|
|
if (isLeftBraceAhead()) {
|
|
skipSpacesOrOneEndl();
|
|
pendingFormats.insert(braceDepth, format);
|
|
++braceDepth;
|
|
++pos;
|
|
}
|
|
else {
|
|
append(Atom::String, getArgument());
|
|
append(Atom::FormattingRight, format);
|
|
if (format == ATOM_FORMATTING_INDEX && indexStartedPara) {
|
|
skipAllSpaces();
|
|
indexStartedPara = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DocParser::openCommand(int cmd)
|
|
{
|
|
int outer = openedCommands.top();
|
|
bool ok = true;
|
|
|
|
if (cmd != CMD_LINK) {
|
|
if (outer == CMD_LIST) {
|
|
ok = (cmd == CMD_FOOTNOTE || cmd == CMD_LIST);
|
|
}
|
|
else if (outer == CMD_ABSTRACT) {
|
|
ok = (cmd == CMD_LIST ||
|
|
cmd == CMD_QUOTATION ||
|
|
cmd == CMD_TABLE);
|
|
}
|
|
else if (outer == CMD_SIDEBAR) {
|
|
ok = (cmd == CMD_LIST ||
|
|
cmd == CMD_QUOTATION ||
|
|
cmd == CMD_SIDEBAR);
|
|
}
|
|
else if (outer == CMD_QUOTATION) {
|
|
ok = (cmd == CMD_LIST);
|
|
}
|
|
else if (outer == CMD_TABLE) {
|
|
ok = (cmd == CMD_LIST ||
|
|
cmd == CMD_FOOTNOTE ||
|
|
cmd == CMD_QUOTATION);
|
|
}
|
|
else if (outer == CMD_FOOTNOTE || outer == CMD_LINK) {
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
openedCommands.push(cmd);
|
|
}
|
|
else {
|
|
location().warning(tr("Cannot use '\\%1' within '\\%2'")
|
|
.arg(cmdName(cmd)).arg(cmdName(outer)));
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool DocParser::closeCommand(int endCmd)
|
|
{
|
|
if (endCmdFor(openedCommands.top()) == endCmd && openedCommands.size() > 1) {
|
|
openedCommands.pop();
|
|
return true;
|
|
}
|
|
else {
|
|
bool contains = false;
|
|
QStack<int> opened2 = openedCommands;
|
|
while (opened2.size() > 1) {
|
|
if (endCmdFor(opened2.top()) == endCmd) {
|
|
contains = true;
|
|
break;
|
|
}
|
|
opened2.pop();
|
|
}
|
|
|
|
if (contains) {
|
|
while (endCmdFor(openedCommands.top()) != endCmd && openedCommands.size() > 1) {
|
|
location().warning(tr("Missing '\\%1' before '\\%2'")
|
|
.arg(endCmdName(openedCommands.top()))
|
|
.arg(cmdName(endCmd)));
|
|
openedCommands.pop();
|
|
}
|
|
}
|
|
else {
|
|
location().warning(tr("Unexpected '\\%1'").arg(cmdName(endCmd)));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DocParser::startSection(Doc::Sections unit, int cmd)
|
|
{
|
|
leaveValueList();
|
|
|
|
if (currentSection == Doc::NoSection) {
|
|
currentSection = (Doc::Sections) (unit);
|
|
priv->constructExtra();
|
|
priv->extra->section = currentSection;
|
|
}
|
|
else
|
|
endSection(unit,cmd);
|
|
|
|
append(Atom::SectionLeft, QString::number(unit));
|
|
priv->constructExtra();
|
|
priv->extra->tableOfContents.append(priv->text.lastAtom());
|
|
priv->extra->tableOfContentsLevels.append(unit);
|
|
enterPara(Atom::SectionHeadingLeft,
|
|
Atom::SectionHeadingRight,
|
|
QString::number(unit));
|
|
currentSection = unit;
|
|
|
|
}
|
|
|
|
void DocParser::endSection(int , int) // (int unit, int endCmd)
|
|
{
|
|
leavePara();
|
|
append(Atom::SectionRight, QString::number(currentSection));
|
|
currentSection = (Doc::NoSection);
|
|
}
|
|
|
|
void DocParser::parseAlso()
|
|
{
|
|
leavePara();
|
|
skipSpacesOnLine();
|
|
while (pos < len && in[pos] != '\n') {
|
|
QString target;
|
|
QString str;
|
|
|
|
if (in[pos] == '{') {
|
|
target = getArgument();
|
|
skipSpacesOnLine();
|
|
if (in[pos] == '{') {
|
|
str = getArgument();
|
|
|
|
// hack for C++ to support links like \l{QString::}{count()}
|
|
if (target.endsWith("::"))
|
|
target += str;
|
|
}
|
|
else {
|
|
str = target;
|
|
}
|
|
#ifdef QDOC2_COMPAT
|
|
}
|
|
else if (in[pos] == '\\' && in.mid(pos, 5) == "\\link") {
|
|
pos += 6;
|
|
target = getArgument();
|
|
int endPos = in.indexOf("\\endlink", pos);
|
|
if (endPos != -1) {
|
|
str = in.mid(pos, endPos - pos).trimmed();
|
|
pos = endPos + 8;
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
target = getArgument();
|
|
str = cleanLink(target);
|
|
}
|
|
|
|
Text also;
|
|
also << Atom(Atom::Link, target)
|
|
<< Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK)
|
|
<< str
|
|
<< Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK);
|
|
priv->addAlso(also);
|
|
|
|
skipSpacesOnLine();
|
|
if (pos < len && in[pos] == ',') {
|
|
pos++;
|
|
skipSpacesOrOneEndl();
|
|
}
|
|
else if (in[pos] != '\n') {
|
|
location().warning(tr("Missing comma in '\\%1'").arg(cmdName(CMD_SA)));
|
|
}
|
|
}
|
|
}
|
|
|
|
//static bool debug = false;
|
|
#if 0
|
|
if (type == Atom::DivLeft)
|
|
debug = true;
|
|
if (debug)
|
|
qDebug() << type << string;
|
|
if (type == Atom::DivRight)
|
|
debug = false;
|
|
#endif
|
|
|
|
void DocParser::append(Atom::Type type, const QString &string)
|
|
{
|
|
Atom::Type lastType = priv->text.lastAtom()->type();
|
|
if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
|
|
priv->text.lastAtom()->chopString();
|
|
priv->text << Atom(type, string);
|
|
}
|
|
|
|
void DocParser::append(Atom::Type type, const QString& p1, const QString& p2)
|
|
{
|
|
Atom::Type lastType = priv->text.lastAtom()->type();
|
|
if ((lastType == Atom::Code) && priv->text.lastAtom()->string().endsWith(QLatin1String("\n\n")))
|
|
priv->text.lastAtom()->chopString();
|
|
priv->text << Atom(type, p1, p2);
|
|
}
|
|
|
|
void DocParser::appendChar(QChar ch)
|
|
{
|
|
if (priv->text.lastAtom()->type() != Atom::String)
|
|
append(Atom::String);
|
|
Atom *atom = priv->text.lastAtom();
|
|
if (ch == QLatin1Char(' ')) {
|
|
if (!atom->string().endsWith(QLatin1Char(' ')))
|
|
atom->appendChar(QLatin1Char(' '));
|
|
}
|
|
else
|
|
atom->appendChar(ch);
|
|
}
|
|
|
|
void DocParser::appendWord(const QString &word)
|
|
{
|
|
if (priv->text.lastAtom()->type() != Atom::String) {
|
|
append(Atom::String, word);
|
|
}
|
|
else
|
|
priv->text.lastAtom()->appendString(word);
|
|
}
|
|
|
|
void DocParser::appendToCode(const QString& markedCode)
|
|
{
|
|
Atom::Type lastType = priv->text.lastAtom()->type();
|
|
#ifdef QDOC_QML
|
|
if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
|
|
append(Atom::Qml);
|
|
#else
|
|
if (lastType != Atom::Code)
|
|
append(Atom::Code);
|
|
#endif
|
|
priv->text.lastAtom()->appendString(markedCode);
|
|
}
|
|
|
|
void DocParser::appendToCode(const QString &markedCode, Atom::Type defaultType)
|
|
{
|
|
Atom::Type lastType = priv->text.lastAtom()->type();
|
|
if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript)
|
|
append(defaultType, markedCode);
|
|
else
|
|
priv->text.lastAtom()->appendString(markedCode);
|
|
}
|
|
|
|
void DocParser::startNewPara()
|
|
{
|
|
leavePara();
|
|
enterPara();
|
|
}
|
|
|
|
void DocParser::enterPara(Atom::Type leftType,
|
|
Atom::Type rightType,
|
|
const QString& string)
|
|
{
|
|
if (paraState == OutsideParagraph) {
|
|
|
|
if ((priv->text.lastAtom()->type() != Atom::ListItemLeft) &&
|
|
(priv->text.lastAtom()->type() != Atom::DivLeft)) {
|
|
leaveValueList();
|
|
}
|
|
|
|
append(leftType, string);
|
|
indexStartedPara = false;
|
|
pendingParaLeftType = leftType;
|
|
pendingParaRightType = rightType;
|
|
pendingParaString = string;
|
|
if (leftType == Atom::SectionHeadingLeft) {
|
|
paraState = InSingleLineParagraph;
|
|
}
|
|
else {
|
|
paraState = InMultiLineParagraph;
|
|
}
|
|
skipSpacesOrOneEndl();
|
|
}
|
|
}
|
|
|
|
void DocParser::leavePara()
|
|
{
|
|
if (paraState != OutsideParagraph) {
|
|
if (!pendingFormats.isEmpty()) {
|
|
location().warning(tr("Missing '}'"));
|
|
pendingFormats.clear();
|
|
}
|
|
|
|
if (priv->text.lastAtom()->type() == pendingParaLeftType) {
|
|
priv->text.stripLastAtom();
|
|
}
|
|
else {
|
|
if (priv->text.lastAtom()->type() == Atom::String &&
|
|
priv->text.lastAtom()->string().endsWith(" ")) {
|
|
priv->text.lastAtom()->chopString();
|
|
}
|
|
append(pendingParaRightType, pendingParaString);
|
|
}
|
|
paraState = OutsideParagraph;
|
|
indexStartedPara = false;
|
|
pendingParaRightType = Atom::Nop;
|
|
pendingParaString = "";
|
|
}
|
|
}
|
|
|
|
void DocParser::leaveValue()
|
|
{
|
|
leavePara();
|
|
if (openedLists.isEmpty()) {
|
|
openedLists.push(OpenedList(OpenedList::Value));
|
|
append(Atom::ListLeft, ATOM_LIST_VALUE);
|
|
}
|
|
else {
|
|
if (priv->text.lastAtom()->type() == Atom::Nop)
|
|
priv->text.stripLastAtom();
|
|
append(Atom::ListItemRight, ATOM_LIST_VALUE);
|
|
}
|
|
}
|
|
|
|
void DocParser::leaveValueList()
|
|
{
|
|
leavePara();
|
|
if (!openedLists.isEmpty() &&
|
|
(openedLists.top().style() == OpenedList::Value)) {
|
|
if (priv->text.lastAtom()->type() == Atom::Nop)
|
|
priv->text.stripLastAtom();
|
|
append(Atom::ListItemRight, ATOM_LIST_VALUE);
|
|
append(Atom::ListRight, ATOM_LIST_VALUE);
|
|
openedLists.pop();
|
|
}
|
|
}
|
|
|
|
void DocParser::leaveTableRow()
|
|
{
|
|
if (inTableItem) {
|
|
leavePara();
|
|
append(Atom::TableItemRight);
|
|
inTableItem = false;
|
|
}
|
|
if (inTableHeader) {
|
|
append(Atom::TableHeaderRight);
|
|
inTableHeader = false;
|
|
}
|
|
if (inTableRow) {
|
|
append(Atom::TableRowRight);
|
|
inTableRow = false;
|
|
}
|
|
}
|
|
|
|
CodeMarker *DocParser::quoteFromFile()
|
|
{
|
|
return Doc::quoteFromFile(location(), quoter, getArgument());
|
|
}
|
|
|
|
void DocParser::expandMacro(const QString &name,
|
|
const QString &def,
|
|
int numParams)
|
|
{
|
|
if (numParams == 0) {
|
|
append(Atom::RawString, def);
|
|
}
|
|
else {
|
|
QStringList args;
|
|
QString rawString;
|
|
|
|
for (int i = 0; i < numParams; i++) {
|
|
if (numParams == 1 || isLeftBraceAhead()) {
|
|
args << getArgument(true);
|
|
}
|
|
else {
|
|
location().warning(tr("Macro '\\%1' invoked with too few"
|
|
" arguments (expected %2, got %3)")
|
|
.arg(name).arg(numParams).arg(i));
|
|
break;
|
|
}
|
|
}
|
|
|
|
int j = 0;
|
|
while (j < def.size()) {
|
|
int paramNo;
|
|
if (((paramNo = def[j].unicode()) >= 1) &&
|
|
(paramNo <= numParams)) {
|
|
if (!rawString.isEmpty()) {
|
|
append(Atom::RawString, rawString);
|
|
rawString = "";
|
|
}
|
|
append(Atom::String, args[paramNo - 1]);
|
|
j += 1;
|
|
}
|
|
else {
|
|
rawString += def[j++];
|
|
}
|
|
}
|
|
if (!rawString.isEmpty())
|
|
append(Atom::RawString, rawString);
|
|
}
|
|
}
|
|
|
|
QString DocParser::expandMacroToString(const QString &name, const QString &def, int numParams)
|
|
{
|
|
if (numParams == 0) {
|
|
return def;
|
|
}
|
|
else {
|
|
QStringList args;
|
|
QString rawString;
|
|
|
|
for (int i = 0; i < numParams; i++) {
|
|
if (numParams == 1 || isLeftBraceAhead()) {
|
|
args << getArgument(true);
|
|
}
|
|
else {
|
|
location().warning(tr("Macro '\\%1' invoked with too few"
|
|
" arguments (expected %2, got %3)")
|
|
.arg(name).arg(numParams).arg(i));
|
|
break;
|
|
}
|
|
}
|
|
|
|
int j = 0;
|
|
while (j < def.size()) {
|
|
int paramNo;
|
|
if (((paramNo = def[j].unicode()) >= 1) &&
|
|
(paramNo <= numParams)) {
|
|
rawString += args[paramNo - 1];
|
|
j += 1;
|
|
}
|
|
else {
|
|
rawString += def[j++];
|
|
}
|
|
}
|
|
return rawString;
|
|
}
|
|
}
|
|
|
|
Doc::Sections DocParser::getSectioningUnit()
|
|
{
|
|
QString name = getOptionalArgument();
|
|
|
|
if (name == "part") {
|
|
return Doc::Part;
|
|
}
|
|
else if (name == "chapter") {
|
|
return Doc::Chapter;
|
|
}
|
|
else if (name == "section1") {
|
|
return Doc::Section1;
|
|
}
|
|
else if (name == "section2") {
|
|
return Doc::Section2;
|
|
}
|
|
else if (name == "section3") {
|
|
return Doc::Section3;
|
|
}
|
|
else if (name == "section4") {
|
|
return Doc::Section4;
|
|
}
|
|
else if (name.isEmpty()) {
|
|
return Doc::NoSection;
|
|
}
|
|
else {
|
|
location().warning(tr("Invalid section '%1'").arg(name));
|
|
return Doc::NoSection;
|
|
}
|
|
}
|
|
|
|
QString DocParser::getArgument(bool verbatim)
|
|
{
|
|
QString arg;
|
|
int delimDepth = 0;
|
|
|
|
skipSpacesOrOneEndl();
|
|
|
|
int startPos = pos;
|
|
|
|
/*
|
|
Typically, an argument ends at the next white-space. However,
|
|
braces can be used to group words:
|
|
|
|
{a few words}
|
|
|
|
Also, opening and closing parentheses have to match. Thus,
|
|
|
|
printf("%d\n", x)
|
|
|
|
is an argument too, although it contains spaces. Finally,
|
|
trailing punctuation is not included in an argument, nor is 's.
|
|
*/
|
|
if (pos < (int) in.length() && in[pos] == '{') {
|
|
pos++;
|
|
while (pos < (int) in.length() && delimDepth >= 0) {
|
|
switch (in[pos].unicode()) {
|
|
case '{':
|
|
delimDepth++;
|
|
arg += "{";
|
|
pos++;
|
|
break;
|
|
case '}':
|
|
delimDepth--;
|
|
if (delimDepth >= 0)
|
|
arg += "}";
|
|
pos++;
|
|
break;
|
|
case '\\':
|
|
if (verbatim) {
|
|
arg += in[pos];
|
|
pos++;
|
|
}
|
|
else {
|
|
pos++;
|
|
if (pos < (int) in.length()) {
|
|
if (in[pos].isLetterOrNumber())
|
|
break;
|
|
arg += in[pos];
|
|
if (in[pos].isSpace()) {
|
|
skipAllSpaces();
|
|
}
|
|
else {
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
arg += in[pos];
|
|
pos++;
|
|
}
|
|
}
|
|
if (delimDepth > 0)
|
|
location().warning(tr("Missing '}'"));
|
|
}
|
|
else {
|
|
while ((pos < in.length()) &&
|
|
((delimDepth > 0) || ((delimDepth == 0) && !in[pos].isSpace()))) {
|
|
switch (in[pos].unicode()) {
|
|
case '(':
|
|
case '[':
|
|
case '{':
|
|
delimDepth++;
|
|
arg += in[pos];
|
|
pos++;
|
|
break;
|
|
case ')':
|
|
case ']':
|
|
case '}':
|
|
delimDepth--;
|
|
if (pos == startPos || delimDepth >= 0) {
|
|
arg += in[pos];
|
|
pos++;
|
|
}
|
|
break;
|
|
case '\\':
|
|
if (verbatim) {
|
|
arg += in[pos];
|
|
pos++;
|
|
}
|
|
else {
|
|
pos++;
|
|
if (pos < (int) in.length()) {
|
|
if (in[pos].isLetterOrNumber())
|
|
break;
|
|
arg += in[pos];
|
|
if (in[pos].isSpace()) {
|
|
skipAllSpaces();
|
|
}
|
|
else {
|
|
pos++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
arg += in[pos];
|
|
pos++;
|
|
}
|
|
}
|
|
if ((arg.length() > 1) &&
|
|
(QString(".,:;!?").indexOf(in[pos - 1]) != -1) &&
|
|
!arg.endsWith("...")) {
|
|
arg.truncate(arg.length() - 1);
|
|
pos--;
|
|
}
|
|
if (arg.length() > 2 && in.mid(pos - 2, 2) == "'s") {
|
|
arg.truncate(arg.length() - 2);
|
|
pos -= 2;
|
|
}
|
|
}
|
|
return arg.simplified();
|
|
}
|
|
|
|
QString DocParser::getOptionalArgument()
|
|
{
|
|
skipSpacesOrOneEndl();
|
|
if (pos + 1 < (int) in.length() && in[pos] == '\\' &&
|
|
in[pos + 1].isLetterOrNumber()) {
|
|
return "";
|
|
}
|
|
else {
|
|
return getArgument();
|
|
}
|
|
}
|
|
|
|
QString DocParser::getRestOfLine()
|
|
{
|
|
QString t;
|
|
|
|
skipSpacesOnLine();
|
|
|
|
bool trailingSlash = false;
|
|
|
|
do {
|
|
int begin = pos;
|
|
|
|
while (pos < in.size() && in[pos] != '\n') {
|
|
if (in[pos] == '\\' && !trailingSlash) {
|
|
trailingSlash = true;
|
|
++pos;
|
|
while ((pos < in.size()) &&
|
|
in[pos].isSpace() &&
|
|
(in[pos] != '\n'))
|
|
++pos;
|
|
}
|
|
else {
|
|
trailingSlash = false;
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
if (!t.isEmpty())
|
|
t += " ";
|
|
t += in.mid(begin, pos - begin).simplified();
|
|
|
|
if (trailingSlash) {
|
|
t.chop(1);
|
|
t = t.simplified();
|
|
}
|
|
if (pos < in.size())
|
|
++pos;
|
|
} while (pos < in.size() && trailingSlash);
|
|
|
|
return t;
|
|
}
|
|
|
|
/*!
|
|
The metacommand argument is normally the remaining text to
|
|
the right of the metacommand itself. The extra blanks are
|
|
stripped and the argument string is returned.
|
|
*/
|
|
QString DocParser::getMetaCommandArgument(const QString &cmdStr)
|
|
{
|
|
skipSpacesOnLine();
|
|
|
|
int begin = pos;
|
|
int parenDepth = 0;
|
|
|
|
while (pos < in.size() && (in[pos] != '\n' || parenDepth > 0)) {
|
|
if (in.at(pos) == '(')
|
|
++parenDepth;
|
|
else if (in.at(pos) == ')')
|
|
--parenDepth;
|
|
|
|
++pos;
|
|
}
|
|
if (pos == in.size() && parenDepth > 0) {
|
|
pos = begin;
|
|
location().warning(tr("Unbalanced parentheses in '%1'").arg(cmdStr));
|
|
}
|
|
|
|
QString t = in.mid(begin, pos - begin).simplified();
|
|
skipSpacesOnLine();
|
|
return t;
|
|
}
|
|
|
|
QString DocParser::getUntilEnd(int cmd)
|
|
{
|
|
int endCmd = endCmdFor(cmd);
|
|
QRegExp rx("\\\\" + cmdName(endCmd) + "\\b");
|
|
QString t;
|
|
int end = rx.indexIn(in, pos);
|
|
|
|
if (end == -1) {
|
|
location().warning(tr("Missing '\\%1'").arg(cmdName(endCmd)));
|
|
pos = in.length();
|
|
}
|
|
else {
|
|
t = in.mid(pos, end - pos);
|
|
pos = end + rx.matchedLength();
|
|
}
|
|
return t;
|
|
}
|
|
|
|
QString DocParser::getCode(int cmd, CodeMarker *marker)
|
|
{
|
|
QString code = untabifyEtc(getUntilEnd(cmd));
|
|
int indent = indentLevel(code);
|
|
if (indent < minIndent)
|
|
minIndent = indent;
|
|
code = unindent(minIndent, code);
|
|
if (!marker)
|
|
marker = CodeMarker::markerForCode(code);
|
|
return marker->markedUpCode(code, 0, location());
|
|
}
|
|
|
|
/*!
|
|
Was used only for generating doxygen output.
|
|
*/
|
|
QString DocParser::getUnmarkedCode(int cmd)
|
|
{
|
|
QString code = getUntilEnd(cmd);
|
|
return code;
|
|
}
|
|
|
|
bool DocParser::isBlankLine()
|
|
{
|
|
int i = pos;
|
|
|
|
while (i < len && in[i].isSpace()) {
|
|
if (in[i] == '\n')
|
|
return true;
|
|
i++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DocParser::isLeftBraceAhead()
|
|
{
|
|
int numEndl = 0;
|
|
int i = pos;
|
|
|
|
while (i < len && in[i].isSpace() && numEndl < 2) {
|
|
// ### bug with '\\'
|
|
if (in[i] == '\n')
|
|
numEndl++;
|
|
i++;
|
|
}
|
|
return numEndl < 2 && i < len && in[i] == '{';
|
|
}
|
|
|
|
/*!
|
|
Skips to the next non-space character or EOL.
|
|
*/
|
|
void DocParser::skipSpacesOnLine()
|
|
{
|
|
while ((pos < in.length()) &&
|
|
in[pos].isSpace() &&
|
|
(in[pos].unicode() != '\n'))
|
|
++pos;
|
|
}
|
|
|
|
/*!
|
|
Skips spaces and on EOL.
|
|
*/
|
|
void DocParser::skipSpacesOrOneEndl()
|
|
{
|
|
int firstEndl = -1;
|
|
while (pos < (int) in.length() && in[pos].isSpace()) {
|
|
QChar ch = in[pos];
|
|
if (ch == '\n') {
|
|
if (firstEndl == -1) {
|
|
firstEndl = pos;
|
|
}
|
|
else {
|
|
pos = firstEndl;
|
|
break;
|
|
}
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
void DocParser::skipAllSpaces()
|
|
{
|
|
while (pos < len && in[pos].isSpace())
|
|
pos++;
|
|
}
|
|
|
|
void DocParser::skipToNextPreprocessorCommand()
|
|
{
|
|
QRegExp rx("\\\\(?:" + cmdName(CMD_IF) + "|" +
|
|
cmdName(CMD_ELSE) + "|" +
|
|
cmdName(CMD_ENDIF) + ")\\b");
|
|
int end = rx.indexIn(in, pos + 1); // ### + 1 necessary?
|
|
|
|
if (end == -1)
|
|
pos = in.length();
|
|
else
|
|
pos = end;
|
|
}
|
|
|
|
int DocParser::endCmdFor(int cmd)
|
|
{
|
|
switch (cmd) {
|
|
case CMD_ABSTRACT:
|
|
return CMD_ENDABSTRACT;
|
|
case CMD_BADCODE:
|
|
return CMD_ENDCODE;
|
|
case CMD_CHAPTER:
|
|
return CMD_ENDCHAPTER;
|
|
case CMD_CODE:
|
|
return CMD_ENDCODE;
|
|
case CMD_DIV:
|
|
return CMD_ENDDIV;
|
|
case CMD_QML:
|
|
return CMD_ENDQML;
|
|
case CMD_QMLTEXT:
|
|
return CMD_ENDQMLTEXT;
|
|
case CMD_JS:
|
|
return CMD_ENDJS;
|
|
case CMD_FOOTNOTE:
|
|
return CMD_ENDFOOTNOTE;
|
|
case CMD_LEGALESE:
|
|
return CMD_ENDLEGALESE;
|
|
case CMD_LINK:
|
|
return CMD_ENDLINK;
|
|
case CMD_LIST:
|
|
return CMD_ENDLIST;
|
|
case CMD_NEWCODE:
|
|
return CMD_ENDCODE;
|
|
case CMD_OLDCODE:
|
|
return CMD_NEWCODE;
|
|
case CMD_OMIT:
|
|
return CMD_ENDOMIT;
|
|
case CMD_PART:
|
|
return CMD_ENDPART;
|
|
case CMD_QUOTATION:
|
|
return CMD_ENDQUOTATION;
|
|
case CMD_RAW:
|
|
return CMD_ENDRAW;
|
|
case CMD_SECTION1:
|
|
return CMD_ENDSECTION1;
|
|
case CMD_SECTION2:
|
|
return CMD_ENDSECTION2;
|
|
case CMD_SECTION3:
|
|
return CMD_ENDSECTION3;
|
|
case CMD_SECTION4:
|
|
return CMD_ENDSECTION4;
|
|
case CMD_SIDEBAR:
|
|
return CMD_ENDSIDEBAR;
|
|
case CMD_TABLE:
|
|
return CMD_ENDTABLE;
|
|
default:
|
|
return cmd;
|
|
}
|
|
}
|
|
|
|
QString DocParser::cmdName(int cmd)
|
|
{
|
|
return *cmds[cmd].alias;
|
|
}
|
|
|
|
QString DocParser::endCmdName(int cmd)
|
|
{
|
|
return cmdName(endCmdFor(cmd));
|
|
}
|
|
|
|
QString DocParser::untabifyEtc(const QString& str)
|
|
{
|
|
QString result;
|
|
result.reserve(str.length());
|
|
int column = 0;
|
|
|
|
for (int i = 0; i < str.length(); i++) {
|
|
const QChar c = str.at(i);
|
|
if (c == QLatin1Char('\r'))
|
|
continue;
|
|
if (c == QLatin1Char('\t')) {
|
|
result += " " + (column % tabSize);
|
|
column = ((column / tabSize) + 1) * tabSize;
|
|
continue;
|
|
}
|
|
if (c == QLatin1Char('\n')) {
|
|
while (result.endsWith(QLatin1Char(' ')))
|
|
result.chop(1);
|
|
result += c;
|
|
column = 0;
|
|
continue;
|
|
}
|
|
result += c;
|
|
column++;
|
|
}
|
|
|
|
while (result.endsWith("\n\n"))
|
|
result.truncate(result.length() - 1);
|
|
while (result.startsWith("\n"))
|
|
result = result.mid(1);
|
|
|
|
return result;
|
|
}
|
|
|
|
int DocParser::indentLevel(const QString& str)
|
|
{
|
|
int minIndent = INT_MAX;
|
|
int column = 0;
|
|
|
|
for (int i = 0; i < (int) str.length(); i++) {
|
|
if (str[i] == '\n') {
|
|
column = 0;
|
|
}
|
|
else {
|
|
if (str[i] != ' ' && column < minIndent)
|
|
minIndent = column;
|
|
column++;
|
|
}
|
|
}
|
|
return minIndent;
|
|
}
|
|
|
|
QString DocParser::unindent(int level, const QString& str)
|
|
{
|
|
if (level == 0)
|
|
return str;
|
|
|
|
QString t;
|
|
int column = 0;
|
|
|
|
for (int i = 0; i < (int) str.length(); i++) {
|
|
if (str[i] == QLatin1Char('\n')) {
|
|
t += '\n';
|
|
column = 0;
|
|
}
|
|
else {
|
|
if (column >= level)
|
|
t += str[i];
|
|
column++;
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
QString DocParser::slashed(const QString& str)
|
|
{
|
|
QString result = str;
|
|
result.replace("/", "\\/");
|
|
return "/" + result + "/";
|
|
}
|
|
|
|
#define COMMAND_BRIEF Doc::alias("brief")
|
|
#define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
|
|
|
|
Doc::Doc(const Location& start_loc,
|
|
const Location& end_loc,
|
|
const QString& source,
|
|
const QSet<QString>& metaCommandSet)
|
|
{
|
|
priv = new DocPrivate(start_loc,end_loc,source);
|
|
DocParser parser;
|
|
parser.parse(source,priv,metaCommandSet);
|
|
}
|
|
|
|
Doc::Doc(const Doc& doc)
|
|
: priv(0)
|
|
{
|
|
operator=(doc);
|
|
}
|
|
|
|
Doc::~Doc()
|
|
{
|
|
if (priv && priv->deref())
|
|
delete priv;
|
|
}
|
|
|
|
Doc &Doc::operator=(const Doc& doc)
|
|
{
|
|
if (doc.priv)
|
|
doc.priv->ref();
|
|
if (priv && priv->deref())
|
|
delete priv;
|
|
priv = doc.priv;
|
|
return *this;
|
|
}
|
|
|
|
void Doc::renameParameters(const QStringList &oldNames,
|
|
const QStringList &newNames)
|
|
{
|
|
if (priv && oldNames != newNames) {
|
|
detach();
|
|
|
|
priv->params = newNames.toSet();
|
|
|
|
Atom *atom = priv->text.firstAtom();
|
|
while (atom) {
|
|
if (atom->type() == Atom::FormattingLeft
|
|
&& atom->string() == ATOM_FORMATTING_PARAMETER) {
|
|
atom = atom->next();
|
|
if (!atom)
|
|
return;
|
|
int index = oldNames.indexOf(atom->string());
|
|
if (index != -1 && index < newNames.count())
|
|
atom->setString(newNames.at(index));
|
|
}
|
|
atom = atom->next();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Doc::simplifyEnumDoc()
|
|
{
|
|
if (priv) {
|
|
if (priv->isEnumDocSimplifiable()) {
|
|
detach();
|
|
|
|
Text newText;
|
|
|
|
Atom *atom = priv->text.firstAtom();
|
|
while (atom) {
|
|
if ((atom->type() == Atom::ListLeft) &&
|
|
(atom->string() == ATOM_LIST_VALUE)) {
|
|
while (atom && ((atom->type() != Atom::ListRight) ||
|
|
(atom->string() != ATOM_LIST_VALUE)))
|
|
atom = atom->next();
|
|
if (atom)
|
|
atom = atom->next();
|
|
}
|
|
else {
|
|
newText << *atom;
|
|
atom = atom->next();
|
|
}
|
|
}
|
|
priv->text = newText;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Doc::setBody(const Text &text)
|
|
{
|
|
detach();
|
|
priv->text = text;
|
|
}
|
|
|
|
/*!
|
|
Returns the starting location of a qdoc comment.
|
|
*/
|
|
const Location &Doc::location() const
|
|
{
|
|
static const Location dummy;
|
|
return priv == 0 ? dummy : priv->start_loc;
|
|
}
|
|
|
|
const QString &Doc::source() const
|
|
{
|
|
static QString null;
|
|
return priv == 0 ? null : priv->src;
|
|
}
|
|
|
|
bool Doc::isEmpty() const
|
|
{
|
|
return priv == 0 || priv->src.isEmpty();
|
|
}
|
|
|
|
const Text& Doc::body() const
|
|
{
|
|
static const Text dummy;
|
|
return priv == 0 ? dummy : priv->text;
|
|
}
|
|
|
|
Text Doc::briefText(bool inclusive) const
|
|
{
|
|
return body().subText(Atom::BriefLeft, Atom::BriefRight, 0, inclusive);
|
|
}
|
|
|
|
Text Doc::trimmedBriefText(const QString &className) const
|
|
{
|
|
QString classNameOnly = className;
|
|
if (className.contains("::"))
|
|
classNameOnly = className.split("::").last();
|
|
|
|
Text originalText = briefText();
|
|
Text resultText;
|
|
const Atom *atom = originalText.firstAtom();
|
|
if (atom) {
|
|
QString briefStr;
|
|
QString whats;
|
|
bool standardWording = true;
|
|
|
|
/*
|
|
This code is really ugly. The entire \brief business
|
|
should be rethought.
|
|
*/
|
|
while (atom) {
|
|
if (atom->type() == Atom::AutoLink || atom->type() == Atom::String) {
|
|
briefStr += atom->string();
|
|
}
|
|
atom = atom->next();
|
|
}
|
|
|
|
QStringList w = briefStr.split(" ");
|
|
if (!w.isEmpty() && w.first() == "Returns") {
|
|
}
|
|
else {
|
|
if (!w.isEmpty() && w.first() == "The")
|
|
w.removeFirst();
|
|
else {
|
|
location().warning(
|
|
tr("Nonstandard wording in '\\%1' text for '%2' (expected 'The')")
|
|
.arg(COMMAND_BRIEF).arg(className));
|
|
standardWording = false;
|
|
}
|
|
|
|
if (!w.isEmpty() && (w.first() == className || w.first() == classNameOnly))
|
|
w.removeFirst();
|
|
else {
|
|
location().warning(
|
|
tr("Nonstandard wording in '\\%1' text for '%2' (expected '%3')")
|
|
.arg(COMMAND_BRIEF).arg(className).arg(className));
|
|
standardWording = false;
|
|
}
|
|
|
|
if (!w.isEmpty() && ((w.first() == "class") ||
|
|
(w.first() == "function") ||
|
|
(w.first() == "macro") ||
|
|
(w.first() == "widget") ||
|
|
(w.first() == "namespace") ||
|
|
(w.first() == "header")))
|
|
w.removeFirst();
|
|
else {
|
|
location().warning(
|
|
tr("Nonstandard wording in '\\%1' text for '%2' ("
|
|
"expected 'class', 'function', 'macro', 'widget', "
|
|
"'namespace' or 'header')")
|
|
.arg(COMMAND_BRIEF).arg(className));
|
|
standardWording = false;
|
|
}
|
|
|
|
if (!w.isEmpty() && (w.first() == "is" || w.first() == "provides"))
|
|
w.removeFirst();
|
|
|
|
if (!w.isEmpty() && (w.first() == "a" || w.first() == "an"))
|
|
w.removeFirst();
|
|
}
|
|
|
|
whats = w.join(" ");
|
|
|
|
if (whats.endsWith("."))
|
|
whats.truncate(whats.length() - 1);
|
|
|
|
if (whats.isEmpty()) {
|
|
location().warning(
|
|
tr("Nonstandard wording in '\\%1' text for '%2' (expected more text)")
|
|
.arg(COMMAND_BRIEF).arg(className));
|
|
standardWording = false;
|
|
}
|
|
else
|
|
whats[0] = whats[0].toUpper();
|
|
|
|
// ### move this once \brief is abolished for properties
|
|
if (standardWording)
|
|
resultText << whats;
|
|
}
|
|
return resultText;
|
|
}
|
|
|
|
Text Doc::legaleseText() const
|
|
{
|
|
if (priv == 0 || !priv->hasLegalese)
|
|
return Text();
|
|
else
|
|
return body().subText(Atom::LegaleseLeft, Atom::LegaleseRight);
|
|
}
|
|
|
|
const QString& Doc::baseName() const
|
|
{
|
|
static QString null;
|
|
if (priv == 0 || priv->extra == 0) {
|
|
return null;
|
|
}
|
|
else {
|
|
return priv->extra->baseName;
|
|
}
|
|
}
|
|
|
|
Doc::Sections Doc::granularity() const
|
|
{
|
|
if (priv == 0 || priv->extra == 0) {
|
|
return DocPrivateExtra().granularity;
|
|
}
|
|
else {
|
|
return priv->extra->granularity;
|
|
}
|
|
}
|
|
|
|
const QSet<QString> &Doc::parameterNames() const
|
|
{
|
|
return priv == 0 ? *null_Set_QString() : priv->params;
|
|
}
|
|
|
|
const QStringList &Doc::enumItemNames() const
|
|
{
|
|
return priv == 0 ? *null_QStringList() : priv->enumItemList;
|
|
}
|
|
|
|
const QStringList &Doc::omitEnumItemNames() const
|
|
{
|
|
return priv == 0 ? *null_QStringList() : priv->omitEnumItemList;
|
|
}
|
|
|
|
const QSet<QString> &Doc::metaCommandsUsed() const
|
|
{
|
|
return priv == 0 ? *null_Set_QString() : priv->metacommandsUsed;
|
|
}
|
|
|
|
QStringList Doc::metaCommandArgs(const QString& metacommand) const
|
|
{
|
|
return priv == 0 ? QStringList() : priv->metaCommandMap.value(metacommand);
|
|
}
|
|
|
|
const QList<Text> &Doc::alsoList() const
|
|
{
|
|
return priv == 0 ? *null_QList_Text() : priv->alsoList;
|
|
}
|
|
|
|
bool Doc::hasTableOfContents() const
|
|
{
|
|
return priv && priv->extra && !priv->extra->tableOfContents.isEmpty();
|
|
}
|
|
|
|
bool Doc::hasKeywords() const
|
|
{
|
|
return priv && priv->extra && !priv->extra->keywords.isEmpty();
|
|
}
|
|
|
|
bool Doc::hasTargets() const
|
|
{
|
|
return priv && priv->extra && !priv->extra->targets.isEmpty();
|
|
}
|
|
|
|
const QList<Atom *> &Doc::tableOfContents() const
|
|
{
|
|
priv->constructExtra();
|
|
return priv->extra->tableOfContents;
|
|
}
|
|
|
|
const QList<int> &Doc::tableOfContentsLevels() const
|
|
{
|
|
priv->constructExtra();
|
|
return priv->extra->tableOfContentsLevels;
|
|
}
|
|
|
|
const QList<Atom *> &Doc::keywords() const
|
|
{
|
|
priv->constructExtra();
|
|
return priv->extra->keywords;
|
|
}
|
|
|
|
const QList<Atom *> &Doc::targets() const
|
|
{
|
|
priv->constructExtra();
|
|
return priv->extra->targets;
|
|
}
|
|
|
|
const QStringMultiMap &Doc::metaTagMap() const
|
|
{
|
|
return priv && priv->extra ? priv->extra->metaMap : *null_QStringMultiMap();
|
|
}
|
|
|
|
void Doc::initialize(const Config& config)
|
|
{
|
|
DocParser::tabSize = config.getInt(CONFIG_TABSIZE);
|
|
DocParser::exampleFiles = config.getStringList(CONFIG_EXAMPLES);
|
|
DocParser::exampleDirs = config.getStringList(CONFIG_EXAMPLEDIRS);
|
|
DocParser::sourceFiles = config.getStringList(CONFIG_SOURCES);
|
|
DocParser::sourceDirs = config.getStringList(CONFIG_SOURCEDIRS);
|
|
DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION);
|
|
|
|
QmlClassNode::qmlOnly = config.getBool(CONFIG_QMLONLY);
|
|
|
|
QStringMap reverseAliasMap;
|
|
|
|
QSet<QString> commands = config.subVars(CONFIG_ALIAS);
|
|
QSet<QString>::ConstIterator c = commands.begin();
|
|
while (c != commands.end()) {
|
|
QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c);
|
|
if (reverseAliasMap.contains(alias)) {
|
|
config.lastLocation().warning(tr("Command name '\\%1' cannot stand"
|
|
" for both '\\%2' and '\\%3'")
|
|
.arg(alias)
|
|
.arg(reverseAliasMap[alias])
|
|
.arg(*c));
|
|
}
|
|
else {
|
|
reverseAliasMap.insert(alias, *c);
|
|
}
|
|
aliasMap()->insert(*c, alias);
|
|
++c;
|
|
}
|
|
|
|
int i = 0;
|
|
while (cmds[i].english) {
|
|
cmds[i].alias = new QString(alias(cmds[i].english));
|
|
cmdHash()->insert(*cmds[i].alias, cmds[i].no);
|
|
|
|
if (cmds[i].no != i)
|
|
Location::internalError(tr("command %1 missing").arg(i));
|
|
i++;
|
|
}
|
|
|
|
QSet<QString> macroNames = config.subVars(CONFIG_MACRO);
|
|
QSet<QString>::ConstIterator n = macroNames.begin();
|
|
while (n != macroNames.end()) {
|
|
QString macroDotName = CONFIG_MACRO + Config::dot + *n;
|
|
Macro macro;
|
|
macro.numParams = -1;
|
|
macro.defaultDef = config.getString(macroDotName);
|
|
if (!macro.defaultDef.isEmpty()) {
|
|
macro.defaultDefLocation = config.lastLocation();
|
|
macro.numParams = Config::numParams(macro.defaultDef);
|
|
}
|
|
bool silent = false;
|
|
|
|
QSet<QString> formats = config.subVars(macroDotName);
|
|
QSet<QString>::ConstIterator f = formats.begin();
|
|
while (f != formats.end()) {
|
|
QString def = config.getString(macroDotName + Config::dot + *f);
|
|
if (!def.isEmpty()) {
|
|
macro.otherDefs.insert(*f, def);
|
|
int m = Config::numParams(def);
|
|
if (macro.numParams == -1) {
|
|
macro.numParams = m;
|
|
}
|
|
else if (macro.numParams != m) {
|
|
if (!silent) {
|
|
QString other = tr("default");
|
|
if (macro.defaultDef.isEmpty())
|
|
other = macro.otherDefs.begin().key();
|
|
config.lastLocation().warning(tr("Macro '\\%1' takes"
|
|
" inconsistent number"
|
|
" of arguments (%2"
|
|
" %3, %4 %5)")
|
|
.arg(*n)
|
|
.arg(*f)
|
|
.arg(m)
|
|
.arg(other)
|
|
.arg(macro.numParams));
|
|
silent = true;
|
|
}
|
|
if (macro.numParams < m)
|
|
macro.numParams = m;
|
|
}
|
|
}
|
|
++f;
|
|
}
|
|
|
|
if (macro.numParams != -1)
|
|
macroHash()->insert(*n, macro);
|
|
++n;
|
|
}
|
|
}
|
|
|
|
void Doc::terminate()
|
|
{
|
|
DocParser::exampleFiles.clear();
|
|
DocParser::exampleDirs.clear();
|
|
DocParser::sourceFiles.clear();
|
|
DocParser::sourceDirs.clear();
|
|
aliasMap()->clear();
|
|
cmdHash()->clear();
|
|
macroHash()->clear();
|
|
|
|
int i = 0;
|
|
while (cmds[i].english) {
|
|
delete cmds[i].alias;
|
|
cmds[i].alias = 0;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
QString Doc::alias(const QString &english)
|
|
{
|
|
return aliasMap()->value(english, english);
|
|
}
|
|
|
|
/*!
|
|
Trims the deadwood out of \a str. i.e., this function
|
|
cleans up \a str.
|
|
*/
|
|
void Doc::trimCStyleComment(Location& location, QString& str)
|
|
{
|
|
QString cleaned;
|
|
Location m = location;
|
|
bool metAsterColumn = true;
|
|
int asterColumn = location.columnNo() + 1;
|
|
int i;
|
|
|
|
for (i = 0; i < (int) str.length(); i++) {
|
|
if (m.columnNo() == asterColumn) {
|
|
if (str[i] != '*')
|
|
break;
|
|
cleaned += ' ';
|
|
metAsterColumn = true;
|
|
}
|
|
else {
|
|
if (str[i] == '\n') {
|
|
if (!metAsterColumn)
|
|
break;
|
|
metAsterColumn = false;
|
|
}
|
|
cleaned += str[i];
|
|
}
|
|
m.advance(str[i]);
|
|
}
|
|
if (cleaned.length() == str.length())
|
|
str = cleaned;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
location.advance(str[i]);
|
|
str = str.mid(3, str.length() - 5);
|
|
}
|
|
|
|
CodeMarker *Doc::quoteFromFile(const Location &location,
|
|
Quoter "er,
|
|
const QString &fileName)
|
|
{
|
|
quoter.reset();
|
|
|
|
QString code;
|
|
|
|
QString userFriendlyFilePath;
|
|
QString filePath = Config::findFile(location,
|
|
DocParser::exampleFiles,
|
|
DocParser::exampleDirs,
|
|
fileName, userFriendlyFilePath);
|
|
if (filePath.isEmpty()) {
|
|
location.warning(tr("Cannot find example file '%1'").arg(fileName));
|
|
}
|
|
else {
|
|
QFile inFile(filePath);
|
|
if (!inFile.open(QFile::ReadOnly)) {
|
|
location.warning(tr("Cannot open example file '%1'").arg(userFriendlyFilePath));
|
|
}
|
|
else {
|
|
QTextStream inStream(&inFile);
|
|
code = DocParser::untabifyEtc(inStream.readAll());
|
|
}
|
|
}
|
|
|
|
QString dirPath = QFileInfo(filePath).path();
|
|
CodeMarker *marker = CodeMarker::markerForFileName(fileName);
|
|
quoter.quoteFromFile(userFriendlyFilePath,
|
|
code,
|
|
marker->markedUpCode(code, 0, location));
|
|
return marker;
|
|
}
|
|
|
|
QString Doc::canonicalTitle(const QString &title)
|
|
{
|
|
// The code below is equivalent to the following chunk, but _much_
|
|
// faster (accounts for ~10% of total running time)
|
|
//
|
|
// QRegExp attributeExpr("[^A-Za-z0-9]+");
|
|
// QString result = title.toLower();
|
|
// result.replace(attributeExpr, " ");
|
|
// result = result.simplified();
|
|
// result.replace(QLatin1Char(' '), QLatin1Char('-'));
|
|
|
|
QString result;
|
|
result.reserve(title.size());
|
|
|
|
bool dashAppended = false;
|
|
bool begun = false;
|
|
int lastAlnum = 0;
|
|
for (int i = 0; i != title.size(); ++i) {
|
|
uint c = title.at(i).unicode();
|
|
if (c >= 'A' && c <= 'Z')
|
|
c -= 'A' - 'a';
|
|
bool alnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
|
if (alnum) {
|
|
result += QLatin1Char(c);
|
|
begun = true;
|
|
dashAppended = false;
|
|
lastAlnum = result.size();
|
|
}
|
|
else if (!dashAppended) {
|
|
if (begun)
|
|
result += QLatin1Char('-');
|
|
dashAppended = true;
|
|
}
|
|
}
|
|
result.truncate(lastAlnum);
|
|
return result;
|
|
}
|
|
|
|
void Doc::detach()
|
|
{
|
|
if (!priv) {
|
|
priv = new DocPrivate;
|
|
return;
|
|
}
|
|
if (priv->count == 1)
|
|
return;
|
|
|
|
--priv->count;
|
|
|
|
DocPrivate *newPriv = new DocPrivate(*priv);
|
|
newPriv->count = 1;
|
|
if (priv->extra)
|
|
newPriv->extra = new DocPrivateExtra(*priv->extra);
|
|
|
|
priv = newPriv;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|