From d08f68a54e3c79353f785901985a675dc82f8a4a Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Fri, 8 Jan 2010 14:55:11 +0100 Subject: [PATCH] Refactored the JavaScript indeter so it can be adapted to support both QML and JS. --- src/plugins/qtscripteditor/qtscripteditor.cpp | 10 +- src/plugins/qtscripteditor/qtscripteditor.pro | 5 +- .../qscripthighlighter/qscripthighlighter.pri | 4 +- .../qscripthighlighter/qscriptindenter.cpp} | 132 +++++++++++------- .../qscripthighlighter/qscriptindenter.h} | 59 ++++---- 5 files changed, 116 insertions(+), 94 deletions(-) rename src/{plugins/qtscripteditor/qtscriptindenter.cpp => shared/qscripthighlighter/qscriptindenter.cpp} (90%) rename src/{plugins/qtscripteditor/qtscriptindenter.h => shared/qscripthighlighter/qscriptindenter.h} (69%) diff --git a/src/plugins/qtscripteditor/qtscripteditor.cpp b/src/plugins/qtscripteditor/qtscripteditor.cpp index 8b38d97e7fe..837f8d49200 100644 --- a/src/plugins/qtscripteditor/qtscripteditor.cpp +++ b/src/plugins/qtscripteditor/qtscripteditor.cpp @@ -31,7 +31,8 @@ #include "qtscripteditorconstants.h" #include "qtscripthighlighter.h" #include "qtscripteditorplugin.h" -#include "qtscriptindenter.h" + +#include <qscripthighlighter/qscriptindenter.h> #include "parser/javascriptengine_p.h" #include "parser/javascriptparser_p.h" @@ -340,15 +341,12 @@ bool ScriptEditor::isElectricCharacter(const QChar &ch) const void ScriptEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar) { - const TextEditor::TextBlockIterator begin(doc->begin()); - const TextEditor::TextBlockIterator end(block.next()); - TextEditor::TabSettings ts = tabSettings(); - QtScriptIndenter indenter; + SharedTools::QScriptIndenter indenter; indenter.setTabSize(ts.m_tabSize); indenter.setIndentSize(ts.m_indentSize); - const int indent = indenter.indentForBottomLine(begin, end, typedChar); + const int indent = indenter.indentForBottomLine(doc->begin(), block.next(), typedChar); ts.indentLine(block, indent); } diff --git a/src/plugins/qtscripteditor/qtscripteditor.pro b/src/plugins/qtscripteditor/qtscripteditor.pro index f3257a52a77..301bc47b605 100644 --- a/src/plugins/qtscripteditor/qtscripteditor.pro +++ b/src/plugins/qtscripteditor/qtscripteditor.pro @@ -6,21 +6,20 @@ include(../../qtcreatorplugin.pri) include(../../plugins/coreplugin/coreplugin.pri) include(../../plugins/texteditor/texteditor.pri) include(../../shared/qscripthighlighter/qscripthighlighter.pri) -include(../../shared/indenter/indenter.pri) include(parser/parser.pri) +DEPENDPATH += ../../shared/qscripthighlighter + HEADERS += qtscripteditor.h \ qtscripteditorfactory.h \ qtscripteditorplugin.h \ qtscripthighlighter.h \ -qtscriptindenter.h \ qtscriptcodecompletion.h SOURCES += qtscripteditor.cpp \ qtscripteditorfactory.cpp \ qtscripteditorplugin.cpp \ qtscripthighlighter.cpp \ -qtscriptindenter.cpp \ qtscriptcodecompletion.cpp RESOURCES += qtscripteditor.qrc diff --git a/src/shared/qscripthighlighter/qscripthighlighter.pri b/src/shared/qscripthighlighter/qscripthighlighter.pri index a6c88c508fa..0a4112e352a 100644 --- a/src/shared/qscripthighlighter/qscripthighlighter.pri +++ b/src/shared/qscripthighlighter/qscripthighlighter.pri @@ -6,6 +6,6 @@ SOURCES += $$PWD/qscriptincrementalscanner.cpp HEADERS += $$PWD/qscriptincrementalscanner.h $$PWD/qscripthighlighter_global.h contains(QT, gui) { - SOURCES += $$PWD/qscripthighlighter.cpp - HEADERS += $$PWD/qscripthighlighter.h + SOURCES += $$PWD/qscripthighlighter.cpp $$PWD/qscriptindenter.cpp + HEADERS += $$PWD/qscripthighlighter.h $$PWD/qscriptindenter.h } diff --git a/src/plugins/qtscripteditor/qtscriptindenter.cpp b/src/shared/qscripthighlighter/qscriptindenter.cpp similarity index 90% rename from src/plugins/qtscripteditor/qtscriptindenter.cpp rename to src/shared/qscripthighlighter/qscriptindenter.cpp index 7f6bb1ae601..45f4fa2235e 100644 --- a/src/plugins/qtscripteditor/qtscriptindenter.cpp +++ b/src/shared/qscripthighlighter/qscriptindenter.cpp @@ -65,9 +65,10 @@ as comments and string literals are removed beforehand. */ -#include "qtscriptindenter.h" +#include "qscriptindenter.h" +#include <QtDebug> -using namespace QtScriptEditor::Internal; +using namespace SharedTools; /* The indenter avoids getting stuck in almost infinite loops by @@ -77,15 +78,16 @@ using namespace QtScriptEditor::Internal; For example, the indenter never considers more than BigRoof lines backwards when looking for the start of a C-style comment. */ -const int QtScriptIndenter::SmallRoof = 40; -const int QtScriptIndenter::BigRoof = 400; +const int QScriptIndenter::SmallRoof = 40; +const int QScriptIndenter::BigRoof = 400; -QtScriptIndenter::QtScriptIndenter() +QScriptIndenter::QScriptIndenter() : literal(QRegExp(QLatin1String("([\"'])(?:\\\\.|[^\\\\])*\\1"))), - label(QRegExp(QLatin1String("^\\s*((?:case\\b([^:]|::)+|[a-zA-Z_0-9]+)(?:\\s+slots)?:)(?!:)"))), + label(QRegExp(QLatin1String("^\\s*((?:case\\b([^:])+|[a-zA-Z_0-9.]+)(?:\\s+)?:)(?!:)"))), inlineCComment(QRegExp(QLatin1String("/\\*.*\\*/"))), braceX(QRegExp(QLatin1String("^\\s*\\}\\s*(?:else|catch)\\b"))), - iflikeKeyword(QRegExp(QLatin1String("\\b(?:catch|do|for|if|while|with)\\b"))) + iflikeKeyword(QRegExp(QLatin1String("\\b(?:catch|do|for|if|while|with)\\b"))), + propertylikeKeyword(QRegExp(QLatin1String("^\\s*\\b(?:property|signal|import)\\b"))) { /* @@ -123,16 +125,16 @@ QtScriptIndenter::QtScriptIndenter() inlineCComment.setMinimal(true); } -QtScriptIndenter::~QtScriptIndenter() +QScriptIndenter::~QScriptIndenter() { } -void QtScriptIndenter::setTabSize(int size) +void QScriptIndenter::setTabSize(int size) { ppHardwareTabSize = size; } -void QtScriptIndenter::setIndentSize(int size) +void QScriptIndenter::setIndentSize(int size) { ppIndentSize = size; ppContinuationIndentSize = 2 * size; @@ -142,7 +144,7 @@ void QtScriptIndenter::setIndentSize(int size) Returns the first non-space character in the string t, or QChar() if the string is made only of white space. */ -QChar QtScriptIndenter::firstNonWhiteSpace(const QString &t) +QChar QScriptIndenter::firstNonWhiteSpace(const QString &t) const { int i = 0; while (i < t.length()) { @@ -157,7 +159,7 @@ QChar QtScriptIndenter::firstNonWhiteSpace(const QString &t) Returns true if string t is made only of white space; otherwise returns false. */ -bool QtScriptIndenter::isOnlyWhiteSpace(const QString &t) +bool QScriptIndenter::isOnlyWhiteSpace(const QString &t) const { return firstNonWhiteSpace(t).isNull(); } @@ -167,7 +169,7 @@ bool QtScriptIndenter::isOnlyWhiteSpace(const QString &t) index. Column numbers and index are identical for strings that don't contain '\t's. */ -int QtScriptIndenter::columnForIndex(const QString &t, int index) +int QScriptIndenter::columnForIndex(const QString &t, int index) const { int col = 0; if (index > t.length()) @@ -186,7 +188,7 @@ int QtScriptIndenter::columnForIndex(const QString &t, int index) /* Returns the indentation size of string t. */ -int QtScriptIndenter::indentOfLine(const QString &t) +int QScriptIndenter::indentOfLine(const QString &t) const { return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t))); } @@ -197,7 +199,7 @@ int QtScriptIndenter::indentOfLine(const QString &t) provisions are taken against '\n' or '\r', which shouldn't occur in t anyway. */ -inline void QtScriptIndenter::eraseChar(QString &t, int k, QChar ch) +void QScriptIndenter::eraseChar(QString &t, int k, QChar ch) const { if (t.at(k) != QLatin1Char('\t')) t[k] = ch; @@ -207,7 +209,7 @@ inline void QtScriptIndenter::eraseChar(QString &t, int k, QChar ch) Removes some nefast constructs from a code line and returns the resulting line. */ -QString QtScriptIndenter::trimmedCodeLine(const QString &t) +QString QScriptIndenter::trimmedCodeLine(const QString &t) const { QString trimmed = t; int k; @@ -220,7 +222,7 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) continuation lines. */ k = 0; - while ((k = trimmed.indexOf(literal, k)) != -1) { + while ((k = literal.indexIn(trimmed, k)) != -1) { for (int i = 0; i < literal.matchedLength(); i++) eraseChar(trimmed, k + i, QLatin1Char('X')); k += literal.matchedLength(); @@ -231,7 +233,7 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) handled elsewhere. */ k = 0; - while ((k = trimmed.indexOf(inlineCComment, k)) != -1) { + while ((k = inlineCComment.indexIn(trimmed, k)) != -1) { for (int i = 0; i < inlineCComment.matchedLength(); i++) eraseChar(trimmed, k + i, QLatin1Char(' ')); k += inlineCComment.matchedLength(); @@ -242,9 +244,13 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) with this case: foo1: bar1; - bar2; + bar2; */ - while (trimmed.lastIndexOf(QLatin1Char(':')) != -1 && trimmed.indexOf(label) != -1) { + + bool insertSemicolon = false; + while (trimmed.lastIndexOf(QLatin1Char(':')) != -1 && label.indexIn(trimmed) != -1) { + insertSemicolon = true; + const QString cap1 = label.cap(1); int pos1 = label.pos(1); int stop = cap1.length(); @@ -254,7 +260,7 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) int i = 0; while (i < stop) { - eraseChar(trimmed, pos1 + i, QLatin1Char(' ')); + eraseChar(trimmed, pos1 + i, QLatin1Char(';')); i++; } while (i < cap1.length()) { @@ -270,6 +276,16 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) if (k != -1) trimmed.truncate(k); + const QString e = trimmed.trimmed(); + + if (insertSemicolon + || e.endsWith(QLatin1Char(',')) + || e.endsWith(QLatin1Char(']')) + || trimmed.indexOf(propertylikeKeyword) != -1) + trimmed.append(QLatin1Char(';')); + + //qDebug() << trimmed; + return trimmed; } @@ -277,7 +293,7 @@ QString QtScriptIndenter::trimmedCodeLine(const QString &t) Returns '(' if the last parenthesis is opening, ')' if it is closing, and QChar() if there are no parentheses in t. */ -inline QChar QtScriptIndenter::lastParen(const QString &t) +QChar QScriptIndenter::lastParen(const QString &t) const { int i = t.length(); while (i > 0) { @@ -292,7 +308,7 @@ inline QChar QtScriptIndenter::lastParen(const QString &t) Returns true if typedIn the same as okayCh or is null; otherwise returns false. */ -inline bool QtScriptIndenter::okay(QChar typedIn, QChar okayCh) +bool QScriptIndenter::okay(QChar typedIn, QChar okayCh) const { return typedIn == QChar() || typedIn == okayCh; } @@ -309,7 +325,7 @@ inline bool QtScriptIndenter::okay(QChar typedIn, QChar okayCh) accordingly. yyLine is cleaned from comments and other damageable constructs. Empty lines are skipped. */ -bool QtScriptIndenter::readLine() +bool QScriptIndenter::readLine() { int k; @@ -317,13 +333,13 @@ bool QtScriptIndenter::readLine() (firstNonWhiteSpace(yyLinizerState.line) == QLatin1Char('{')); do { - if (yyLinizerState.iter == yyProgram.constBegin()) { + if (yyLinizerState.iter == yyProgram.firstBlock()) { yyLinizerState.line.clear(); return false; } - --yyLinizerState.iter; - yyLinizerState.line = *yyLinizerState.iter; + yyLinizerState.iter = yyLinizerState.iter.previous(); + yyLinizerState.line = yyLinizerState.iter.text(); yyLinizerState.line = trimmedCodeLine(yyLinizerState.line); @@ -416,7 +432,7 @@ bool QtScriptIndenter::readLine() Resets the linizer to its initial state, with yyLine containing the line above the bottom line of the program. */ -void QtScriptIndenter::startLinizer() +void QScriptIndenter::startLinizer() { yyLinizerState.braceDepth = 0; yyLinizerState.inCComment = false; @@ -426,9 +442,9 @@ void QtScriptIndenter::startLinizer() yyBraceDepth = &yyLinizerState.braceDepth; yyLeftBraceFollows = &yyLinizerState.leftBraceFollows; - yyLinizerState.iter = yyProgram.constEnd(); - --yyLinizerState.iter; - yyLinizerState.line = *yyLinizerState.iter; + yyLinizerState.iter = yyProgram.lastBlock(); + yyLinizerState.iter = yyLinizerState.iter.previous(); + yyLinizerState.line = yyLinizerState.iter.text(); readLine(); } @@ -437,7 +453,7 @@ void QtScriptIndenter::startLinizer() potentially the whole line) is part of a C-style comment; otherwise returns false. */ -bool QtScriptIndenter::bottomLineStartsInCComment() +bool QScriptIndenter::bottomLineStartsInCComment() { const QLatin1String slashAster("/*"); const QLatin1String asterSlash("*/"); @@ -446,15 +462,15 @@ bool QtScriptIndenter::bottomLineStartsInCComment() We could use the linizer here, but that would slow us down terribly. We are better to trim only the code lines we need. */ - Program::const_iterator p = yyProgram.constEnd(); - --p; // skip bottom line + QTextBlock p = yyProgram.lastBlock(); + p = p.previous(); // skip bottom line for (int i = 0; i < BigRoof; i++) { - if (p == yyProgram.constBegin()) + if (p == yyProgram.firstBlock()) return false; - --p; + p = p.previous(); - const QString blockText = *p; + const QString blockText = p.text(); if (blockText.indexOf(slashAster) != -1 || blockText.indexOf(asterSlash) != -1) { const QString trimmed = trimmedCodeLine(blockText); @@ -477,7 +493,7 @@ bool QtScriptIndenter::bottomLineStartsInCComment() Essentially, we're trying to align against some text on the previous line. */ -int QtScriptIndenter::indentWhenBottomLineStartsInCComment() +int QScriptIndenter::indentWhenBottomLineStartsInCComment() { int k = yyLine->lastIndexOf(QLatin1String("/*")); if (k == -1) { @@ -521,7 +537,7 @@ int QtScriptIndenter::indentWhenBottomLineStartsInCComment() if (x) y; */ -bool QtScriptIndenter::matchBracelessControlStatement() +bool QScriptIndenter::matchBracelessControlStatement() { int delimDepth = 0; @@ -606,7 +622,7 @@ bool QtScriptIndenter::matchBracelessControlStatement() f + // unfinished continuation line g; // continuation line */ -bool QtScriptIndenter::isUnfinishedLine() +bool QScriptIndenter::isUnfinishedLine() { bool unf = false; @@ -651,7 +667,7 @@ bool QtScriptIndenter::isUnfinishedLine() Returns true if yyLine is a continuation line; otherwise returns false. */ -bool QtScriptIndenter::isContinuationLine() +bool QScriptIndenter::isContinuationLine() { bool cont = false; @@ -670,7 +686,7 @@ bool QtScriptIndenter::isContinuationLine() or other bracked left opened on a previous line, or some interesting operator such as '='. */ -int QtScriptIndenter::indentForContinuationLine() +int QScriptIndenter::indentForContinuationLine() { int braceDepth = 0; int delimDepth = 0; @@ -687,14 +703,15 @@ int QtScriptIndenter::indentForContinuationLine() switch (ch.unicode()) { case ')': - case ']': delimDepth++; break; + case ']': + braceDepth++; + break; case '}': braceDepth++; break; case '(': - case '[': delimDepth--; /* An unclosed delimiter is a good place to align at, @@ -703,6 +720,16 @@ int QtScriptIndenter::indentForContinuationLine() if (delimDepth == -1) hook = j; break; + + case '[': + braceDepth--; + /* + An unclosed delimiter is a good place to align at, + at least for some styles (including Qt's). + */ + if (braceDepth == -1) + hook = j; + break; case '{': braceDepth--; /* @@ -903,7 +930,7 @@ int QtScriptIndenter::indentForContinuationLine() accommodate people with irregular indentation schemes. A hook line near at hand is much more reliable than a remote one. */ -int QtScriptIndenter::indentForStandaloneLine() +int QScriptIndenter::indentForStandaloneLine() { for (int i = 0; i < SmallRoof; i++) { if (!*yyLeftBraceFollows) { @@ -988,7 +1015,7 @@ int QtScriptIndenter::indentForStandaloneLine() slighly more liberal if typedIn is always null. The user might be annoyed by the liberal behavior. */ -int QtScriptIndenter::indentForBottomLine(TextEditor::TextBlockIterator begin, TextEditor::TextBlockIterator end, QChar typedIn) +int QScriptIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn) { if (begin == end) return 0; @@ -996,12 +1023,11 @@ int QtScriptIndenter::indentForBottomLine(TextEditor::TextBlockIterator begin, T yyProgram = Program(begin, end); startLinizer(); - TextEditor::TextBlockIterator last = end; - --last; + const QTextBlock last = end.previous(); - const QString &bottomLine = *last; + QString bottomLine = last.text(); QChar firstCh = firstNonWhiteSpace(bottomLine); - int indent; + int indent = 0; if (bottomLineStartsInCComment()) { /* @@ -1034,9 +1060,9 @@ int QtScriptIndenter::indentForBottomLine(TextEditor::TextBlockIterator begin, T indent -= ppIndentSize; } else if (okay(typedIn, QLatin1Char(':'))) { QRegExp caseLabel( - "\\s*(?:case\\b(?:[^:]|::)+" - "|(?:default)\\s*" - ")?:.*"); + QLatin1String("\\s*(?:case\\b(?:[^:]|::)+" + "|(?:default)\\s*" + ")?:.*")); if (caseLabel.exactMatch(bottomLine)) { /* diff --git a/src/plugins/qtscripteditor/qtscriptindenter.h b/src/shared/qscripthighlighter/qscriptindenter.h similarity index 69% rename from src/plugins/qtscripteditor/qtscriptindenter.h rename to src/shared/qscripthighlighter/qscriptindenter.h index 215d15152b0..985387cfb7b 100644 --- a/src/plugins/qtscripteditor/qtscriptindenter.h +++ b/src/shared/qscripthighlighter/qscriptindenter.h @@ -30,39 +30,40 @@ #ifndef QTSCRIPTINDENTER_H #define QTSCRIPTINDENTER_H +#include <qscripthighlighter/qscripthighlighter_global.h> + #include <QtCore/QRegExp> #include <QtCore/QStringList> -#include <texteditor/textblockiterator.h> +#include <QtGui/QTextBlock> -namespace QtScriptEditor { -namespace Internal { +namespace SharedTools { -class QtScriptIndenter +class QSCRIPTHIGHLIGHTER_EXPORT QScriptIndenter { - Q_DISABLE_COPY(QtScriptIndenter) + Q_DISABLE_COPY(QScriptIndenter) public: - QtScriptIndenter(); - ~QtScriptIndenter(); + QScriptIndenter(); + ~QScriptIndenter(); void setTabSize(int size); void setIndentSize(int size); - int indentForBottomLine(TextEditor::TextBlockIterator begin, TextEditor::TextBlockIterator end, QChar typedIn); - QChar firstNonWhiteSpace(const QString &t); + int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn); + QChar firstNonWhiteSpace(const QString &t) const; private: static const int SmallRoof; static const int BigRoof; - bool isOnlyWhiteSpace(const QString &t); - int columnForIndex(const QString &t, int index); - int indentOfLine(const QString &t); - QString trimmedCodeLine(const QString &t); + bool isOnlyWhiteSpace(const QString &t) const; + int columnForIndex(const QString &t, int index) const; + int indentOfLine(const QString &t) const; + QString trimmedCodeLine(const QString &t) const; - inline void eraseChar(QString &t, int k, QChar ch); - inline QChar lastParen(const QString &t); - inline bool okay(QChar typedIn, QChar okayCh); + void eraseChar(QString &t, int k, QChar ch) const; + QChar lastParen(const QString &t) const; + bool okay(QChar typedIn, QChar okayCh) const; /* The "linizer" is a group of functions and variables to iterate @@ -103,23 +104,21 @@ private: bool inCComment; bool pendingRightBrace; QString line; - TextEditor::TextBlockIterator iter; + QTextBlock iter; }; - struct Program { - TextEditor::TextBlockIterator b, e; - typedef TextEditor::TextBlockIterator iterator; - typedef TextEditor::TextBlockIterator const_iterator; - + class Program + { + public: Program() {} - Program(TextEditor::TextBlockIterator begin, TextEditor::TextBlockIterator end) - : b(begin), e(end) {} + Program(QTextBlock begin, QTextBlock end) + : begin(begin), end(end) {} - iterator begin() const { return b; } - iterator end() const { return e; } + QTextBlock firstBlock() const { return begin; } + QTextBlock lastBlock() const { return end; } - const_iterator constBegin() const { return b; } - const_iterator constEnd() const { return e; } + private: + QTextBlock begin, end; }; Program yyProgram; @@ -135,10 +134,10 @@ private: QRegExp inlineCComment; QRegExp braceX; QRegExp iflikeKeyword; + QRegExp propertylikeKeyword; }; -} // namespace Internal -} // namespace QtScriptEditor +} // namespace SharedTools #endif // QTSCRIPTINDENTER_H -- GitLab