diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /tools/qdoc3/quoter.cpp | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'tools/qdoc3/quoter.cpp')
-rw-r--r-- | tools/qdoc3/quoter.cpp | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/tools/qdoc3/quoter.cpp b/tools/qdoc3/quoter.cpp new file mode 100644 index 0000000..40ca15a --- /dev/null +++ b/tools/qdoc3/quoter.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qfileinfo.h> +#include <qregexp.h> +#include <qdebug.h> + +#include "quoter.h" + +QT_BEGIN_NAMESPACE + +static void replaceMultipleNewlines(QString &s) +{ + const int n = s.size(); + bool slurping = false; + int j = -1; + const QChar newLine = QLatin1Char('\n'); + QChar *d = s.data(); + for (int i = 0; i != n; ++i) { + const QChar c = d[i]; + bool hit = (c == newLine); + if (slurping && hit) + continue; + d[++j] = c; + slurping = hit; + } + s.resize(++j); +} + +// This is equivalent to line.split( QRegExp("\n(?!\n|$)") ) but much faster +static QStringList splitLines(const QString &line) +{ + QStringList result; + int i = line.size(); + while (true) { + int j = i - 1; + while (j >= 0 && line.at(j) == QLatin1Char('\n')) + --j; + while (j >= 0 && line.at(j) != QLatin1Char('\n')) + --j; + result.prepend(line.mid(j + 1, i - j - 1)); + if (j < 0) + break; + i = j; + } + return result; +} + +/* + Transforms 'int x = 3 + 4' into 'int x=3+4'. A white space is kept + between 'int' and 'x' because it is meaningful in C++. +*/ +static void trimWhiteSpace( QString& str ) +{ + enum { Normal, MetAlnum, MetSpace } state = Normal; + const int n = str.length(); + + int j = -1; + QChar *d = str.data(); + for ( int i = 0; i != n; ++i ) { + const QChar c = d[i]; + if ( c.isLetterOrNumber() ) { + if ( state == Normal ) { + state = MetAlnum; + } else { + if ( state == MetSpace ) + str[++j] = c; + state = Normal; + } + str[++j] = c; + } else if ( c.isSpace() ) { + if ( state == MetAlnum ) + state = MetSpace; + } else { + state = Normal; + str[++j] = c; + } + } + str.resize(++j); +} + +Quoter::Quoter() + : silent( false ) +{ + /* We're going to hard code these delimiters: + * C++, Qt, Qt Script, Java: + //! [<id>] + * .pro files: + #! [<id>] + * .xq, .xml, .html files: + <!-- [<id>] --> + */ + commentHash["pro"] = "#!"; + commentHash["py"] = "#!"; + commentHash["html"] = "<!--"; + commentHash["qrc"] = "<!--"; + commentHash["ui"] = "<!--"; + commentHash["xml"] = "<!--"; + commentHash["xq"] = "<!--"; +} + +void Quoter::reset() +{ + silent = false; + plainLines.clear(); + markedLines.clear(); + codeLocation = Location::null; +} + +void Quoter::quoteFromFile( const QString& userFriendlyFilePath, + const QString& plainCode, + const QString& markedCode ) +{ + silent = false; + + /* + Split the source code into logical lines. Empty lines are + treated specially. Before: + + p->alpha(); + p->beta(); + + p->gamma(); + + + p->delta(); + + After: + + p->alpha(); + p->beta();\n + p->gamma();\n\n + p->delta(); + + Newlines are preserved because they affect codeLocation. + */ + codeLocation = Location( userFriendlyFilePath ); + + plainLines = splitLines(plainCode); + markedLines = splitLines(markedCode); + if (markedLines.count() != plainLines.count()) { + codeLocation.warning(tr("Something is wrong with qdoc's handling of marked code")); + markedLines = plainLines; + } + + /* + Squeeze blanks (cat -s). + */ + QStringList::Iterator m = markedLines.begin(); + while ( m != markedLines.end() ) { + replaceMultipleNewlines( *m ); + ++m; + } + codeLocation.start(); +} + +QString Quoter::quoteLine( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + if ( plainLines.isEmpty() ) { + failedAtEnd( docLocation, command ); + return QString(); + } + + if ( pattern.isEmpty() ) { + docLocation.warning( tr("Missing pattern after '\\%1'").arg(command) ); + return QString(); + } + + if ( match(docLocation, pattern, plainLines.first()) ) + return getLine(); + + if ( !silent ) { + docLocation.warning( tr("Command '\\%1' failed").arg(command) ); + codeLocation.warning( tr("Pattern '%1' didn't match here") + .arg(pattern) ); + silent = true; + } + return QString(); +} + +QString Quoter::quoteSnippet(const Location &docLocation, const QString &identifier) +{ + QString comment = commentForCode(); + QString delimiter = comment + QString(" [%1]").arg(identifier); + QString t; + + while (!plainLines.isEmpty()) { + if (match(docLocation, delimiter, plainLines.first())) { + getLine(); + break; + } + getLine(); + } + while (!plainLines.isEmpty()) { + QString line = plainLines.first(); + if (match(docLocation, delimiter, line)) { + QString lastLine = getLine(); + int dIndex = lastLine.indexOf(delimiter); + if (dIndex > 0) { + QString leading = lastLine.left(dIndex); + dIndex = leading.indexOf(comment); + if (dIndex != -1) + leading = leading.left(dIndex); + if (!leading.trimmed().isEmpty()) + t += leading; + } + return t; + } + // Remove special macros to support Qt namespacing. + if (line.startsWith("QT_BEGIN_NAMESPACE")) { + getLine(); + } else if (line.startsWith("QT_END_NAMESPACE")) { + getLine(); + t += QLatin1Char('\n'); + } else if (!line.startsWith(comment)) { + // Ordinary code + t += getLine(); + } else { + // Normal comments + if (line.contains(QLatin1Char('\n'))) + t += QLatin1Char('\n'); + getLine(); + } + } + failedAtEnd(docLocation, QString("snippet (%1)").arg(delimiter)); + return t; +} + +QString Quoter::quoteTo( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + QString t; + QString comment = commentForCode(); + + if ( pattern.isEmpty() ) { + while ( !plainLines.isEmpty() ) { + QString line = plainLines.first(); + // Remove special macros to support Qt namespacing. + if (line.startsWith("QT_BEGIN_NAMESPACE")) { + getLine(); + } else if (line.startsWith("QT_END_NAMESPACE")) { + getLine(); + t += QLatin1Char('\n'); + } else if (!line.startsWith(comment)) + // Ordinary code + t += getLine(); + else { + // Normal comments + if (line.contains(QLatin1Char('\n'))) + t += QLatin1Char('\n'); + getLine(); + } + } + } else { + while ( !plainLines.isEmpty() ) { + if ( match(docLocation, pattern, plainLines.first()) ) { + return t; + } + t += getLine(); + } + failedAtEnd( docLocation, command ); + } + return t; +} + +QString Quoter::quoteUntil( const Location& docLocation, const QString& command, + const QString& pattern ) +{ + QString t = quoteTo( docLocation, command, pattern ); + t += getLine(); + return t; +} + +QString Quoter::getLine() +{ + if ( plainLines.isEmpty() ) + return QString(); + + plainLines.removeFirst(); + + QString t = markedLines.takeFirst(); + t += QLatin1Char('\n'); + codeLocation.advanceLines( t.count( QLatin1Char('\n') ) ); + return t; +} + +bool Quoter::match( const Location& docLocation, const QString& pattern0, + const QString& line ) +{ + QString str = line; + while ( str.endsWith(QLatin1Char('\n')) ) + str.truncate( str.length() - 1 ); + + QString pattern = pattern0; + if ( pattern.startsWith(QLatin1Char('/')) + && pattern.endsWith(QLatin1Char('/')) + && pattern.length() > 2 ) { + QRegExp rx( pattern.mid(1, pattern.length() - 2) ); + if ( !silent && !rx.isValid() ) { + docLocation.warning( tr("Invalid regular expression '%1'") + .arg(rx.pattern()) ); + silent = true; + } + return str.indexOf( rx ) != -1; + } + trimWhiteSpace(str); + trimWhiteSpace(pattern); + return str.indexOf(pattern) != -1; +} + +void Quoter::failedAtEnd( const Location& docLocation, const QString& command ) +{ + if (!silent && !command.isEmpty()) { + if ( codeLocation.filePath().isEmpty() ) { + docLocation.warning( tr("Unexpected '\\%1'").arg(command) ); + } else { + docLocation.warning( tr("Command '\\%1' failed at end of file '%2'") + .arg(command).arg(codeLocation.filePath()) ); + } + silent = true; + } +} + +QString Quoter::commentForCode() const +{ + QString suffix = QFileInfo(codeLocation.fileName()).suffix(); + return commentHash.value(suffix, "//!"); +} + +QT_END_NAMESPACE |