1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "qmljsautocompleter.h"
36 #include <qmljs/qmljsscanner.h>
38 #include <QtCore/QChar>
39 #include <QtCore/QLatin1Char>
40 #include <QtGui/QTextDocument>
41 #include <QtGui/QTextCursor>
42 #include <QtGui/QTextBlock>
44 using namespace QmlJSEditor;
45 using namespace Internal;
46 using namespace QmlJS;
48 static int blockStartState(const QTextBlock &block)
50 int state = block.previous().userState();
58 static Token tokenUnderCursor(const QTextCursor &cursor)
60 const QString blockText = cursor.block().text();
61 const int blockState = blockStartState(cursor.block());
64 const QList<Token> tokens = tokenize(blockText, blockState);
65 const int pos = cursor.positionInBlock();
68 for (; tokenIndex < tokens.size(); ++tokenIndex) {
69 const Token &token = tokens.at(tokenIndex);
71 if (token.is(Token::Comment) || token.is(Token::String)) {
72 if (pos > token.begin() && pos <= token.end())
75 if (pos >= token.begin() && pos < token.end())
80 if (tokenIndex != tokens.size())
81 return tokens.at(tokenIndex);
86 static bool shouldInsertMatchingText(QChar lookAhead)
88 switch (lookAhead.unicode()) {
96 if (lookAhead.isSpace())
103 static bool shouldInsertMatchingText(const QTextCursor &tc)
105 QTextDocument *doc = tc.document();
106 return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd()));
109 static bool shouldInsertNewline(const QTextCursor &tc)
111 QTextDocument *doc = tc.document();
112 int pos = tc.selectionEnd();
114 // count the number of empty lines.
116 for (int e = doc->characterCount(); pos != e; ++pos) {
117 const QChar ch = doc->characterAt(pos);
121 else if (ch == QChar::ParagraphSeparator)
125 if (newlines <= 1 && doc->characterAt(pos) != QLatin1Char('}'))
131 static bool isCompleteStringLiteral(const QStringRef &text)
133 if (text.length() < 2)
136 const QChar quote = text.at(0);
138 if (text.at(text.length() - 1) == quote)
139 return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly.
144 AutoCompleter::AutoCompleter()
147 AutoCompleter::~AutoCompleter()
150 bool AutoCompleter::contextAllowsAutoParentheses(const QTextCursor &cursor,
151 const QString &textToInsert) const
155 if (! textToInsert.isEmpty())
156 ch = textToInsert.at(0);
158 switch (ch.unicode()) {
180 const Token token = tokenUnderCursor(cursor);
181 switch (token.kind) {
185 case Token::String: {
186 const QString blockText = cursor.block().text();
187 const QStringRef tokenText = blockText.midRef(token.offset, token.length);
188 QChar quote = tokenText.at(0);
189 // if a string literal doesn't start with a quote, it must be multiline
190 if (quote != QLatin1Char('"') && quote != QLatin1Char('\'')) {
191 const int startState = blockStartState(cursor.block());
192 if (startState == Scanner::MultiLineStringDQuote)
193 quote = QLatin1Char('"');
194 else if (startState == Scanner::MultiLineStringSQuote)
195 quote = QLatin1Char('\'');
198 // never insert ' into string literals, it adds spurious ' when writing contractions
199 if (ch == QLatin1Char('\''))
202 if (ch != quote || isCompleteStringLiteral(tokenText))
215 bool AutoCompleter::contextAllowsElectricCharacters(const QTextCursor &cursor) const
217 Token token = tokenUnderCursor(cursor);
218 switch (token.kind) {
227 bool AutoCompleter::isInComment(const QTextCursor &cursor) const
229 return tokenUnderCursor(cursor).is(Token::Comment);
232 QString AutoCompleter::insertMatchingBrace(const QTextCursor &cursor,
235 int *skippedChars) const
237 if (text.length() != 1)
240 if (! shouldInsertMatchingText(cursor))
243 const QChar la = cursor.document()->characterAt(cursor.position());
245 const QChar ch = text.at(0);
246 switch (ch.unicode()) {
260 return QString(QLatin1Char(')'));
263 return QString(QLatin1Char(']'));
266 return QString(); // nothing to do.
283 QString AutoCompleter::insertParagraphSeparator(const QTextCursor &cursor) const
285 if (shouldInsertNewline(cursor)) {
286 QTextCursor selCursor = cursor;
287 selCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
288 if (! selCursor.selectedText().trimmed().isEmpty())
291 return QLatin1String("}\n");
294 return QLatin1String("}");