diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 1389d4d01d7cb24b2481e7bb91dd0bee0c867cf2..e04fee48162c2e6ad181509fb0ab63de5289ac98 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -166,7 +166,9 @@ public: QmlObjectValue::QmlObjectValue(const QMetaObject *metaObject, const QString &qmlTypeName, int majorVersion, int minorVersion, Engine *engine) : ObjectValue(engine), _metaObject(metaObject), _qmlTypeName(qmlTypeName), _majorVersion(majorVersion), _minorVersion(minorVersion) -{} +{ + setClassName(qmlTypeName); // ### TODO: we probably need to do more than just this... +} QmlObjectValue::~QmlObjectValue() {} diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index d3f41ae66a6abe9266303c96de1aeb270c94627c..fba3a742ef8cbbb0d94ac6234336fa70bc0faf1b 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "qmlcodecompletion.h" +#include "qmlexpressionundercursor.h" #include "qmljseditor.h" #include "qmlmodelmanagerinterface.h" #include "qmllookupcontext.h" @@ -97,77 +98,6 @@ static QIcon iconForColor(const QColor &color) namespace { -class ExpressionUnderCursor -{ - QTextCursor _cursor; - QmlJSScanner scanner; - -public: - QString operator()(const QTextCursor &cursor) - { - _cursor = cursor; - - QTextBlock block = _cursor.block(); - const QString blockText = block.text().left(cursor.columnNumber()); - //qDebug() << "block text:" << blockText; - - int startState = block.previous().userState(); - if (startState == -1) - startState = 0; - else - startState = startState & 0xff; - - const QList<Token> originalTokens = scanner(blockText, startState); - QList<Token> tokens; - int skipping = 0; - for (int index = originalTokens.size() - 1; index != -1; --index) { - const Token &tk = originalTokens.at(index); - - if (tk.is(Token::Comment) || tk.is(Token::String) || tk.is(Token::Number)) - continue; - - if (! skipping) { - tokens.append(tk); - - if (tk.is(Token::Identifier)) { - if (index > 0 && originalTokens.at(index - 1).isNot(Token::Dot)) - break; - } - } else { - //qDebug() << "skip:" << blockText.mid(tk.offset, tk.length); - } - - if (tk.is(Token::RightParenthesis) || tk.is(Token::RightBracket)) - ++skipping; - - else if (tk.is(Token::LeftParenthesis) || tk.is(Token::LeftBracket)) { - --skipping; - - if (! skipping) - tokens.append(tk); - - if (index > 0 && originalTokens.at(index - 1).isNot(Token::Identifier)) - break; - } - } - - if (! tokens.isEmpty()) { - QString expr; - for (int index = tokens.size() - 1; index >= 0; --index) { - Token tk = tokens.at(index); - expr.append(QLatin1Char(' ')); - expr.append(blockText.midRef(tk.offset, tk.length)); - } - - //qDebug() << "expression under cursor:" << expr; - return expr; - } - - //qDebug() << "no expression"; - return QString(); - } -}; - class SearchPropertyDefinitions: protected AST::Visitor { QList<AST::UiPublicMember *> _properties; @@ -714,20 +644,15 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) QTextCursor tc = edit->textCursor(); tc.setPosition(m_startPosition - 1); - ExpressionUnderCursor expressionUnderCursor; - const QString expression = expressionUnderCursor(tc); + QmlExpressionUnderCursor expressionUnderCursor; + QmlJS::AST::ExpressionNode *expression = expressionUnderCursor(tc); //qDebug() << "expression:" << expression; - // Wrap the expression in a QML document. - QmlJS::Document::Ptr exprDoc = Document::create(QLatin1String("<expression>")); - exprDoc->setSource(expression); - exprDoc->parseExpression(); - - if (exprDoc->expression() != 0) { + if (expression != 0) { Check evaluate(&interp); // Evaluate the expression under cursor. - const Interpreter::Value *value = interp.convertToObject(evaluate(exprDoc->expression(), scope)); + const Interpreter::Value *value = interp.convertToObject(evaluate(expression , scope)); //qDebug() << "type:" << interp.typeId(value); if (value && completionOperator == QLatin1Char('.')) { // member completion @@ -744,10 +669,10 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } else if (value && completionOperator == QLatin1Char('(') && m_startPosition == editor->position()) { // function completion if (const Interpreter::FunctionValue *f = value->asFunctionValue()) { - QString functionName = expression; - int indexOfDot = expression.lastIndexOf(QLatin1Char('.')); + QString functionName = expressionUnderCursor.text(); + int indexOfDot = functionName.lastIndexOf(QLatin1Char('.')); if (indexOfDot != -1) - functionName = expression.mid(indexOfDot + 1); + functionName = functionName.mid(indexOfDot + 1); // Recreate if necessary if (!m_functionArgumentWidget) diff --git a/src/plugins/qmljseditor/qmlexpressionundercursor.cpp b/src/plugins/qmljseditor/qmlexpressionundercursor.cpp index ec56f3d3e16691d51a0f919d75930044880ec9e3..30fff086c9cc16abaf5262d8e2d2ffa47fa633b6 100644 --- a/src/plugins/qmljseditor/qmlexpressionundercursor.cpp +++ b/src/plugins/qmljseditor/qmlexpressionundercursor.cpp @@ -30,11 +30,9 @@ #include "qmlexpressionundercursor.h" #include <qmljs/parser/qmljsast_p.h> -#include <qmljs/parser/qmljsastvisitor_p.h> -#include <qmljs/parser/qmljsengine_p.h> -#include <qmljs/parser/qmljslexer_p.h> -#include <qmljs/parser/qmljsnodepool_p.h> -#include <qmljs/parser/qmljsparser_p.h> +#include <qmljs/qmljsscanner.h> + +#include <QtGui/QTextBlock> #include <QDebug> @@ -43,162 +41,84 @@ using namespace QmlJS::AST; namespace QmlJSEditor { namespace Internal { - class PositionCalculator: protected Visitor - { - public: - Node *operator()(Node *ast, int pos) - { - _pos = pos; - _expression = 0; - _offset = -1; - _length = -1; - - Node::accept(ast, this); - return _expression; - } + class ExpressionUnderCursor + { + QTextCursor _cursor; + QmlJSScanner scanner; - int offset() const - { return _offset; } + public: + ExpressionUnderCursor() + : start(0), end(0) + {} - int length() const - { return _length; } + int start, end; - protected: - bool visit(FieldMemberExpression *ast) + QString operator()(const QTextCursor &cursor) { - if (ast->identifierToken.offset <= _pos && _pos <= ast->identifierToken.end()) { - _expression = ast; - _offset = ast->identifierToken.offset; - _length = ast->identifierToken.length; - } + _cursor = cursor; - return true; - } - - bool visit(IdentifierExpression *ast) - { - if (ast->firstSourceLocation().offset <= _pos && _pos <= ast->lastSourceLocation().end()) { - _expression = ast; - _offset = ast->firstSourceLocation().offset; - _length = ast->lastSourceLocation().end() - _offset; - } + QTextBlock block = _cursor.block(); + const QString blockText = block.text().left(cursor.columnNumber()); + //qDebug() << "block text:" << blockText; - return false; - } + int startState = block.previous().userState(); + if (startState == -1) + startState = 0; + else + startState = startState & 0xff; - bool visit(UiImport * /*ast*/) - { - return false; - } + const QList<Token> originalTokens = scanner(blockText, startState); + QList<Token> tokens; + int skipping = 0; + for (int index = originalTokens.size() - 1; index != -1; --index) { + const Token &tk = originalTokens.at(index); - bool visit(UiQualifiedId *ast) - { - if (ast->identifierToken.offset <= _pos) { - for (UiQualifiedId *iter = ast; iter; iter = iter->next) { - if (_pos <= iter->identifierToken.end()) { - // found it - _expression = ast; - _offset = ast->identifierToken.offset; + if (tk.is(Token::Comment) || tk.is(Token::String) || tk.is(Token::Number)) + continue; - for (UiQualifiedId *iter2 = ast; iter2; iter2 = iter2->next) { - _length = iter2->identifierToken.end() - _offset; - } + if (! skipping) { + tokens.append(tk); - break; + if (tk.is(Token::Identifier)) { + if (index > 0 && originalTokens.at(index - 1).isNot(Token::Dot)) + break; } + } else { + //qDebug() << "skip:" << blockText.mid(tk.offset, tk.length); } - } - - return false; - } - - private: - quint32 _pos; - Node *_expression; - int _offset; - int _length; - }; - - class ScopeCalculator: protected Visitor - { - public: - QStack<Symbol*> operator()(const Document::Ptr &doc, int pos) - { - _doc = doc; - _pos = pos; - _scopes.clear(); - _currentSymbol = 0; - Node::accept(doc->qmlProgram(), this); - return _scopes; - } - - protected: - virtual bool visit(Block * /*ast*/) - { - // TODO -// if (_pos > ast->lbraceToken.end() && _pos < ast->rbraceToken.offset) { -// push(ast); -// Node::accept(ast->statements, this); -// } - - return false; - } - - virtual bool visit(UiObjectBinding *ast) - { - if (ast->initializer && ast->initializer->lbraceToken.offset < _pos && _pos <= ast->initializer->rbraceToken.end()) { - push(ast); - Node::accept(ast->initializer, this); - } - return false; - } + if (tk.is(Token::RightParenthesis) || tk.is(Token::RightBracket)) + ++skipping; - virtual bool visit(UiObjectDefinition *ast) - { - if (ast->initializer && ast->initializer->lbraceToken.offset < _pos && _pos <= ast->initializer->rbraceToken.end()) { - push(ast); - Node::accept(ast->initializer, this); - } + else if (tk.is(Token::LeftParenthesis) || tk.is(Token::LeftBracket)) { + --skipping; - return false; - } + if (! skipping) + tokens.append(tk); - virtual bool visit(UiArrayBinding *ast) - { - if (ast->lbracketToken.offset < _pos && _pos <= ast->rbracketToken.end()) { - push(ast); - Node::accept(ast->members, this); + if (index > 0 && originalTokens.at(index - 1).isNot(Token::Identifier)) + break; + } } - return false; - } - - private: - void push(Node *node) - { - SymbolFromFile* symbol; + if (! tokens.isEmpty()) { + QString expr; + for (int index = tokens.size() - 1; index >= 0; --index) { + Token tk = tokens.at(index); + expr.append(QLatin1Char(' ')); + expr.append(blockText.midRef(tk.offset, tk.length)); - if (_currentSymbol) { - symbol = _currentSymbol->findMember(node); - } else { - symbol = _doc->findSymbol(node); + } + start = tokens.first().begin(); + end = tokens.first().end(); + //qDebug() << "expression under cursor:" << expr; + return expr; } - if (symbol) { - _currentSymbol = symbol; - - if (!cast<UiArrayBinding*>(node)) - _scopes.push(symbol); - } + //qDebug() << "no expression"; + return QString(); } - - private: - Document::Ptr _doc; - quint32 _pos; - QStack<Symbol*> _scopes; - SymbolFromFile* _currentSymbol; }; } } @@ -207,101 +127,32 @@ using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; QmlExpressionUnderCursor::QmlExpressionUnderCursor() - : _expressionNode(0), - _pos(0), _engine(0), _nodePool(0) -{ -} - -QmlExpressionUnderCursor::~QmlExpressionUnderCursor() -{ - if (_engine) { delete _engine; _engine = 0; } - if (_nodePool) { delete _nodePool; _nodePool = 0; } -} + : _expressionNode(0), _expressionOffset(0), _expressionLength(0) +{} -void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor, - const Document::Ptr &doc) +QmlJS::AST::ExpressionNode *QmlExpressionUnderCursor::operator()(const QTextCursor &cursor) { - if (_engine) { delete _engine; _engine = 0; } - if (_nodePool) { delete _nodePool; _nodePool = 0; } - - _pos = cursor.position(); _expressionNode = 0; _expressionOffset = -1; _expressionLength = -1; - _expressionScopes.clear(); - - const QTextBlock block = cursor.block(); - parseExpression(block); - - if (_expressionOffset != -1) { - ScopeCalculator calculator; - _expressionScopes = calculator(doc, _expressionOffset); - } -} -void QmlExpressionUnderCursor::parseExpression(const QTextBlock &block) -{ - int textPosition = _pos - block.position(); - const QString blockText = block.text(); - if (textPosition > 0) { - if (blockText.at(textPosition - 1) == QLatin1Char('.')) - --textPosition; - } else { - textPosition = 0; - } + ExpressionUnderCursor expressionUnderCursor; + _text = expressionUnderCursor(cursor); - const QString text = blockText.left(textPosition); + exprDoc = Document::create(QLatin1String("<expression>")); + exprDoc->setSource(_text); + exprDoc->parseExpression(); - Node *node = 0; + _expressionNode = exprDoc->expression(); - if (UiObjectMember *binding = tryBinding(text)) { -// qDebug() << "**** binding"; - node = binding; - } else if (Statement *stmt = tryStatement(text)) { -// qDebug() << "**** statement"; - node = stmt; - } else { -// qDebug() << "**** none"; - } + _expressionOffset = cursor.block().position() + expressionUnderCursor.start; + _expressionLength = expressionUnderCursor.end - expressionUnderCursor.start; - if (node) { - PositionCalculator calculator; - _expressionNode = calculator(node, textPosition); - _expressionOffset = calculator.offset() + block.position(); - _expressionLength = calculator.length(); - } + return _expressionNode; } -Statement *QmlExpressionUnderCursor::tryStatement(const QString &text) +ExpressionNode *QmlExpressionUnderCursor::expressionNode() const { - _engine = new Engine(); - _nodePool = new NodePool("", _engine); - Lexer lexer(_engine); - Parser parser(_engine); - lexer.setCode(text, /*line = */ 1); - - if (parser.parseStatement()) - return parser.statement(); - else - return 0; + return _expressionNode; } -UiObjectMember *QmlExpressionUnderCursor::tryBinding(const QString &text) -{ - _engine = new Engine(); - _nodePool = new NodePool("", _engine); - Lexer lexer(_engine); - Parser parser(_engine); - lexer.setCode(text, /*line = */ 1); - - if (parser.parseUiObjectMember()) { - UiObjectMember *member = parser.uiObjectMember(); - - if (cast<UiObjectBinding*>(member) || cast<UiArrayBinding*>(member) || cast<UiScriptBinding*>(member)) - return member; - else - return 0; - } else { - return 0; - } -} diff --git a/src/plugins/qmljseditor/qmlexpressionundercursor.h b/src/plugins/qmljseditor/qmlexpressionundercursor.h index 83e814e5c0f56be0fab58acf5301b5302fc160c9..8b617f23486f6bae98b35de0ee8268c7a9fc0494 100644 --- a/src/plugins/qmljseditor/qmlexpressionundercursor.h +++ b/src/plugins/qmljseditor/qmlexpressionundercursor.h @@ -32,17 +32,9 @@ #include <qmljs/parser/qmljsastfwd_p.h> #include <qmljs/qmljsdocument.h> -#include <qmljs/qmljssymbol.h> -#include <QStack> -#include <QTextBlock> #include <QTextCursor> -namespace QmlJS { - class Engine; - class NodePool; -} - namespace QmlJSEditor { namespace Internal { @@ -50,15 +42,10 @@ class QmlExpressionUnderCursor { public: QmlExpressionUnderCursor(); - virtual ~QmlExpressionUnderCursor(); - - void operator()(const QTextCursor &cursor, const QmlJS::Document::Ptr &doc); - QStack<QmlJS::Symbol *> expressionScopes() const - { return _expressionScopes; } + QmlJS::AST::ExpressionNode * operator()(const QTextCursor &cursor); - QmlJS::AST::Node *expressionNode() const - { return _expressionNode; } + QmlJS::AST::ExpressionNode *expressionNode() const; int expressionOffset() const { return _expressionOffset; } @@ -66,20 +53,20 @@ public: int expressionLength() const { return _expressionLength; } + QString text() const + { return _text; } + private: void parseExpression(const QTextBlock &block); - QmlJS::AST::Statement *tryStatement(const QString &text); - QmlJS::AST::UiObjectMember *tryBinding(const QString &text); + void tryExpression(const QString &text); private: - QStack<QmlJS::Symbol *> _expressionScopes; - QmlJS::AST::Node *_expressionNode; + QmlJS::AST::ExpressionNode *_expressionNode; int _expressionOffset; int _expressionLength; - quint32 _pos; - QmlJS::Engine *_engine; - QmlJS::NodePool *_nodePool; + QmlJS::Document::Ptr exprDoc; + QString _text; }; } // namespace Internal diff --git a/src/plugins/qmljseditor/qmlhoverhandler.cpp b/src/plugins/qmljseditor/qmlhoverhandler.cpp index f132708453e22d924c9e6037beef063a12829575..7b2d9666f21312d8e410a89d476beb4d8eab15c8 100644 --- a/src/plugins/qmljseditor/qmlhoverhandler.cpp +++ b/src/plugins/qmljseditor/qmlhoverhandler.cpp @@ -39,6 +39,9 @@ #include <debugger/debuggerconstants.h> #include <extensionsystem/pluginmanager.h> #include <qmljs/qmljssymbol.h> +#include <qmljs/qmljsbind.h> +#include <qmljs/qmljscheck.h> +#include <qmljs/qmljsinterpreter.h> #include <texteditor/itexteditor.h> #include <texteditor/basetexteditor.h> @@ -146,23 +149,23 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in if (!m_modelManager) return; - QmlJSTextEditor *scriptEditor = qobject_cast<QmlJSTextEditor *>(editor->widget()); - if (!scriptEditor) + QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(editor->widget()); + if (!edit) return; const Snapshot snapshot = m_modelManager->snapshot(); - const QString fileName = editor->file()->fileName(); - Document::Ptr doc = snapshot.document(fileName); - if (!doc) - return; // nothing to do + SemanticInfo semanticInfo = edit->semanticInfo(); + Document::Ptr qmlDocument = semanticInfo.document; + if (qmlDocument.isNull()) + return; - QTextCursor tc(scriptEditor->document()); + QTextCursor tc(edit->document()); tc.setPosition(pos); // We only want to show F1 if the tooltip matches the help id bool showF1 = true; - foreach (const QTextEdit::ExtraSelection &sel, scriptEditor->extraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection)) { + foreach (const QTextEdit::ExtraSelection &sel, edit->extraSelections(TextEditor::BaseTextEditor::CodeWarningsSelection)) { if (pos >= sel.cursor.selectionStart() && pos <= sel.cursor.selectionEnd()) { showF1 = false; m_toolTip = sel.format.toolTip(); @@ -184,10 +187,24 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in // Fetch the expression's code QmlExpressionUnderCursor expressionUnderCursor; - expressionUnderCursor(tc, doc); + QmlJS::AST::ExpressionNode *expression = expressionUnderCursor(tc); + + AST::UiObjectMember *declaringMember = 0; + + foreach (const Range &range, semanticInfo.ranges) { + if (pos >= range.begin.position() && pos <= range.end.position()) { + declaringMember = range.ast; + } + } - QmlJS::TypeSystem *typeSystem = ExtensionSystem::PluginManager::instance()->getObject<QmlJS::TypeSystem>(); + Interpreter::Engine interp; + Bind bind(qmlDocument, snapshot, &interp); + Interpreter::ObjectValue *scope = bind(declaringMember); + Check check(&interp); + const Interpreter::Value *value = check(expression, scope); + m_toolTip = interp.typeId(value); +#if 0 QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, m_modelManager->snapshot(), typeSystem); QmlResolveExpression resolver(context); Symbol *resolvedSymbol = resolver.typeOf(expressionUnderCursor.expressionNode()); @@ -200,6 +217,7 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in else if (SymbolFromFile *symbolFromFile = resolvedSymbol->asSymbolFromFile()) m_toolTip = symbolFromFile->fileName(); } +#endif } if (m_helpEngineNeedsSetup && m_helpEngine->registeredDocumentations().count() > 0) { diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index def167768de2dccaa3ac98853118eaa78dcd6b6c..693669521c4136be51b1dea0a993e831f4588f4c 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -809,23 +809,14 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & } QmlExpressionUnderCursor expressionUnderCursor; - expressionUnderCursor(expressionCursor, doc); - - QmlLookupContext context(expressionUnderCursor.expressionScopes(), doc, snapshot, m_typeSystem); - QmlResolveExpression resolver(context); - Symbol *symbol = resolver.typeOf(expressionUnderCursor.expressionNode()); - - if (!symbol) - return link; - - if (const SymbolFromFile *target = symbol->asSymbolFromFile()) { + if (expressionUnderCursor(expressionCursor)) { link.pos = expressionUnderCursor.expressionOffset(); link.length = expressionUnderCursor.expressionLength(); - link.fileName = target->fileName(); - link.line = target->line(); - link.column = target->column(); - if (link.column > 0) - --link.column; +// link.fileName = target->fileName(); +// link.line = target->line(); +// link.column = target->column(); +// if (link.column > 0) +// --link.column; } return link;