diff options
-rw-r--r-- | src/plugins/cppeditor/cppautocompleter.cpp | 138 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppautocompleter.h | 59 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.cpp | 86 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.h | 11 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppeditor.pro | 6 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljsautocompleter.cpp | 279 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljsautocompleter.h | 59 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljseditor.cpp | 229 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljseditor.h | 7 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljseditor.pro | 6 | ||||
-rw-r--r-- | src/plugins/texteditor/autocompleter.cpp | 106 | ||||
-rw-r--r-- | src/plugins/texteditor/autocompleter.h | 78 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor.cpp | 61 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor.h | 15 | ||||
-rw-r--r-- | src/plugins/texteditor/basetexteditor_p.h | 1 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditor.pro | 6 |
16 files changed, 759 insertions, 388 deletions
diff --git a/src/plugins/cppeditor/cppautocompleter.cpp b/src/plugins/cppeditor/cppautocompleter.cpp new file mode 100644 index 00000000000..9f1088ada6b --- /dev/null +++ b/src/plugins/cppeditor/cppautocompleter.cpp @@ -0,0 +1,138 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cppautocompleter.h" + +#include <Token.h> + +#include <cplusplus/SimpleLexer.h> +#include <cplusplus/MatchingText.h> +#include <cplusplus/BackwardsScanner.h> + +#include <QtCore/QLatin1Char> +#include <QtGui/QTextCursor> + +using namespace CppEditor; +using namespace Internal; +using namespace CPlusPlus; + +CppAutoCompleter::CppAutoCompleter() +{} + +CppAutoCompleter::~CppAutoCompleter() +{} + +bool CppAutoCompleter::doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert) const +{ + QChar ch; + + if (! textToInsert.isEmpty()) + ch = textToInsert.at(0); + + if (! (MatchingText::shouldInsertMatchingText(cursor) + || ch == QLatin1Char('\'') + || ch == QLatin1Char('"'))) + return false; + else if (isInComment(cursor)) + return false; + + return true; +} + +bool CppAutoCompleter::doContextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), + BackwardsScanner::previousBlockState(cursor.block())); + + // XXX Duplicated from CPPEditor::isInComment to avoid tokenizing twice + if (tk.isComment()) { + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + + if (pos == tk.end()) { + if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) + return false; + + const int state = cursor.block().userState() & 0xFF; + if (state > 0) + return false; + } + + if (pos < tk.end()) + return false; + } + else if (tk.is(T_STRING_LITERAL) || tk.is(T_WIDE_STRING_LITERAL) + || tk.is(T_CHAR_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL)) { + + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + if (pos <= tk.end()) + return false; + } + + return true; +} + +bool CppAutoCompleter::doIsInComment(const QTextCursor &cursor) const +{ + const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), + BackwardsScanner::previousBlockState(cursor.block())); + + if (tk.isComment()) { + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + + if (pos == tk.end()) { + if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) + return true; + + const int state = cursor.block().userState() & 0xFF; + if (state > 0) + return true; + } + + if (pos < tk.end()) + return true; + } + + return false; +} + +QString CppAutoCompleter::doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const +{ + MatchingText m; + return m.insertMatchingBrace(cursor, text, la, skippedChars); +} + +QString CppAutoCompleter::doInsertParagraphSeparator(const QTextCursor &cursor) const +{ + MatchingText m; + return m.insertParagraphSeparator(cursor); +} diff --git a/src/plugins/cppeditor/cppautocompleter.h b/src/plugins/cppeditor/cppautocompleter.h new file mode 100644 index 00000000000..0a80679a9c5 --- /dev/null +++ b/src/plugins/cppeditor/cppautocompleter.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CPPAUTOCOMPLETER_H +#define CPPAUTOCOMPLETER_H + +#include <texteditor/autocompleter.h> + +namespace CppEditor { +namespace Internal { + +class CppAutoCompleter : public TextEditor::AutoCompleter +{ +public: + CppAutoCompleter(); + virtual ~CppAutoCompleter(); + +private: + virtual bool doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert = QString()) const; + virtual bool doContextAllowsElectricCharacters(const QTextCursor &cursor) const; + virtual bool doIsInComment(const QTextCursor &cursor) const; + virtual QString doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const; + virtual QString doInsertParagraphSeparator(const QTextCursor &cursor) const; +}; + +} // Internal +} // CppEditor + +#endif // CPPAUTOCOMPLETER_H diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 74798b8b40a..ab3bc7b23a7 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -36,6 +36,7 @@ #include "cpplocalsymbols.h" #include "cppquickfixcollector.h" #include "cppqtstyleindenter.h" +#include "cppautocompleter.h" #include <AST.h> #include <Control.h> @@ -416,6 +417,7 @@ CPPEditor::CPPEditor(QWidget *parent) setCodeFoldingSupported(true); setCodeFoldingVisible(true); setIndenter(new CppQtStyleIndenter); + setAutoCompleter(new CppAutoCompleter); baseTextDocument()->setSyntaxHighlighter(new CppHighlighter); m_modelManager = CppTools::CppModelManagerInterface::instance(); @@ -1407,90 +1409,6 @@ QModelIndex CPPEditor::outlineModelIndex() return m_outlineModelIndex; } -QString CPPEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text, - QChar la, int *skippedChars) const -{ - MatchingText m; - return m.insertMatchingBrace(tc, text, la, skippedChars); -} - -QString CPPEditor::insertParagraphSeparator(const QTextCursor &tc) const -{ - MatchingText m; - return m.insertParagraphSeparator(tc); -} - - -bool CPPEditor::contextAllowsAutoParentheses(const QTextCursor &cursor, - const QString &textToInsert) const -{ - QChar ch; - - if (! textToInsert.isEmpty()) - ch = textToInsert.at(0); - - if (! (MatchingText::shouldInsertMatchingText(cursor) || ch == QLatin1Char('\'') || ch == QLatin1Char('"'))) - return false; - else if (isInComment(cursor)) - return false; - - return true; -} - -bool CPPEditor::contextAllowsElectricCharacters(const QTextCursor &cursor) const -{ - const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), BackwardsScanner::previousBlockState(cursor.block())); - - // XXX Duplicated from CPPEditor::isInComment to avoid tokenizing twice - if (tk.isComment()) { - const unsigned pos = cursor.selectionEnd() - cursor.block().position(); - - if (pos == tk.end()) { - if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) - return false; - - const int state = cursor.block().userState() & 0xFF; - if (state > 0) - return false; - } - - if (pos < tk.end()) - return false; - } - else if (tk.is(T_STRING_LITERAL) || tk.is(T_WIDE_STRING_LITERAL) - || tk.is(T_CHAR_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL)) { - - const unsigned pos = cursor.selectionEnd() - cursor.block().position(); - if (pos <= tk.end()) - return false; - } - - return true; -} - -bool CPPEditor::isInComment(const QTextCursor &cursor) const -{ - const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), BackwardsScanner::previousBlockState(cursor.block())); - - if (tk.isComment()) { - const unsigned pos = cursor.selectionEnd() - cursor.block().position(); - - if (pos == tk.end()) { - if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) - return true; - - const int state = cursor.block().userState() & 0xFF; - if (state > 0) - return true; - } - - if (pos < tk.end()) - return true; - } - - return false; -} - bool CPPEditor::event(QEvent *e) { switch (e->type()) { diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index 0989ee6c415..332fc1f0dce 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -207,17 +207,6 @@ protected: TextEditor::BaseTextEditorEditable *createEditableInterface(); - virtual QString insertMatchingBrace(const QTextCursor &tc, const QString &text, - QChar la, int *skippedChars) const; - - virtual QString insertParagraphSeparator(const QTextCursor &tc) const; - - virtual bool contextAllowsAutoParentheses(const QTextCursor &cursor, - const QString &textToInsert = QString()) const; - virtual bool contextAllowsElectricCharacters(const QTextCursor &cursor) const; - - virtual bool isInComment(const QTextCursor &cursor) const; - const CPlusPlus::Macro *findCanonicalMacro(const QTextCursor &cursor, CPlusPlus::Document::Ptr doc) const; diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 0883e69ed25..4ade5624bb6 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -23,7 +23,8 @@ HEADERS += cppplugin.h \ cpptypehierarchy.h \ cppelementevaluator.h \ cppquickfixcollector.h \ - cppqtstyleindenter.h + cppqtstyleindenter.h \ + cppautocompleter.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ cpphighlighter.cpp \ @@ -40,6 +41,7 @@ SOURCES += cppplugin.cpp \ cpptypehierarchy.cpp \ cppelementevaluator.cpp \ cppquickfixcollector.cpp \ - cppqtstyleindenter.cpp + cppqtstyleindenter.cpp \ + cppautocompleter.cpp RESOURCES += cppeditor.qrc OTHER_FILES += CppEditor.mimetypes.xml diff --git a/src/plugins/qmljseditor/qmljsautocompleter.cpp b/src/plugins/qmljseditor/qmljsautocompleter.cpp new file mode 100644 index 00000000000..fd9d686d9a7 --- /dev/null +++ b/src/plugins/qmljseditor/qmljsautocompleter.cpp @@ -0,0 +1,279 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "qmljsautocompleter.h" + +#include <qmljs/qmljsscanner.h> + +#include <QtCore/QChar> +#include <QtCore/QLatin1Char> +#include <QtGui/QTextDocument> +#include <QtGui/QTextCursor> +#include <QtGui/QTextBlock> + +using namespace QmlJSEditor; +using namespace Internal; +using namespace QmlJS; + +static int blockStartState(const QTextBlock &block) +{ + int state = block.userState(); + + if (state == -1) + return 0; + else + return state & 0xff; +} + +static Token tokenUnderCursor(const QTextCursor &cursor) +{ + const QString blockText = cursor.block().text(); + const int blockState = blockStartState(cursor.block()); + + Scanner tokenize; + const QList<Token> tokens = tokenize(blockText, blockState); + const int pos = cursor.positionInBlock(); + + int tokenIndex = 0; + for (; tokenIndex < tokens.size(); ++tokenIndex) { + const Token &token = tokens.at(tokenIndex); + + if (token.is(Token::Comment) || token.is(Token::String)) { + if (pos > token.begin() && pos <= token.end()) + break; + } else { + if (pos >= token.begin() && pos < token.end()) + break; + } + } + + if (tokenIndex != tokens.size()) + return tokens.at(tokenIndex); + + return Token(); +} + +static bool shouldInsertMatchingText(QChar lookAhead) +{ + switch (lookAhead.unicode()) { + case '{': case '}': + case ']': case ')': + case ';': case ',': + case '"': case '\'': + return true; + + default: + if (lookAhead.isSpace()) + return true; + + return false; + } // switch +} + +static bool shouldInsertMatchingText(const QTextCursor &tc) +{ + QTextDocument *doc = tc.document(); + return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd())); +} + +static bool shouldInsertNewline(const QTextCursor &tc) +{ + QTextDocument *doc = tc.document(); + int pos = tc.selectionEnd(); + + // count the number of empty lines. + int newlines = 0; + for (int e = doc->characterCount(); pos != e; ++pos) { + const QChar ch = doc->characterAt(pos); + + if (! ch.isSpace()) + break; + else if (ch == QChar::ParagraphSeparator) + ++newlines; + } + + if (newlines <= 1 && doc->characterAt(pos) != QLatin1Char('}')) + return true; + + return false; +} + +static bool isCompleteStringLiteral(const QStringRef &text) +{ + if (text.length() < 2) + return false; + + const QChar quote = text.at(0); + + if (text.at(text.length() - 1) == quote) + return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly. + + return false; +} + +AutoCompleter::AutoCompleter() +{} + +AutoCompleter::~AutoCompleter() +{} + +bool AutoCompleter::doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert) const +{ + QChar ch; + + if (! textToInsert.isEmpty()) + ch = textToInsert.at(0); + + switch (ch.unicode()) { + case '\'': + case '"': + + case '(': + case '[': + case '{': + + case ')': + case ']': + case '}': + + case ';': + break; + + default: + if (ch.isNull()) + break; + + return false; + } // end of switch + + const Token token = tokenUnderCursor(cursor); + switch (token.kind) { + case Token::Comment: + return false; + + case Token::String: { + const QString blockText = cursor.block().text(); + const QStringRef tokenText = blockText.midRef(token.offset, token.length); + const QChar quote = tokenText.at(0); + + if (ch != quote || isCompleteStringLiteral(tokenText)) + break; + + return false; + } + + default: + break; + } // end of switch + + return true; +} + +bool AutoCompleter::doContextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + Token token = tokenUnderCursor(cursor); + switch (token.kind) { + case Token::Comment: + case Token::String: + return false; + default: + return true; + } +} + +bool AutoCompleter::doIsInComment(const QTextCursor &cursor) const +{ + return tokenUnderCursor(cursor).is(Token::Comment); +} + +QString AutoCompleter::doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar, + int *skippedChars) const +{ + if (text.length() != 1) + return QString(); + + if (! shouldInsertMatchingText(cursor)) + return QString(); + + const QChar la = cursor.document()->characterAt(cursor.position()); + + const QChar ch = text.at(0); + switch (ch.unicode()) { + case '\'': + if (la != ch) + return QString(ch); + ++*skippedChars; + break; + + case '"': + if (la != ch) + return QString(ch); + ++*skippedChars; + break; + + case '(': + return QString(QLatin1Char(')')); + + case '[': + return QString(QLatin1Char(']')); + + case '{': + return QString(); // nothing to do. + + case ')': + case ']': + case '}': + case ';': + if (la == ch) + ++*skippedChars; + break; + + default: + break; + } // end of switch + + return QString(); +} + +QString AutoCompleter::doInsertParagraphSeparator(const QTextCursor &cursor) const +{ + if (shouldInsertNewline(cursor)) { + QTextCursor cursor = cursor; + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + if (! cursor.selectedText().trimmed().isEmpty()) + return QString(); + + return QLatin1String("}\n"); + } + + return QLatin1String("}"); +} diff --git a/src/plugins/qmljseditor/qmljsautocompleter.h b/src/plugins/qmljseditor/qmljsautocompleter.h new file mode 100644 index 00000000000..7e6b2bab03f --- /dev/null +++ b/src/plugins/qmljseditor/qmljsautocompleter.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef QMLJSAUTOCOMPLETER_H +#define QMLJSAUTOCOMPLETER_H + +#include <texteditor/autocompleter.h> + +namespace QmlJSEditor { +namespace Internal { + +class AutoCompleter : public TextEditor::AutoCompleter +{ +public: + AutoCompleter(); + virtual ~AutoCompleter(); + +private: + virtual bool doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert = QString()) const; + virtual bool doContextAllowsElectricCharacters(const QTextCursor &cursor) const; + virtual bool doIsInComment(const QTextCursor &cursor) const; + virtual QString doInsertMatchingBrace(const QTextCursor &tc, + const QString &text, + QChar la, + int *skippedChars) const; + virtual QString doInsertParagraphSeparator(const QTextCursor &tc) const; +}; + +} // Internal +} // QmlJSEditor + +#endif // QMLJSAUTOCOMPLETER_H diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index f40858c3edc..c9a3afc3a58 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -39,6 +39,7 @@ #include "qmljsfindreferences.h" #include "qmljssemantichighlighter.h" #include "qmljsindenter.h" +#include "qmljsautocompleter.h" #include <qmljs/qmljsbind.h> #include <qmljs/qmljsdocument.h> @@ -91,39 +92,6 @@ using namespace QmlJS::AST; using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; -static int blockStartState(const QTextBlock &block) -{ - int state = block.userState(); - - if (state == -1) - return 0; - else - return state & 0xff; -} - -static bool shouldInsertMatchingText(QChar lookAhead) -{ - switch (lookAhead.unicode()) { - case '{': case '}': - case ']': case ')': - case ';': case ',': - case '"': case '\'': - return true; - - default: - if (lookAhead.isSpace()) - return true; - - return false; - } // switch -} - -static bool shouldInsertMatchingText(const QTextCursor &tc) -{ - QTextDocument *doc = tc.document(); - return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd())); -} - namespace { class FindIdDeclarations: protected Visitor @@ -647,6 +615,7 @@ QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) : setCodeFoldingSupported(true); setCodeFoldingVisible(true); setIndenter(new Indenter); + setAutoCompleter(new AutoCompleter); m_updateDocumentTimer = new QTimer(this); m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL); @@ -1504,200 +1473,6 @@ void QmlJSTextEditor::unCommentSelection() Utils::unCommentSelection(this); } -static bool isCompleteStringLiteral(const QStringRef &text) -{ - if (text.length() < 2) - return false; - - const QChar quote = text.at(0); - - if (text.at(text.length() - 1) == quote) - return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly. - - return false; -} - -static Token tokenUnderCursor(const QTextCursor &cursor) -{ - const QString blockText = cursor.block().text(); - const int blockState = blockStartState(cursor.block()); - - Scanner tokenize; - const QList<Token> tokens = tokenize(blockText, blockState); - const int pos = cursor.positionInBlock(); - - int tokenIndex = 0; - for (; tokenIndex < tokens.size(); ++tokenIndex) { - const Token &token = tokens.at(tokenIndex); - - if (token.is(Token::Comment) || token.is(Token::String)) { - if (pos > token.begin() && pos <= token.end()) - break; - } else { - if (pos >= token.begin() && pos < token.end()) - break; - } - } - - if (tokenIndex != tokens.size()) - return tokens.at(tokenIndex); - - return Token(); -} - -bool QmlJSTextEditor::contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert) const -{ - QChar ch; - - if (! textToInsert.isEmpty()) - ch = textToInsert.at(0); - - switch (ch.unicode()) { - case '\'': - case '"': - - case '(': - case '[': - case '{': - - case ')': - case ']': - case '}': - - case ';': - break; - - default: - if (ch.isNull()) - break; - - return false; - } // end of switch - - const Token token = tokenUnderCursor(cursor); - switch (token.kind) { - case Token::Comment: - return false; - - case Token::String: { - const QString blockText = cursor.block().text(); - const QStringRef tokenText = blockText.midRef(token.offset, token.length); - const QChar quote = tokenText.at(0); - - if (ch != quote || isCompleteStringLiteral(tokenText)) - break; - - return false; - } - - default: - break; - } // end of switch - - return true; -} - -bool QmlJSTextEditor::contextAllowsElectricCharacters(const QTextCursor &cursor) const -{ - Token token = tokenUnderCursor(cursor); - switch (token.kind) { - case Token::Comment: - case Token::String: - return false; - default: - return true; - } -} - -bool QmlJSTextEditor::isInComment(const QTextCursor &cursor) const -{ - return tokenUnderCursor(cursor).is(Token::Comment); -} - -QString QmlJSTextEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text, QChar, int *skippedChars) const -{ - if (text.length() != 1) - return QString(); - - if (! shouldInsertMatchingText(tc)) - return QString(); - - const QChar la = characterAt(tc.position()); - - const QChar ch = text.at(0); - switch (ch.unicode()) { - case '\'': - if (la != ch) - return QString(ch); - ++*skippedChars; - break; - - case '"': - if (la != ch) - return QString(ch); - ++*skippedChars; - break; - - case '(': - return QString(QLatin1Char(')')); - - case '[': - return QString(QLatin1Char(']')); - - case '{': - return QString(); // nothing to do. - - case ')': - case ']': - case '}': - case ';': - if (la == ch) - ++*skippedChars; - break; - - default: - break; - } // end of switch - - return QString(); -} - -static bool shouldInsertNewline(const QTextCursor &tc) -{ - QTextDocument *doc = tc.document(); - int pos = tc.selectionEnd(); - - // count the number of empty lines. - int newlines = 0; - for (int e = doc->characterCount(); pos != e; ++pos) { - const QChar ch = doc->characterAt(pos); - - if (! ch.isSpace()) - break; - else if (ch == QChar::ParagraphSeparator) - ++newlines; - } - - if (newlines <= 1 && doc->characterAt(pos) != QLatin1Char('}')) - return true; - - return false; -} - -QString QmlJSTextEditor::insertParagraphSeparator(const QTextCursor &tc) const -{ - if (shouldInsertNewline(tc)) { - QTextCursor cursor = tc; - cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - if (! cursor.selectedText().trimmed().isEmpty()) - return QString(); - - return QLatin1String("}\n"); - } - - return QLatin1String("}"); -} - void QmlJSTextEditor::forceSemanticRehighlight() { m_semanticHighlighter->rehighlight(currentSource(/* force = */ true)); diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index ec7151a56aa..6392036444f 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -202,13 +202,6 @@ protected: void createToolBar(Internal::QmlJSEditorEditable *editable); TextEditor::BaseTextEditor::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true); - //// brace matching - virtual bool contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert = QString()) const; - virtual bool contextAllowsElectricCharacters(const QTextCursor &cursor) const; - virtual bool isInComment(const QTextCursor &cursor) const; - virtual QString insertMatchingBrace(const QTextCursor &tc, const QString &text, QChar la, int *skippedChars) const; - virtual QString insertParagraphSeparator(const QTextCursor &tc) const; - private: bool isClosingBrace(const QList<QmlJS::Token> &tokens) const; diff --git a/src/plugins/qmljseditor/qmljseditor.pro b/src/plugins/qmljseditor/qmljseditor.pro index 7093b3ec0eb..1d66b2f958c 100644 --- a/src/plugins/qmljseditor/qmljseditor.pro +++ b/src/plugins/qmljseditor/qmljseditor.pro @@ -35,7 +35,8 @@ HEADERS += \ qmljsfindreferences.h \ qmljseditoreditable.h \ qmljssemantichighlighter.h \ - qmljsindenter.h + qmljsindenter.h \ + qmljsautocompleter.h SOURCES += \ qmljscodecompletion.cpp \ @@ -64,7 +65,8 @@ SOURCES += \ qmljsfindreferences.cpp \ qmljseditoreditable.cpp \ qmljssemantichighlighter.cpp \ - qmljsindenter.cpp + qmljsindenter.cpp \ + qmljsautocompleter.cpp RESOURCES += qmljseditor.qrc OTHER_FILES += QmlJSEditor.mimetypes.xml diff --git a/src/plugins/texteditor/autocompleter.cpp b/src/plugins/texteditor/autocompleter.cpp new file mode 100644 index 00000000000..92ce3161a40 --- /dev/null +++ b/src/plugins/texteditor/autocompleter.cpp @@ -0,0 +1,106 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "autocompleter.h" + +#include <QtGui/QTextCursor> + +using namespace TextEditor; + +AutoCompleter::AutoCompleter() +{} + +AutoCompleter::~AutoCompleter() +{} + +bool AutoCompleter::contextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert) const +{ + return doContextAllowsAutoParentheses(cursor, textToInsert); +} + +bool AutoCompleter::contextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + return doContextAllowsElectricCharacters(cursor); +} + +bool AutoCompleter::isInComment(const QTextCursor &cursor) const +{ + return doIsInComment(cursor); +} + +QString AutoCompleter::insertMatchingBrace(const QTextCursor &cursor, const + QString &text, + QChar la, + int *skippedChars) const +{ + return doInsertMatchingBrace(cursor, text, la, skippedChars); +} + +QString AutoCompleter::insertParagraphSeparator(const QTextCursor &cursor) const +{ + return doInsertParagraphSeparator(cursor); +} + +bool AutoCompleter::doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert) const +{ + Q_UNUSED(cursor); + Q_UNUSED(textToInsert); + return false; +} + +bool AutoCompleter::doContextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + return doContextAllowsAutoParentheses(cursor); +} + +bool AutoCompleter::doIsInComment(const QTextCursor &cursor) const +{ + Q_UNUSED(cursor); + return false; +} + +QString AutoCompleter::doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const +{ + Q_UNUSED(cursor); + Q_UNUSED(text); + Q_UNUSED(la); + Q_UNUSED(skippedChars); + return QString(); +} + +QString AutoCompleter::doInsertParagraphSeparator(const QTextCursor &cursor) const +{ + Q_UNUSED(cursor); + return QString(); +} diff --git a/src/plugins/texteditor/autocompleter.h b/src/plugins/texteditor/autocompleter.h new file mode 100644 index 00000000000..3b49d499a4e --- /dev/null +++ b/src/plugins/texteditor/autocompleter.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef AUTOCOMPLETER_H +#define AUTOCOMPLETER_H + +#include "texteditor_global.h" + +#include <QtCore/QChar> +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE +class QTextCursor; +QT_END_NAMESPACE + +namespace TextEditor { + +class TEXTEDITOR_EXPORT AutoCompleter +{ +public: + AutoCompleter(); + virtual ~AutoCompleter(); + + bool contextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert = QString()) const; + bool contextAllowsElectricCharacters(const QTextCursor &cursor) const; + + // Returns true if the cursor is inside a comment. + bool isInComment(const QTextCursor &cursor) const; + + QString insertMatchingBrace(const QTextCursor &cursor, const + QString &text, + QChar la, + int *skippedChars) const; + // Returns the text that needs to be inserted + QString insertParagraphSeparator(const QTextCursor &cursor) const; + +private: + virtual bool doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert = QString()) const; + virtual bool doContextAllowsElectricCharacters(const QTextCursor &cursor) const; + virtual bool doIsInComment(const QTextCursor &cursor) const; + virtual QString doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const; + virtual QString doInsertParagraphSeparator(const QTextCursor &cursor) const; +}; + +} // TextEditor + +#endif // AUTOCOMPLETER_H diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 7457634afdf..f2fe088e6c5 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -43,6 +43,7 @@ #include "tooltip.h" #include "tipcontents.h" #include "indenter.h" +#include "autocompleter.h" #include <aggregation/aggregate.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -1364,7 +1365,7 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) cursor.insertText(autoText); cursor.setPosition(pos); } - if (!electricChar.isNull() && contextAllowsElectricCharacters(cursor)) + if (!electricChar.isNull() && d->m_autoCompleter->contextAllowsElectricCharacters(cursor)) indent(document(), cursor, electricChar); if (doEditBlock) @@ -1889,6 +1890,16 @@ void BaseTextEditor::setIndenter(Indenter *indenter) d->m_indenter.reset(indenter); } +void BaseTextEditor::setAutoCompleter(AutoCompleter *autoCompleter) +{ + d->m_autoCompleter.reset(autoCompleter); +} + +AutoCompleter *BaseTextEditor::autoCompleter() const +{ + return d->m_autoCompleter.data(); +} + //--------- BaseTextEditorPrivate ----------- BaseTextEditorPrivate::BaseTextEditorPrivate() @@ -1937,7 +1948,8 @@ BaseTextEditorPrivate::BaseTextEditorPrivate() m_requestAutoCompletionRevision(0), m_requestAutoCompletionPosition(0), m_requestAutoCompletionTimer(0), - m_cursorBlockNumber(-1) + m_cursorBlockNumber(-1), + m_autoCompleter(new AutoCompleter) { } @@ -4051,41 +4063,6 @@ void BaseTextEditor::countBrackets(QTextCursor cursor, int from, int end, QChar } } -bool BaseTextEditor::contextAllowsAutoParentheses(const QTextCursor &cursor, - const QString &textToInsert) const -{ - Q_UNUSED(cursor); - Q_UNUSED(textToInsert); - return false; -} - -bool BaseTextEditor::contextAllowsElectricCharacters(const QTextCursor &cursor) const -{ - return contextAllowsAutoParentheses(cursor); -} - -bool BaseTextEditor::isInComment(const QTextCursor &cursor) const -{ - Q_UNUSED(cursor); - return false; -} - -QString BaseTextEditor::insertMatchingBrace(const QTextCursor &tc, const QString &text, - QChar la, int *skippedChars) const -{ - Q_UNUSED(tc); - Q_UNUSED(text); - Q_UNUSED(la); - Q_UNUSED(skippedChars); - return QString(); -} - -QString BaseTextEditor::insertParagraphSeparator(const QTextCursor &tc) const -{ - Q_UNUSED(tc); - return QString(); -} - QString BaseTextEditor::autoComplete(QTextCursor &cursor, const QString &textToInsert) const { const bool checkBlockEnd = d->m_allowSkippingOfBlockEnd; @@ -4094,7 +4071,7 @@ QString BaseTextEditor::autoComplete(QTextCursor &cursor, const QString &textToI if (!d->m_autoParenthesesEnabled) return QString(); - if (!contextAllowsAutoParentheses(cursor, textToInsert)) + if (!d->m_autoCompleter->contextAllowsAutoParentheses(cursor, textToInsert)) return QString(); const QString text = textToInsert; @@ -4128,7 +4105,7 @@ QString BaseTextEditor::autoComplete(QTextCursor &cursor, const QString &textToI } int skippedChars = 0; - const QString autoText = insertMatchingBrace(cursor, text, lookAhead, &skippedChars); + const QString autoText = d->m_autoCompleter->insertMatchingBrace(cursor, text, lookAhead, &skippedChars); if (checkBlockEnd && textToInsert.at(0) == QLatin1Char('}')) { if (textToInsert.length() > 1) @@ -4200,7 +4177,7 @@ bool BaseTextEditor::autoBackspace(QTextCursor &cursor) && lookFurtherBehind != QLatin1Char('\\')) || (lookBehind == QLatin1Char('\'') && lookAhead == QLatin1Char('\'') && lookFurtherBehind != QLatin1Char('\\'))) { - if (! isInComment(c)) { + if (! d->m_autoCompleter->isInComment(c)) { cursor.beginEditBlock(); cursor.deleteChar(); cursor.deletePreviousChar(); @@ -4219,7 +4196,7 @@ int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor) if (characterAt(cursor.position() - 1) != QLatin1Char('{')) return 0; - if (!contextAllowsAutoParentheses(cursor)) + if (!d->m_autoCompleter->contextAllowsAutoParentheses(cursor)) return 0; // verify that we indeed do have an extra opening brace in the document @@ -4253,7 +4230,7 @@ int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor) int pos = cursor.position(); - const QString textToInsert = insertParagraphSeparator(cursor); + const QString textToInsert = d->m_autoCompleter->insertParagraphSeparator(cursor); cursor.insertText(textToInsert); cursor.setPosition(pos); diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 77caa0ff1c3..ba2c4d1d4c4 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -69,6 +69,7 @@ class CompletionSettings; class DisplaySettings; class StorageSettings; class Indenter; +class AutoCompleter; class TEXTEDITOR_EXPORT BaseTextEditorAnimator : public QObject { @@ -225,6 +226,9 @@ public: void setIndenter(Indenter *indenter); + void setAutoCompleter(AutoCompleter *autoCompleter); + AutoCompleter *autoCompleter() const; + public slots: void setDisplayName(const QString &title); @@ -429,17 +433,6 @@ public: // Reindent at cursor. Selection will be adjusted according to the indentation change of the first block virtual void reindent(QTextDocument *doc, const QTextCursor &cursor); - virtual bool contextAllowsAutoParentheses(const QTextCursor &cursor, const QString &textToInsert = QString()) const; - virtual bool contextAllowsElectricCharacters(const QTextCursor &cursor) const; - - // Returns true if the cursor is inside a comment. - virtual bool isInComment(const QTextCursor &cursor) const; - - virtual QString insertMatchingBrace(const QTextCursor &tc, const QString &text, QChar la, int *skippedChars) const; - - // Returns the text that needs to be inserted - virtual QString insertParagraphSeparator(const QTextCursor &tc) const; - protected: static void countBracket(QChar open, QChar close, QChar c, int *errors, int *stillopen); diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index bc6a3365eae..876e28a6aec 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -294,6 +294,7 @@ public: QPointer<BaseTextEditorAnimator> m_animator; int m_cursorBlockNumber; + QScopedPointer<AutoCompleter> m_autoCompleter; QScopedPointer<Indenter> m_indenter; }; diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 3b84f9c979c..43ba19013a8 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -70,7 +70,8 @@ SOURCES += texteditorplugin.cpp \ tooltip/tipfactory.cpp \ basehoverhandler.cpp \ helpitem.cpp \ - snippetsparser.cpp + snippetsparser.cpp \ + autocompleter.cpp HEADERS += texteditorplugin.h \ textfilewizard.h \ @@ -144,7 +145,8 @@ HEADERS += texteditorplugin.h \ tooltip/tipfactory.h \ basehoverhandler.h \ helpitem.h \ - snippetsparser.h + snippetsparser.h \ + autocompleter.h FORMS += behaviorsettingspage.ui \ displaysettingspage.ui \ |