void AbstractSettings::readDocumentation()
{
    const QString filename = documentationFilePath();
    if (filename.isEmpty()) {
        BeautifierPlugin::showError(tr("No documentation file specified."));
        return;
    }

    QFile file(filename);
    if (!file.exists())
        createDocumentationFile();

    if (!file.open(QIODevice::ReadOnly)) {
        BeautifierPlugin::showError(tr("Cannot open documentation file \"%1\".").arg(filename));
        return;
    }

    QXmlStreamReader xml(&file);
    if (!xml.readNextStartElement())
        return;
    if (xml.name() != Constants::DOCUMENTATION_XMLROOT) {
        BeautifierPlugin::showError(tr("The file \"%1\" is not a valid documentation file.")
                                    .arg(filename));
        return;
    }

    // We do not use QHash<QString, QString> since e.g. in Artistic Style there are many different
    // keys with the same (long) documentation text. By splitting the keys from the documentation
    // text we save a huge amount of memory.
    m_options.clear();
    m_docu.clear();
    QStringList keys;
    while (!(xml.atEnd() || xml.hasError())) {
        if (xml.readNext() == QXmlStreamReader::StartElement) {
            const QStringRef &name = xml.name();
            if (name == Constants::DOCUMENTATION_XMLENTRY) {
                keys.clear();
            } else if (name == Constants::DOCUMENTATION_XMLKEY) {
                if (xml.readNext() == QXmlStreamReader::Characters)
                    keys << xml.text().toString();
            } else if (name == Constants::DOCUMENTATION_XMLDOC) {
                if (xml.readNext() == QXmlStreamReader::Characters) {
                    m_docu << xml.text().toString();
                    const int index = m_docu.size() - 1;
                    for (const QString &key : keys)
                        m_options.insert(key, index);
                }
            }
        }
    }

    if (xml.hasError()) {
        BeautifierPlugin::showError(tr("Cannot read documentation file \"%1\": %2.")
                                    .arg(filename).arg(xml.errorString()));
    }
}
void ArtisticStyleSettings::createDocumentationFile() const
{
    QProcess process;
    process.start(command(), QStringList() << QLatin1String("-h"));
    process.waitForFinished(2000); // show help should be really fast.
    if (process.error() != QProcess::UnknownError)
        return;

    QFile file(documentationFilePath());
    const QFileInfo fi(file);
    if (!fi.exists())
        fi.dir().mkpath(fi.absolutePath());
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
        return;

    bool contextWritten = false;
    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true);
    stream.writeStartDocument(QLatin1String("1.0"), true);
    stream.writeComment(QLatin1String("Created ")
                        + QDateTime::currentDateTime().toString(Qt::ISODate));
    stream.writeStartElement(QLatin1String(Constants::DOCUMENTATION_XMLROOT));

    // astyle writes its output to 'error'...
    const QStringList lines = QString::fromUtf8(process.readAllStandardError())
            .split(QLatin1Char('\n'));
    const int totalLines = lines.count();
    QStringList keys;
    QStringList docu;
    for (int i = 0; i < totalLines; ++i) {
        const QString &line = lines.at(i).trimmed();
        if ((line.startsWith(QLatin1String("--")) && !line.startsWith(QLatin1String("---")))
                || line.startsWith(QLatin1String("OR "))) {
            QStringList rawKeys = line.split(QLatin1String(" OR "), QString::SkipEmptyParts);
            foreach (QString k, rawKeys) {
                k = k.trimmed();
                k.remove(QLatin1Char('#'));
                keys << k;
                if (k.startsWith(QLatin1String("--")))
                    keys << k.right(k.size() - 2);
            }
        } else {
void UncrustifySettings::createDocumentationFile() const
{
    QProcess process;
    process.start(command(), QStringList() << QLatin1String("--show-config"));
    process.waitForFinished(2000); // show config should be really fast.
    if (process.error() != QProcess::UnknownError)
        return;

    QFile file(documentationFilePath());
    const QFileInfo fi(file);
    if (!fi.exists())
        fi.dir().mkpath(fi.absolutePath());
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
        return;

    bool contextWritten = false;
    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true);
    stream.writeStartDocument(QLatin1String("1.0"), true);
    stream.writeComment(QLatin1String("Created ")
                        + QDateTime::currentDateTime().toString(Qt::ISODate));
    stream.writeStartElement(QLatin1String(Constants::DOCUMENTATION_XMLROOT));

    const QStringList lines = QString::fromUtf8(process.readAll()).split(QLatin1Char('\n'));
    const int totalLines = lines.count();
    for (int i = 0; i < totalLines; ++i) {
        const QString &line = lines.at(i);
        if (line.startsWith(QLatin1Char('#')) || line.trimmed().isEmpty())
            continue;

        const int firstSpace = line.indexOf(QLatin1Char(' '));
        const QString keyword = line.left(firstSpace);
        const QString options = line.right(line.size() - firstSpace).trimmed();
        QStringList docu;
        while (++i < totalLines) {
            const QString &subline = lines.at(i);
            if (line.startsWith(QLatin1Char('#')) || subline.trimmed().isEmpty()) {
                const QString text = QLatin1String("<p><span class=\"option\">") + keyword
                        + QLatin1String("</span> <span class=\"param\">") + options
                        + QLatin1String("</span></p><p>")
                        + (docu.join(QLatin1Char(' ')).toHtmlEscaped())
                        + QLatin1String("</p>");
                stream.writeStartElement(QLatin1String(Constants::DOCUMENTATION_XMLENTRY));
                stream.writeTextElement(QLatin1String(Constants::DOCUMENTATION_XMLKEY), keyword);
                stream.writeTextElement(QLatin1String(Constants::DOCUMENTATION_XMLDOC), text);
                stream.writeEndElement();
                contextWritten = true;
                break;
            } else {
                docu << subline;
            }
        }
    }

    stream.writeEndElement();
    stream.writeEndDocument();

    // An empty file causes error messages and a contextless file preventing this function to run
    // again in order to generate the documentation successfully. Thus delete the file.
    if (!contextWritten) {
        file.close();
        file.remove();
    }
}
void ArtisticStyleSettings::createDocumentationFile() const
{
    Utils::SynchronousProcess process;
    process.setTimeoutS(2);
    Utils::SynchronousProcessResponse response
            = process.runBlocking(command(), QStringList() << QLatin1String("-h"));
    if (response.result != Utils::SynchronousProcessResponse::Finished)
        return;

    QFile file(documentationFilePath());
    const QFileInfo fi(file);
    if (!fi.exists())
        fi.dir().mkpath(fi.absolutePath());
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
        return;

    bool contextWritten = false;
    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true);
    stream.writeStartDocument("1.0", true);
    stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate));
    stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT);

    // astyle writes its output to 'error'...
    const QStringList lines = response.stdErr().split(QLatin1Char('\n'));
    QStringList keys;
    QStringList docu;
    for (QString line : lines) {
        line = line.trimmed();
        if ((line.startsWith("--") && !line.startsWith("---")) || line.startsWith("OR ")) {
            const QStringList rawKeys = line.split(" OR ", QString::SkipEmptyParts);
            for (QString k : rawKeys) {
                k = k.trimmed();
                k.remove('#');
                keys << k;
                if (k.startsWith("--"))
                    keys << k.right(k.size() - 2);
            }
        } else {
            if (line.isEmpty()) {
                if (!keys.isEmpty()) {
                    // Write entry
                    stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY);
                    stream.writeStartElement(Constants::DOCUMENTATION_XMLKEYS);
                    for (const QString &key : keys)
                        stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, key);
                    stream.writeEndElement();
                    const QString text = "<p><span class=\"option\">"
                            + keys.filter(QRegExp("^\\-")).join(", ") + "</span></p><p>"
                            + (docu.join(' ').toHtmlEscaped()) + "</p>";
                    stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text);
                    stream.writeEndElement();
                    contextWritten = true;
                }
                keys.clear();
                docu.clear();
            } else if (!keys.isEmpty()) {
                docu << line;
            }
        }
    }

    stream.writeEndElement();
    stream.writeEndDocument();

    // An empty file causes error messages and a contextless file preventing this function to run
    // again in order to generate the documentation successfully. Thus delete the file.
    if (!contextWritten) {
        file.close();
        file.remove();
    }
}
void UncrustifySettings::createDocumentationFile() const
{
    Utils::SynchronousProcess process;
    process.setTimeoutS(2);
    Utils::SynchronousProcessResponse response
            = process.run(command(), QStringList() << QLatin1String("--show-config"));
    if (response.result != Utils::SynchronousProcessResponse::Finished)
        return;

    QFile file(documentationFilePath());
    const QFileInfo fi(file);
    if (!fi.exists())
        fi.dir().mkpath(fi.absolutePath());
    if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
        return;

    bool contextWritten = false;
    QXmlStreamWriter stream(&file);
    stream.setAutoFormatting(true);
    stream.writeStartDocument("1.0", true);
    stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate));
    stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT);

    const QStringList lines = response.allOutput().split(QLatin1Char('\n'));
    const int totalLines = lines.count();
    for (int i = 0; i < totalLines; ++i) {
        const QString &line = lines.at(i);
        if (line.startsWith('#') || line.trimmed().isEmpty())
            continue;

        const int firstSpace = line.indexOf(' ');
        const QString keyword = line.left(firstSpace);
        const QString options = line.right(line.size() - firstSpace).trimmed();
        QStringList docu;
        while (++i < totalLines) {
            const QString &subline = lines.at(i);
            if (line.startsWith('#') || subline.trimmed().isEmpty()) {
                const QString text = "<p><span class=\"option\">" + keyword
                        + "</span> <span class=\"param\">" + options
                        + "</span></p><p>" + docu.join(' ').toHtmlEscaped() + "</p>";
                stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY);
                stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, keyword);
                stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text);
                stream.writeEndElement();
                contextWritten = true;
                break;
            } else {
                docu << subline;
            }
        }
    }

    stream.writeEndElement();
    stream.writeEndDocument();

    // An empty file causes error messages and a contextless file preventing this function to run
    // again in order to generate the documentation successfully. Thus delete the file.
    if (!contextWritten) {
        file.close();
        file.remove();
    }
}