diff options
author | Topi Reinio <topi.reinio@qt.io> | 2017-09-28 12:14:38 +0200 |
---|---|---|
committer | Topi Reiniƶ <topi.reinio@qt.io> | 2017-10-17 11:00:06 +0000 |
commit | 4c682533ffdabeb7597c98d2ff5f124796d278cc (patch) | |
tree | 26ad2024c6bf93252b910ff0e86d1c1e03b33d21 | |
parent | 0f49c09a6fc5d03b100766e781fc0c98cfbdea7a (diff) | |
download | qttools-4c682533ffdabeb7597c98d2ff5f124796d278cc.tar.gz |
qdoc: Make doc configuration aware of the output format
With the introduction of the WebXML output format, QDoc can no longer
assume that the configuration is specific to HTML. In particular,
Config variables:
- Output directory and subdirectory can now be set for each format.
- 'quotinginformation' can now be prefixed with '<format>.' qualifier.
Because the same parsed tree of atoms is used for all generators,
we need to include both the quoting information and the related
code snippets in the list of atoms; generators then decide what to
include in the output.
Index files:
- Look for index files under the output subdirectory for each specified
format.
This allows per-format configuration and passing multiple formats in
'outputformats' configuration variable.
Clean up and refactor the code related to processing doc config
variables.
Task-number: PYSIDE-363
Change-Id: I66e9c71311689a777fcb445d3b1a8ef070dff93d
Reviewed-by: Topi Reiniƶ <topi.reinio@qt.io>
-rw-r--r-- | src/qdoc/config.cpp | 7 | ||||
-rw-r--r-- | src/qdoc/config.h | 2 | ||||
-rw-r--r-- | src/qdoc/doc.cpp | 256 | ||||
-rw-r--r-- | src/qdoc/generator.cpp | 215 | ||||
-rw-r--r-- | src/qdoc/generator.h | 5 | ||||
-rw-r--r-- | src/qdoc/htmlgenerator.cpp | 7 | ||||
-rw-r--r-- | src/qdoc/main.cpp | 54 | ||||
-rw-r--r-- | src/qdoc/webxmlgenerator.cpp | 53 | ||||
-rw-r--r-- | src/qdoc/webxmlgenerator.h | 1 |
9 files changed, 320 insertions, 280 deletions
diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp index 46d7c5bf1..685add0f9 100644 --- a/src/qdoc/config.cpp +++ b/src/qdoc/config.cpp @@ -342,11 +342,12 @@ int Config::getInt(const QString& var) const } /*! - Function to return the correct outputdir. + Function to return the correct outputdir for the output \a format. + If \a format is not specified, defaults to 'HTML'. outputdir can be set using the qdocconf or the command-line variable -outputdir. */ -QString Config::getOutputDir() const +QString Config::getOutputDir(const QString &format) const { QString t; if (overrideOutputDir.isNull()) @@ -359,7 +360,7 @@ QString Config::getOutputDir() const } if (!Generator::useOutputSubdirs()) { t = t.left(t.lastIndexOf('/')); - QString singleOutputSubdir = getString("HTML.outputsubdir"); + QString singleOutputSubdir = getString(format + Config::dot + "outputsubdir"); if (singleOutputSubdir.isEmpty()) singleOutputSubdir = "html"; t += QLatin1Char('/') + singleOutputSubdir; diff --git a/src/qdoc/config.h b/src/qdoc/config.h index 298cd3f63..3fa24c1d6 100644 --- a/src/qdoc/config.h +++ b/src/qdoc/config.h @@ -85,7 +85,7 @@ public: const Location& lastLocation() const { return lastLocation_; } bool getBool(const QString& var) const; int getInt(const QString& var) const; - QString getOutputDir() const; + QString getOutputDir(const QString &format = QString("HTML")) const; QSet<QString> getOutputFormats() const; QString getString(const QString& var, const QString& defaultString = QString()) const; QSet<QString> getStringSet(const QString& var) const; diff --git a/src/qdoc/doc.cpp b/src/qdoc/doc.cpp index 4124ee1e4..3300da593 100644 --- a/src/qdoc/doc.cpp +++ b/src/qdoc/doc.cpp @@ -496,6 +496,8 @@ private: void skipSpacesOrOneEndl(); void skipAllSpaces(); void skipToNextPreprocessorCommand(); + static bool isCode(const Atom *atom); + static bool isQuote(const Atom *atom); QStack<int> openedInputs; @@ -528,6 +530,7 @@ private: QStack<OpenedList> openedLists; Quoter quoter; QStack<DitaRef*> ditarefs_; + Atom *lastAtom; }; int DocParser::tabSize; @@ -535,7 +538,7 @@ QStringList DocParser::exampleFiles; QStringList DocParser::exampleDirs; QStringList DocParser::sourceFiles; QStringList DocParser::sourceDirs; -bool DocParser::quoting; +bool DocParser::quoting = false; /*! Parse the \a source string to build a Text data structure @@ -610,6 +613,11 @@ void DocParser::parse(const QString& source, } } else { + // Ignore quoting atoms to make appendToCode() + // append to the correct atom. + if (!quoting || !isQuote(priv->text.lastAtom())) + lastAtom = priv->text.lastAtom(); + int cmd = cmdHash()->value(cmdStr,NOT_A_CMD); switch (cmd) { case CMD_A: @@ -679,40 +687,31 @@ void DocParser::parse(const QString& source, 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 { + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); append(Atom::CodeQuoteArgument, " "); } + if (isCode(lastAtom) && lastAtom->string().endsWith("\n\n")) + lastAtom->chopString(); + appendToCode("\n"); } 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 { + QString arg = getOptionalArgument(); + if (arg.isEmpty()) + arg = "4"; + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - QString arg = getOptionalArgument(); - if (arg.isEmpty()) - arg = "4"; append(Atom::CodeQuoteArgument, arg); } + if (isCode(lastAtom) && lastAtom->string().endsWith("\n\n")) + lastAtom->chopString(); + + int indent = arg.toInt(); + for (int i = 0; i < indent; ++i) + appendToCode(" "); + appendToCode("...\n"); } break; case CMD_ELSE: @@ -1104,33 +1103,36 @@ void DocParser::parse(const QString& source, break; case CMD_PRINTLINE: leavePara(); - if (!quoting) - appendToCode(quoter.quoteLine(location(), cmdStr, - getRestOfLine())); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + appendToCode(quoter.quoteLine(location(), cmdStr, rest)); + } break; case CMD_PRINTTO: leavePara(); - if (!quoting) - appendToCode(quoter.quoteTo(location(), cmdStr, - getRestOfLine())); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + appendToCode(quoter.quoteTo(location(), cmdStr, rest)); + } break; case CMD_PRINTUNTIL: leavePara(); - if (!quoting) - appendToCode(quoter.quoteUntil(location(), cmdStr, - getRestOfLine())); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + appendToCode(quoter.quoteUntil(location(), cmdStr, rest)); + } break; case CMD_QUOTATION: if (openCommand(cmd)) { @@ -1139,46 +1141,45 @@ void DocParser::parse(const QString& source, } break; case CMD_QUOTEFILE: - { leavePara(); + { QString fileName = getArgument(); Doc::quoteFromFile(location(), quoter, fileName); - if (!quoting) { - append(Atom::Code, - quoter.quoteTo(location(), cmdStr, QString())); - quoter.reset(); - } - else { + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); append(Atom::CodeQuoteArgument, fileName); } - break; + append(Atom::Code, quoter.quoteTo(location(), cmdStr, QString())); + quoter.reset(); } + break; case CMD_QUOTEFROMFILE: leavePara(); - if (!quoting) - quoteFromFile(); - else { + { + QString arg = getArgument(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getArgument()); + append(Atom::CodeQuoteArgument, arg); } + Doc::quoteFromFile(location(), quoter, arg); + } 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 { + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); append(Atom::CodeQuoteArgument, slashed(marker->functionEndRegExp(p1))); } + quoter.quoteTo(location(), cmdStr, + slashed(marker->functionBeginRegExp(p1))); + append(Atom::Code, + quoter.quoteUntil(location(), cmdStr, + slashed(marker->functionEndRegExp(p1)))); + quoter.reset(); + } break; case CMD_RAW: leavePara(); @@ -1236,36 +1237,36 @@ void DocParser::parse(const QString& source, break; case CMD_SKIPLINE: leavePara(); - if (!quoting) - quoter.quoteLine(location(), - cmdStr, - getRestOfLine()); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + quoter.quoteLine(location(), cmdStr, rest); + } break; case CMD_SKIPTO: leavePara(); - if (!quoting) - quoter.quoteTo(location(), - cmdStr, - getRestOfLine()); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + quoter.quoteTo(location(), cmdStr, rest); + } break; case CMD_SKIPUNTIL: leavePara(); - if (!quoting) - quoter.quoteUntil(location(), - cmdStr, - getRestOfLine()); - else { + { + QString rest = getRestOfLine(); + if (quoting) { append(Atom::CodeQuoteCommand, cmdStr); - append(Atom::CodeQuoteArgument, getRestOfLine()); + append(Atom::CodeQuoteArgument, rest); } + quoter.quoteUntil(location(), cmdStr, rest); + } break; case CMD_SPAN: p1 = ATOM_FORMATTING_SPAN + getArgument(true); @@ -1281,10 +1282,8 @@ void DocParser::parse(const QString& source, append(Atom::SnippetLocation, snippet); append(Atom::SnippetIdentifier, identifier); } - else { - marker = Doc::quoteFromFile(location(),quoter,snippet); - appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType()); - } + marker = Doc::quoteFromFile(location(), quoter, snippet); + appendToCode(quoter.quoteSnippet(location(), identifier), marker->atomType()); } break; case CMD_SUB: @@ -2030,19 +2029,21 @@ void DocParser::appendWord(const QString &word) void DocParser::appendToCode(const QString& markedCode) { - Atom::AtomType lastType = priv->text.lastAtom()->type(); - if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) - append(Atom::Qml); - priv->text.lastAtom()->appendString(markedCode); + if (!isCode(lastAtom)) { + append(Atom::Code); + lastAtom = priv->text.lastAtom(); + } + lastAtom->appendString(markedCode); } void DocParser::appendToCode(const QString &markedCode, Atom::AtomType defaultType) { - Atom::AtomType lastType = priv->text.lastAtom()->type(); - if (lastType != Atom::Qml && lastType != Atom::Code && lastType != Atom::JavaScript) + if (!isCode(lastAtom)) { append(defaultType, markedCode); - else - priv->text.lastAtom()->appendString(markedCode); + lastAtom = priv->text.lastAtom(); + } else { + lastAtom->appendString(markedCode); + } } void DocParser::startNewPara() @@ -2808,6 +2809,30 @@ QString DocParser::slashed(const QString& str) } /*! + Returns \c true if \a atom represents a code snippet. + */ +bool DocParser::isCode(const Atom *atom) +{ + Atom::AtomType type = atom->type(); + return (type == Atom::Code + || type == Atom::Qml + || type == Atom::JavaScript); +} + +/*! + Returns \c true if \a atom represents quoting information. + */ +bool DocParser::isQuote(const Atom *atom) +{ + Atom::AtomType type = atom->type(); + return (type == Atom::CodeQuoteArgument + || type == Atom::CodeQuoteCommand + || type == Atom::SnippetCommand + || type == Atom::SnippetIdentifier + || type == Atom::SnippetLocation); +} + +/*! Parse the qdoc comment \a source. Build up a list of all the topic commands found including their arguments. This constructor is used when there can be more than one topic command in theqdoc comment. @@ -3130,28 +3155,23 @@ void Doc::initialize(const Config& config) DocParser::exampleDirs = config.getCanonicalPathList(CONFIG_EXAMPLEDIRS); DocParser::sourceFiles = config.getCanonicalPathList(CONFIG_SOURCES); DocParser::sourceDirs = config.getCanonicalPathList(CONFIG_SOURCEDIRS); - DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION); QmlTypeNode::qmlOnly = config.getBool(CONFIG_QMLONLY); QStringMap reverseAliasMap; config_ = &config; - QSet<QString> commands = config.subVars(CONFIG_ALIAS); - QSet<QString>::ConstIterator c = commands.constBegin(); - while (c != commands.constEnd()) { - QString alias = config.getString(CONFIG_ALIAS + Config::dot + *c); + for (const auto &a : config.subVars(CONFIG_ALIAS)) { + QString alias = config.getString(CONFIG_ALIAS + Config::dot + a); 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)); + .arg(a)); + } else { + reverseAliasMap.insert(alias, a); } - else { - reverseAliasMap.insert(alias, *c); - } - aliasMap()->insert(*c, alias); - ++c; + aliasMap()->insert(a, alias); } int i = 0; @@ -3164,10 +3184,8 @@ void Doc::initialize(const Config& config) i++; } - QSet<QString> macroNames = config.subVars(CONFIG_MACRO); - QSet<QString>::ConstIterator n = macroNames.constBegin(); - while (n != macroNames.constEnd()) { - QString macroDotName = CONFIG_MACRO + Config::dot + *n; + for (const auto ¯oName : config.subVars(CONFIG_MACRO)) { + QString macroDotName = CONFIG_MACRO + Config::dot + macroName; Macro macro; macro.numParams = -1; macro.defaultDef = config.getString(macroDotName); @@ -3177,16 +3195,13 @@ void Doc::initialize(const Config& config) } bool silent = false; - QSet<QString> formats = config.subVars(macroDotName); - QSet<QString>::ConstIterator f = formats.constBegin(); - while (f != formats.constEnd()) { - QString def = config.getString(macroDotName + Config::dot + *f); + for (const auto &f : config.subVars(macroDotName)) { + QString def = config.getString(macroDotName + Config::dot + f); if (!def.isEmpty()) { - macro.otherDefs.insert(*f, def); + macro.otherDefs.insert(f, def); int m = Config::numParams(def); - if (macro.numParams == -1) { + if (macro.numParams == -1) macro.numParams = m; - } else if (macro.numParams != m) { if (!silent) { QString other = tr("default"); @@ -3196,8 +3211,8 @@ void Doc::initialize(const Config& config) " inconsistent number" " of arguments (%2" " %3, %4 %5)") - .arg(*n) - .arg(*f) + .arg(macroName) + .arg(f) .arg(m) .arg(other) .arg(macro.numParams)); @@ -3207,13 +3222,16 @@ void Doc::initialize(const Config& config) macro.numParams = m; } } - ++f; } - if (macro.numParams != -1) - macroHash()->insert(*n, macro); - ++n; + macroHash()->insert(macroName, macro); } + // If any of the formats define quotinginformation, activate quoting + DocParser::quoting = config.getBool(CONFIG_QUOTINGINFORMATION); + for (const auto &format : config.getOutputFormats()) + DocParser::quoting = DocParser::quoting || config.getBool(format + + Config::dot + + CONFIG_QUOTINGINFORMATION); } /*! diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp index c13f02cf0..dca418f05 100644 --- a/src/qdoc/generator.cpp +++ b/src/qdoc/generator.cpp @@ -1682,6 +1682,7 @@ void Generator::generateOverloadedSignal(const Node* node, CodeMarker* marker) */ void Generator::generateDocs() { + currentGenerator_ = this; generateAggregate(qdb_->primaryTreeRoot()); } @@ -1788,40 +1789,12 @@ QString Generator::indent(int level, const QString& markedCode) return t; } -void Generator::initialize(const Config &config) -{ - if (config.getBool(QString("HTML.nosubdirs"))) - resetUseOutputSubdirs(); - outFileNames_.clear(); +void Generator::initialize(const Config &config) +{ outputFormats = config.getOutputFormats(); redirectDocumentationToDevNull_ = config.getBool(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL); - if (!outputFormats.isEmpty()) { - outDir_ = config.getOutputDir(); - if (outDir_.isEmpty()) { - config.lastLocation().fatal(tr("No output directory specified in " - "configuration file or on the command line")); - } - else { - outSubdir_ = outDir_.mid(outDir_.lastIndexOf('/') + 1); - } - - QDir dirInfo; - if (dirInfo.exists(outDir_)) { - if (!generating() && Generator::useOutputSubdirs()) { - if (!Config::removeDirContents(outDir_)) - config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); - } - } - else { - if (!dirInfo.mkpath(outDir_)) - config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_)); - } - - if (!dirInfo.exists(outDir_ + "/images") && !dirInfo.mkdir(outDir_ + "/images")) - config.lastLocation().fatal(tr("Cannot create images directory '%1'").arg(outDir_ + "/images")); - } imageFiles = config.getCanonicalPathList(CONFIG_IMAGES); imageDirs = config.getCanonicalPathList(CONFIG_IMAGEDIRS); @@ -1833,81 +1806,20 @@ void Generator::initialize(const Config &config) exampleImgExts = config.getStringList(CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS); QString imagesDotFileExtensions = CONFIG_IMAGES + Config::dot + CONFIG_FILEEXTENSIONS; - QSet<QString> formats = config.subVars(imagesDotFileExtensions); - QSet<QString>::ConstIterator f = formats.constBegin(); - while (f != formats.constEnd()) { - imgFileExts[*f] = config.getStringList(imagesDotFileExtensions + Config::dot + *f); - ++f; - } + for (const auto &ext : config.subVars(imagesDotFileExtensions)) + imgFileExts[ext] = config.getStringList(imagesDotFileExtensions + Config::dot + ext); - QList<Generator *>::ConstIterator g = generators.constBegin(); - while (g != generators.constEnd()) { - if (outputFormats.contains((*g)->format())) { - currentGenerator_ = (*g); - (*g)->initializeGenerator(config); - QStringList extraImages = config.getCanonicalPathList((*g)->format() + - Config::dot + - CONFIG_EXTRAIMAGES, true); - QStringList::ConstIterator e = extraImages.constBegin(); - while (e != extraImages.constEnd()) { - QString filePath = *e; - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), filePath, filePath, - (*g)->outputDir() + "/images"); - ++e; - } - - // Documentation template handling - QStringList scripts = config.getCanonicalPathList((*g)->format()+Config::dot+CONFIG_SCRIPTS, true); - if (!scripts.isEmpty()) { - QDir dirInfo; - if (!dirInfo.exists(outDir_ + "/scripts") && !dirInfo.mkdir(outDir_ + "/scripts")) { - config.lastLocation().fatal(tr("Cannot create scripts directory '%1'") - .arg(outDir_ + "/scripts")); - } - else { - e = scripts.constBegin(); - while (e != scripts.constEnd()) { - QString filePath = *e; - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), filePath, filePath, - (*g)->outputDir() + "/scripts"); - ++e; - } - } - } - - QStringList styles = config.getCanonicalPathList((*g)->format()+Config::dot+CONFIG_STYLESHEETS, true); - if (!styles.isEmpty()) { - QDir dirInfo; - if (!dirInfo.exists(outDir_ + "/style") && !dirInfo.mkdir(outDir_ + "/style")) { - config.lastLocation().fatal(tr("Cannot create style directory '%1'") - .arg(outDir_ + "/style")); - } - else { - e = styles.constBegin(); - while (e != styles.constEnd()) { - QString filePath = *e; - if (!filePath.isEmpty()) - Config::copyFile(config.lastLocation(), filePath, filePath, - (*g)->outputDir() + "/style"); - ++e; - } - } - } + for (auto &g : generators) { + if (outputFormats.contains(g->format())) { + currentGenerator_ = g; + g->initializeGenerator(config); } - ++g; } - QRegExp secondParamAndAbove("[\2-\7]"); - QSet<QString> formattingNames = config.subVars(CONFIG_FORMATTING); - QSet<QString>::ConstIterator n = formattingNames.constBegin(); - while (n != formattingNames.constEnd()) { - QString formattingDotName = CONFIG_FORMATTING + Config::dot + *n; - QSet<QString> formats = config.subVars(formattingDotName); - QSet<QString>::ConstIterator f = formats.constBegin(); - while (f != formats.constEnd()) { - QString def = config.getString(formattingDotName + Config::dot + *f); + for (const auto &n : config.subVars(CONFIG_FORMATTING)) { + QString formattingDotName = CONFIG_FORMATTING + Config::dot + n; + for (const auto &f : config.subVars(formattingDotName)) { + QString def = config.getString(formattingDotName + Config::dot + f); if (!def.isEmpty()) { int numParams = Config::numParams(def); int numOccs = def.count("\1"); @@ -1915,24 +1827,20 @@ void Generator::initialize(const Config &config) config.lastLocation().warning(tr("Formatting '%1' must " "have exactly one " "parameter (found %2)") - .arg(*n).arg(numParams)); - } - else if (numOccs > 1) { + .arg(n).arg(numParams)); + } else if (numOccs > 1) { config.lastLocation().fatal(tr("Formatting '%1' must " "contain exactly one " "occurrence of '\\1' " "(found %2)") - .arg(*n).arg(numOccs)); - } - else { + .arg(n).arg(numOccs)); + } else { int paramPos = def.indexOf("\1"); - fmtLeftMaps[*f].insert(*n, def.left(paramPos)); - fmtRightMaps[*f].insert(*n, def.mid(paramPos + 1)); + fmtLeftMaps[f].insert(n, def.left(paramPos)); + fmtRightMaps[f].insert(n, def.mid(paramPos + 1)); } } - ++f; } - ++n; } project_ = config.getString(CONFIG_PROJECT); @@ -1940,26 +1848,95 @@ void Generator::initialize(const Config &config) outputPrefixes.clear(); QStringList items = config.getStringList(CONFIG_OUTPUTPREFIXES); if (!items.isEmpty()) { - foreach (const QString &prefix, items) + for (const auto &prefix : items) outputPrefixes[prefix] = config.getString(CONFIG_OUTPUTPREFIXES + Config::dot + prefix); - } - else { + } else { outputPrefixes[QLatin1String("QML")] = QLatin1String("qml-"); outputPrefixes[QLatin1String("JS")] = QLatin1String("js-"); } outputSuffixes.clear(); - items = config.getStringList(CONFIG_OUTPUTSUFFIXES); - if (!items.isEmpty()) { - foreach (const QString &suffix, items) - outputSuffixes[suffix] = config.getString(CONFIG_OUTPUTSUFFIXES + Config::dot + suffix); - } + for (const auto &suffix : config.getStringList(CONFIG_OUTPUTSUFFIXES)) + outputSuffixes[suffix] = config.getString(CONFIG_OUTPUTSUFFIXES + Config::dot + suffix); noLinkErrors_ = config.getBool(CONFIG_NOLINKERRORS); autolinkErrors_ = config.getBool(CONFIG_AUTOLINKERRORS); } /*! + Creates template-specific subdirs (e.g. /styles and /scripts for HTML) + and copies the files to them. + */ +void Generator::copyTemplateFiles(const Config &config, const QString &configVar, const QString &subDir) +{ + QStringList files = config.getCanonicalPathList(configVar, true); + if (!files.isEmpty()) { + QDir dirInfo; + QString templateDir = outDir_ + QLatin1Char('/') + subDir; + if (!dirInfo.exists(templateDir) && !dirInfo.mkdir(templateDir)) { + config.lastLocation().fatal(tr("Cannot create %1 directory '%2'") + .arg(subDir, templateDir)); + } else { + for (const auto &file : files) { + if (!file.isEmpty()) + Config::copyFile(config.lastLocation(), file, file, templateDir); + } + } + } +} + +/*! + Reads format-specific variables from \a config, sets output + (sub)directories, creates them on the filesystem and copies the + template-specific files. + */ +void Generator::initializeFormat(const Config &config) +{ + outFileNames_.clear(); + useOutputSubdirs_ = true; + if (config.getBool(format() + Config::dot + "nosubdirs")) + resetUseOutputSubdirs(); + + if (outputFormats.isEmpty()) + return; + + outDir_ = config.getOutputDir(format()); + if (outDir_.isEmpty()) { + config.lastLocation().fatal(tr("No output directory specified in " + "configuration file or on the command line")); + } else { + outSubdir_ = outDir_.mid(outDir_.lastIndexOf('/') + 1); + } + + QDir dirInfo; + if (dirInfo.exists(outDir_)) { + if (!generating() && Generator::useOutputSubdirs()) { + if (!Config::removeDirContents(outDir_)) + config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_)); + } + } else if (!dirInfo.mkpath(outDir_)) { + config.lastLocation().fatal(tr("Cannot create output directory '%1'").arg(outDir_)); + } + + // Output directory exists, which is enough for prepare phase. + if (preparing()) + return; + + if (!dirInfo.exists(outDir_ + "/images") && !dirInfo.mkdir(outDir_ + "/images")) + config.lastLocation().fatal(tr("Cannot create images directory '%1'").arg(outDir_ + "/images")); + + copyTemplateFiles(config, format() + Config::dot + CONFIG_STYLESHEETS, "style"); + copyTemplateFiles(config, format() + Config::dot + CONFIG_SCRIPTS, "scripts"); + copyTemplateFiles(config, format() + Config::dot + CONFIG_EXTRAIMAGES, "images"); + + // Use a format-specific .quotinginformation if defined, otherwise a global value + if (config.subVars(format()).contains(CONFIG_QUOTINGINFORMATION)) + quoting_ = config.getBool(format() + Config::dot + CONFIG_QUOTINGINFORMATION); + else + quoting_ = config.getBool(CONFIG_QUOTINGINFORMATION); +} + +/*! Appends each directory path in \a moreImageDirs to the list of image directories. */ diff --git a/src/qdoc/generator.h b/src/qdoc/generator.h index 1552b6ff1..ba69c8ac9 100644 --- a/src/qdoc/generator.h +++ b/src/qdoc/generator.h @@ -66,6 +66,7 @@ public: virtual QString format() = 0; virtual void generateDocs(); virtual void initializeGenerator(const Config &config); + virtual void initializeFormat(const Config &config); virtual void terminateGenerator(); QString fullDocumentLocation(const Node *node, bool useSubdir = false); @@ -231,6 +232,9 @@ private: void generateReimplementedFrom(const FunctionNode *func, CodeMarker *marker); static bool compareNodes(Node *a, Node *b) { return (a->name() < b->name()); } + static void copyTemplateFiles(const Config &config, + const QString &configVar, + const QString &subDir); protected: const Config* config_; @@ -242,6 +246,7 @@ private: bool threeColumnEnumValueTable_; bool showInternal_; bool singleExec_; + bool quoting_; int numTableRows_; QString link_; QString sectionNumber_; diff --git a/src/qdoc/htmlgenerator.cpp b/src/qdoc/htmlgenerator.cpp index 1d468a339..82fcf0ffc 100644 --- a/src/qdoc/htmlgenerator.cpp +++ b/src/qdoc/htmlgenerator.cpp @@ -1354,6 +1354,13 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark case Atom::EndQmlText: // don't do anything with these. They are just tags. break; + case Atom::CodeQuoteArgument: + case Atom::CodeQuoteCommand: + case Atom::SnippetCommand: + case Atom::SnippetIdentifier: + case Atom::SnippetLocation: + // no HTML output (ignore) + break; default: unknownAtom(atom); } diff --git a/src/qdoc/main.cpp b/src/qdoc/main.cpp index 412627e40..a8e147f82 100644 --- a/src/qdoc/main.cpp +++ b/src/qdoc/main.cpp @@ -86,11 +86,13 @@ static QList<Translator> translators; /*! Read some XML indexes containing definitions from other documentation sets. \a config contains a variable that - lists directories where index files can bge found. It also + lists directories where index files can be found. It also contains the \c depends variable, which lists the modules - that the current module depends on. + that the current module depends on. \a formats contains + a list of output formats; each format may have a different + output subdirectory where index files are located. */ -static void loadIndexFiles(Config& config) +static void loadIndexFiles(Config& config, const QSet<QString> &formats) { QDocDatabase* qdb = QDocDatabase::qdocDB(); QStringList indexFiles; @@ -105,14 +107,17 @@ static void loadIndexFiles(Config& config) dependModules += config.getStringList(CONFIG_DEPENDS); dependModules.removeDuplicates(); - - bool noOutputSubdirs = false; - QString singleOutputSubdir; - if (config.getBool(QString("HTML.nosubdirs"))) { - noOutputSubdirs = true; - singleOutputSubdir = config.getString("HTML.outputsubdir"); - if (singleOutputSubdir.isEmpty()) - singleOutputSubdir = "html"; + QSet<QString> subDirs; + + for (const auto &format : formats) { + if (config.getBool(format + Config::dot + "nosubdirs")) { + QString singleOutputSubdir = config.getString(format + + Config::dot + + "outputsubdir"); + if (singleOutputSubdir.isEmpty()) + singleOutputSubdir = "html"; + subDirs << singleOutputSubdir; + } } if (dependModules.size() > 0) { @@ -143,19 +148,21 @@ static void loadIndexFiles(Config& config) for (int i = 0; i < dependModules.size(); i++) { QString indexToAdd; QList<QFileInfo> foundIndices; + // Always look in module-specific subdir, even with *.nosubdirs config + subDirs << dependModules[i]; for (int j = 0; j < indexDirs.size(); j++) { - QString fileToLookFor = indexDirs[j] + QLatin1Char('/'); - if (noOutputSubdirs) - fileToLookFor += singleOutputSubdir; - else - fileToLookFor += dependModules[i]; - fileToLookFor += QLatin1Char('/') + dependModules[i] + QLatin1String(".index"); - if (QFile::exists(fileToLookFor)) { - QFileInfo tempFileInfo(fileToLookFor); - if (!foundIndices.contains(tempFileInfo)) - foundIndices.append(tempFileInfo); + for (const auto &subDir : subDirs) { + QString fileToLookFor = indexDirs[j] + + QLatin1Char('/') + subDir + + QLatin1Char('/') + dependModules[i]; + if (QFile::exists(fileToLookFor)) { + QFileInfo tempFileInfo(fileToLookFor); + if (!foundIndices.contains(tempFileInfo)) + foundIndices.append(tempFileInfo); + } } } + subDirs.remove(dependModules[i]); std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore); if (foundIndices.size() > 1) { /* @@ -271,10 +278,10 @@ static void processQdocconfFile(const QString &fileName) */ Location::initialize(config); Tokenizer::initialize(config); - Doc::initialize(config); CodeMarker::initialize(config); CodeParser::initialize(config); Generator::initialize(config); + Doc::initialize(config); #ifndef QT_NO_TRANSLATION /* @@ -341,7 +348,7 @@ static void processQdocconfFile(const QString &fileName) if (!Generator::singleExec()) { if (!Generator::preparing()) { Generator::debug(" loading index files"); - loadIndexFiles(config); + loadIndexFiles(config, outputFormats); Generator::debug(" done loading index files"); } qdb->newPrimaryTree(project); @@ -499,6 +506,7 @@ static void processQdocconfFile(const QString &fileName) if (generator == 0) outputFormatsLocation.fatal(QCoreApplication::translate("QDoc", "Unknown output format '%1'").arg(*of)); + generator->initializeFormat(config); generator->generateDocs(); ++of; } diff --git a/src/qdoc/webxmlgenerator.cpp b/src/qdoc/webxmlgenerator.cpp index 12f586f71..76aca742c 100644 --- a/src/qdoc/webxmlgenerator.cpp +++ b/src/qdoc/webxmlgenerator.cpp @@ -143,7 +143,7 @@ void WebXMLGenerator::generateIndexSections(QXmlStreamWriter &writer, writer.writeEndElement(); // generatedlist } - inLink = inContents = inSectionHeading = false; + inLink = inContents = inSectionHeading = hasQuotingInformation = false; numTableRows = 0; const Atom *atom = node->doc().body().firstAtom(); @@ -209,6 +209,8 @@ void WebXMLGenerator::generateAggregate(Aggregate *node) const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, const Atom *atom, const Node *relative, CodeMarker *marker) { + bool keepQuoting = false; + switch (atom->type()) { case Atom::AutoLink: if (!inLink && !inSectionHeading) { @@ -287,12 +289,18 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, break; case Atom::Code: - writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string()), QString(), QString())); + if (!hasQuotingInformation) + writer.writeTextElement("code", trimmedTrailing(plainCode(atom->string()), QString(), QString())); + else + keepQuoting = true; break; #ifdef QDOC_QML case Atom::Qml: - writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string()), QString(), QString())); + if (!hasQuotingInformation) + writer.writeTextElement("qml", trimmedTrailing(plainCode(atom->string()), QString(), QString())); + else + keepQuoting = true; #endif case Atom::CodeBad: writer.writeTextElement("badcode", trimmedTrailing(plainCode(atom->string()), QString(), QString())); @@ -309,17 +317,23 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, break; case Atom::CodeQuoteArgument: - if (quoteCommand == "dots") { - writer.writeAttribute("indent", atom->string()); - writer.writeCharacters("..."); - } else - writer.writeCharacters(atom->string()); - writer.writeEndElement(); // code + if (quoting_) { + if (quoteCommand == "dots") { + writer.writeAttribute("indent", atom->string()); + writer.writeCharacters("..."); + } else { + writer.writeCharacters(atom->string()); + } + writer.writeEndElement(); // code + keepQuoting = true; + } break; case Atom::CodeQuoteCommand: - quoteCommand = atom->string(); - writer.writeStartElement(quoteCommand); + if (quoting_) { + quoteCommand = atom->string(); + writer.writeStartElement(quoteCommand); + } break; case Atom::FootnoteLeft: @@ -547,16 +561,23 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, break; case Atom::SnippetCommand: - writer.writeStartElement(atom->string()); + if (quoting_) { + writer.writeStartElement(atom->string()); + + } break; case Atom::SnippetIdentifier: - writer.writeAttribute("identifier", atom->string()); - writer.writeEndElement(); + if (quoting_) { + writer.writeAttribute("identifier", atom->string()); + writer.writeEndElement(); + keepQuoting = true; + } break; case Atom::SnippetLocation: - writer.writeAttribute("location", atom->string()); + if (quoting_) + writer.writeAttribute("location", atom->string()); break; case Atom::String: @@ -618,6 +639,8 @@ const Atom *WebXMLGenerator::addAtomElements(QXmlStreamWriter &writer, break; } + hasQuotingInformation = keepQuoting; + if (atom) return atom->next(); diff --git a/src/qdoc/webxmlgenerator.h b/src/qdoc/webxmlgenerator.h index fb1a05ebe..04c1a9877 100644 --- a/src/qdoc/webxmlgenerator.h +++ b/src/qdoc/webxmlgenerator.h @@ -75,6 +75,7 @@ private: bool inContents; bool inSectionHeading; bool inTableHeader; + bool hasQuotingInformation; int numTableRows; bool threeColumnEnumValueTable; QString quoteCommand; |