diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index 7b5adadcd2bc4e6de4465533deb64c346387fb5e..5a9b6228f2f61b2027f701c885768daaf8f920c9 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -19,7 +19,8 @@ HEADERS += \ $$PWD/qmljslink.h \ $$PWD/qmljscheck.h \ $$PWD/qmljsscopebuilder.h \ - $$PWD/qmljslineinfo.h + $$PWD/qmljslineinfo.h \ + $$PWD/qmljscompletioncontextfinder.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -30,7 +31,8 @@ SOURCES += \ $$PWD/qmljslink.cpp \ $$PWD/qmljscheck.cpp \ $$PWD/qmljsscopebuilder.cpp \ - $$PWD/qmljslineinfo.cpp + $$PWD/qmljslineinfo.cpp \ + $$PWD/qmljscompletioncontextfinder.cpp contains(QT, gui) { SOURCES += $$PWD/qmljsindenter.cpp diff --git a/src/libs/qmljs/qmljscompletioncontextfinder.cpp b/src/libs/qmljs/qmljscompletioncontextfinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05ea7f1fa46c1a2eae177ddef3f6dd871b9261a1 --- /dev/null +++ b/src/libs/qmljs/qmljscompletioncontextfinder.cpp @@ -0,0 +1,157 @@ +#include "qmljscompletioncontextfinder.h" + +#include <QtCore/QDebug> +#include <QtGui/QTextDocument> + +using namespace QmlJS; + +/* + Saves and restores the state of the global linizer. This enables + backtracking. + + Identical to the defines in qmljslineinfo.cpp +*/ +#define YY_SAVE() LinizerState savedState = yyLinizerState +#define YY_RESTORE() yyLinizerState = savedState + +CompletionContextFinder::CompletionContextFinder(const QTextCursor &cursor) + : m_cursor(cursor) +{ + QTextBlock lastBlock = cursor.block(); + if (lastBlock.next().isValid()) + lastBlock = lastBlock.next(); + initialize(cursor.document()->begin(), lastBlock); + + int startTokenIndex = yyLinizerState.tokens.size() - 1; + for (; startTokenIndex >= 0; --startTokenIndex) { + const Token &token = yyLinizerState.tokens.at(startTokenIndex); + if (token.end() <= cursor.positionInBlock()) + break; + } + + getQmlObjectTypeName(startTokenIndex); +} + +void CompletionContextFinder::getQmlObjectTypeName(int startTokenIndex) +{ + YY_SAVE(); + + int tokenIndex = findOpeningBrace(startTokenIndex); + if (tokenIndex != -1) { + --tokenIndex; + + // can be one of + // A.B on c.d + // A.B + + bool identifierExpected = true; + int i = tokenIndex; + forever { + if (i < 0) { + if (!readLine()) + break; + else + i = yyLinizerState.tokens.size() - 1; + } + + const Token &token = yyLinizerState.tokens.at(i); + if (!identifierExpected && token.kind == Token::Dot) { + identifierExpected = true; + } else if (token.kind == Token::Identifier) { + const QString idText = yyLine->mid(token.begin(), token.length); + if (identifierExpected) { + m_qmlObjectTypeName.prepend(idText); + identifierExpected = false; + } else if (idText == QLatin1String("on")) { + m_qmlObjectTypeName.clear(); + identifierExpected = true; + } else { + break; + } + } else { + break; + } + + --i; + } + } + + YY_RESTORE(); +} + +QStringList CompletionContextFinder::qmlObjectTypeName() const +{ + return m_qmlObjectTypeName; +} + +int CompletionContextFinder::findOpeningBrace(int startTokenIndex) +{ + YY_SAVE(); + + if (startTokenIndex == -1) + readLine(); + + Token::Kind nestedClosing; + int nestingCount = 0; + + for (int i = 0; i < BigRoof; ++i) { + if (i != 0 || startTokenIndex == -1) + startTokenIndex = yyLinizerState.tokens.size() - 1; + + for (int t = startTokenIndex; t >= 0; --t) { + const Token &token = yyLinizerState.tokens.at(t); + switch (token.kind) { + case Token::LeftBrace: + if (nestingCount > 0) { + if (nestedClosing == Token::RightBrace) + --nestingCount; + } else { + return t; + } + break; + case Token::LeftParenthesis: + if (nestingCount > 0) { + if (nestedClosing == Token::RightParenthesis) + --nestingCount; + } else { + YY_RESTORE(); + return -1; + } + break; + case Token::LeftBracket: + if (nestingCount > 0) { + if (nestedClosing == Token::RightBracket) + --nestingCount; + } else { + YY_RESTORE(); + return -1; + } + break; + + case Token::RightBrace: + case Token::RightParenthesis: + case Token::RightBracket: + if (nestingCount == 0) { + nestedClosing = token.kind; + nestingCount = 1; + } else if (nestedClosing == token.kind) { + ++nestingCount; + } + break; + + default: break; + } + } + + if (! readLine()) + break; + } + + YY_RESTORE(); + return -1; +} + +bool CompletionContextFinder::inQmlBindingRhs() +{ + return false; +} diff --git a/src/libs/qmljs/qmljscompletioncontextfinder.h b/src/libs/qmljs/qmljscompletioncontextfinder.h new file mode 100644 index 0000000000000000000000000000000000000000..0f912d1a9df058b718e566f00899fc6e73af6177 --- /dev/null +++ b/src/libs/qmljs/qmljscompletioncontextfinder.h @@ -0,0 +1,32 @@ +#ifndef QMLJSCOMPLETIONCONTEXTFINDER_H +#define QMLJSCOMPLETIONCONTEXTFINDER_H + +#include "qmljs_global.h" +#include <qmljs/qmljslineinfo.h> + +#include <QtCore/QStringList> +#include <QtGui/QTextCursor> + +namespace QmlJS { + +class QMLJS_EXPORT CompletionContextFinder : public LineInfo +{ +public: + CompletionContextFinder(const QTextCursor &cursor); + + //bool inQmlObjectDefinition(); + bool inQmlBindingRhs(); + + QStringList qmlObjectTypeName() const; + +private: + int findOpeningBrace(int startTokenIndex); + void getQmlObjectTypeName(int startTokenIndex); + + QTextCursor m_cursor; + QStringList m_qmlObjectTypeName; +}; + +} // namespace QmlJS + +#endif // QMLJSCOMPLETIONCONTEXTFINDER_H diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp index 9162f4d4fe11886a2dd44c287f99d3f181cc1e01..49b95ebc86a2e84d62f8a31d7b30bf14af43359f 100644 --- a/src/libs/qmljs/qmljsindenter.cpp +++ b/src/libs/qmljs/qmljsindenter.cpp @@ -569,14 +569,10 @@ int QmlJSIndenter::indentForStandaloneLine() */ int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn) { - if (begin == end) - return 0; - - yyProgram = Program(begin, end); - startLinizer(); - const QTextBlock last = end.previous(); + initialize(begin, last); + QString bottomLine = last.text(); QChar firstCh = firstNonWhiteSpace(bottomLine); int indent = 0; diff --git a/src/libs/qmljs/qmljslineinfo.cpp b/src/libs/qmljs/qmljslineinfo.cpp index 17113a391c02f9eefa07b20b29ee6be44ff2a42b..b43dc66599730fe40e4d582074eb87285cfd67b9 100644 --- a/src/libs/qmljs/qmljslineinfo.cpp +++ b/src/libs/qmljs/qmljslineinfo.cpp @@ -339,7 +339,6 @@ void LineInfo::startLinizer() yyLeftBraceFollows = &yyLinizerState.leftBraceFollows; yyLinizerState.iter = yyProgram.lastBlock(); - yyLinizerState.iter = yyLinizerState.iter.previous(); yyLinizerState.line = yyLinizerState.iter.text(); readLine(); } diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp index fe7eeb8916f68f5af615a717d8ee0b1e0e25b6de..6ef1bc35c9caf4ab1c86feb99fa1a81aac95b497 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -37,6 +37,7 @@ #include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljsscanner.h> #include <qmljs/qmljsevaluate.h> +#include <qmljs/qmljscompletioncontextfinder.h> #include <texteditor/basetexteditor.h> @@ -638,6 +639,12 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor) if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) || (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) { +/* + QTextCursor startPositionCursor(edit->document()); + startPositionCursor.setPosition(m_startPosition); + CompletionContextFinder contextFinder(startPositionCursor); + qDebug() << "Qml type name" << contextFinder.qmlObjectTypeName(); +*/ // It's a global completion. EnumerateProperties enumerateProperties(&context); enumerateProperties.setGlobalCompletion(true);