From c9efafcb98cdbed4c265e1de767982a8727bfd87 Mon Sep 17 00:00:00 2001 From: Roberto Raggi <roberto.raggi@nokia.com> Date: Mon, 25 Jan 2010 14:18:53 +0100 Subject: [PATCH] Introduced ranges and versioning of QML/JS documents. --- src/libs/qmljs/qmljsdocument.cpp | 13 +++- src/libs/qmljs/qmljsdocument.h | 6 +- src/plugins/qmljseditor/qmlcodecompletion.cpp | 67 +++--------------- src/plugins/qmljseditor/qmljseditor.cpp | 70 ++++++++++++++++--- src/plugins/qmljseditor/qmljseditor.h | 34 +++++++-- src/plugins/qmljseditor/qmlmodelmanager.cpp | 53 ++++++++------ src/plugins/qmljseditor/qmlmodelmanager.h | 11 ++- 7 files changed, 154 insertions(+), 100 deletions(-) diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 58900fec932..951d9877253 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -43,8 +43,9 @@ Document::Document(const QString &fileName) : _engine(0) , _pool(0) , _ast(0) - , _fileName(fileName) + , _documentRevision(0) , _parsedCorrectly(false) + , _fileName(fileName) { const int slashIdx = fileName.lastIndexOf('/'); if (slashIdx != -1) @@ -101,6 +102,16 @@ void Document::setSource(const QString &source) _source = source; } +int Document::documentRevision() const +{ + return _documentRevision; +} + +void Document::setDocumentRevision(int revision) +{ + _documentRevision = revision; +} + bool Document::parseQml() { Q_ASSERT(! _engine); diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 3ea931746a3..bbbdc2e000a 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -72,6 +72,9 @@ public: bool isParsedCorrectly() const { return _parsedCorrectly; } + int documentRevision() const; + void setDocumentRevision(int documentRevision); + IdTable ids() const { return _ids; } QString fileName() const { return _fileName; } @@ -86,12 +89,13 @@ private: QmlJS::Engine *_engine; QmlJS::NodePool *_pool; QmlJS::AST::Node *_ast; + int _documentRevision; + bool _parsedCorrectly; QList<QmlJS::DiagnosticMessage> _diagnosticMessages; QString _fileName; QString _path; QString _componentName; QString _source; - bool _parsedCorrectly; IdTable _ids; QmlJS::Symbol::List _symbols; }; diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index 2f98abf9563..dfc3ab665e2 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -94,41 +94,6 @@ static QIcon iconForColor(const QColor &color) namespace { -class FindMembers: protected AST::Visitor -{ - QList<AST::UiObjectMember *> _members; - -public: - QList<AST::UiObjectMember *> operator()(Document::Ptr doc) - { - _members.clear(); - if (doc && doc->qmlProgram()) - doc->qmlProgram()->accept(this); - return _members; - } - -protected: - using AST::Visitor::visit; - - virtual bool visit(AST::UiArrayBinding *ast) - { - _members.append(ast); - return true; - } - - virtual bool visit(AST::UiObjectBinding *ast) - { - _members.append(ast); - return true; - } - - virtual bool visit(AST::UiObjectDefinition *ast) - { - _members.append(ast); - return true; - } -}; - class ExpressionUnderCursor { QTextCursor _cursor; @@ -692,9 +657,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) m_completions.clear(); QmlJS::Snapshot snapshot = m_modelManager->snapshot(); - Document::Ptr qmlDocument = snapshot.document(fileName); - const QFileInfo currentFileInfo(qmlDocument->fileName()); + SemanticInfo semanticInfo = edit->semanticInfo(); + Document::Ptr qmlDocument = semanticInfo.document; + + const QFileInfo currentFileInfo(fileName); const QString currentFilePath = currentFileInfo.absolutePath(); const QIcon componentIcon = iconForColor(Qt::yellow); @@ -737,27 +704,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) AST::UiObjectMember *declaringMember = 0; AST::UiObjectMember *parentMember = 0; - const int cursorLine = edit->textCursor().blockNumber() + 1; - const int cursorColumn = edit->textCursor().columnNumber() + 1; - - FindMembers findMembers; - const QList<AST::UiObjectMember *> members = findMembers(qmlDocument); - for (int index = 0; index < members.size(); ++index) { - AST::UiObjectMember *member = members.at(index); - - AST::SourceLocation pos = member->firstSourceLocation(); - const int startLine = pos.startLine; - const int startColumn = pos.startColumn; - - if (startLine < cursorLine || (startLine == cursorLine && cursorColumn >= startColumn)) { - AST::SourceLocation endPos = member->lastSourceLocation(); - const int endLine = endPos.startLine; - const int endColumn = endPos.startColumn + endPos.length; - - if (cursorLine < endLine || (cursorLine == endLine && cursorColumn <= endColumn)) { - parentMember = declaringMember; - declaringMember = member; - } + const int cursorPosition = editor->position(); + foreach (const Range &range, semanticInfo.ranges) { + if (cursorPosition >= range.begin.position() && cursorPosition <= range.end.position()) { + parentMember = declaringMember; + declaringMember = range.ast; } } diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 81b39c04452..19c68c46630 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -76,8 +76,10 @@ enum { using namespace QmlJS; using namespace QmlJS::AST; +using namespace QmlJSEditor::Internal; namespace { + int blockBraceDepth(const QTextBlock &block) { int state = block.userState(); @@ -120,11 +122,6 @@ bool shouldInsertMatchingText(const QTextCursor &tc) return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd())); } -} // end of anonymous namespace - -namespace QmlJSEditor { -namespace Internal { - class FindIdDeclarations: protected Visitor { public: @@ -383,6 +380,54 @@ protected: } }; +class CreateRanges: protected AST::Visitor +{ + QTextDocument *_textDocument; + QList<Range> _ranges; + +public: + QList<Range> operator()(QTextDocument *textDocument, Document::Ptr doc) + { + _textDocument = textDocument; + _ranges.clear(); + if (doc && doc->qmlProgram() != 0) + doc->qmlProgram()->accept(this); + return _ranges; + } + +protected: + using AST::Visitor::visit; + + virtual bool visit(AST::UiObjectBinding *ast) + { + _ranges.append(createRange(ast)); + return true; + } + + virtual bool visit(AST::UiObjectDefinition *ast) + { + _ranges.append(createRange(ast)); + return true; + } + + Range createRange(AST::UiObjectMember *ast) + { + Range range; + + range.ast = ast; + + range.begin = QTextCursor(_textDocument); + range.begin.setPosition(ast->firstSourceLocation().begin()); + + range.end = QTextCursor(_textDocument); + range.end.setPosition(ast->lastSourceLocation().end()); + return range; + } +}; + +} // end of anonymous namespace + + QmlJSEditorEditable::QmlJSEditorEditable(QmlJSTextEditor *editor) : BaseTextEditorEditable(editor) { @@ -481,13 +526,23 @@ void QmlJSTextEditor::onDocumentUpdated(QmlJS::Document::Ptr doc) if (file()->fileName() != doc->fileName()) return; - m_document = doc; + if (doc->documentRevision() != document()->revision()) { + // got an outdated document. + return; + } FindIdDeclarations updateIds; m_idsRevision = document()->revision(); m_ids = updateIds(doc->qmlProgram()); if (doc->isParsedCorrectly()) { + // create the ranges + CreateRanges createRanges; + SemanticInfo sem; + sem.document = doc; + sem.ranges = createRanges(document(), doc); + m_semanticInfo = sem; + FindDeclarations findDeclarations; m_declarations = findDeclarations(doc->ast()); @@ -956,6 +1011,3 @@ QString QmlJSTextEditor::insertParagraphSeparator(const QTextCursor &) const { return QLatin1String("}\n"); } - -} // namespace Internal -} // namespace QmlJSEditor diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index a9915a3879e..d6970c969a5 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -91,6 +91,27 @@ struct Declaration { } }; +class Range +{ +public: + Range(): ast(0) {} + +public: // attributes + QmlJS::AST::UiObjectMember *ast; + QTextCursor begin; + QTextCursor end; +}; + +class SemanticInfo +{ +public: + SemanticInfo() {} + +public: // attributes + QmlJS::Document::Ptr document; + QList<Range> ranges; +}; + class QmlJSTextEditor : public TextEditor::BaseTextEditor { Q_OBJECT @@ -109,7 +130,7 @@ public: virtual void unCommentSelection(); - QmlJS::Document::Ptr qmlDocument() const { return m_document; } + SemanticInfo semanticInfo() const { return m_semanticInfo; } public slots: virtual void setFontSettings(const TextEditor::FontSettings &); @@ -154,14 +175,15 @@ private: QTimer *m_updateDocumentTimer; QTimer *m_updateUsesTimer; QComboBox *m_methodCombo; - QList<Declaration> m_declarations; - QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap - int m_idsRevision; - QList<QmlJS::DiagnosticMessage> m_diagnosticMessages; - QmlJS::Document::Ptr m_document; + QList<Declaration> m_declarations; // ### remove me + QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### remove me + int m_idsRevision; // ### remove me + QList<QmlJS::DiagnosticMessage> m_diagnosticMessages; // ### remove me QmlModelManagerInterface *m_modelManager; QmlJS::TypeSystem *m_typeSystem; QTextCharFormat m_occurrencesFormat; + + SemanticInfo m_semanticInfo; }; } // namespace Internal diff --git a/src/plugins/qmljseditor/qmlmodelmanager.cpp b/src/plugins/qmljseditor/qmlmodelmanager.cpp index 3c4f7033836..6efbc801f15 100644 --- a/src/plugins/qmljseditor/qmlmodelmanager.cpp +++ b/src/plugins/qmljseditor/qmlmodelmanager.cpp @@ -29,6 +29,7 @@ #include "qmljseditorconstants.h" #include "qmlmodelmanager.h" +#include "qmljseditor.h" #include <coreplugin/icore.h> #include <coreplugin/editormanager/editormanager.h> @@ -42,6 +43,7 @@ #include <qtconcurrent/runextensions.h> #include <QTextStream> +using namespace QmlJS; using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; @@ -57,7 +59,7 @@ QmlModelManager::QmlModelManager(QObject *parent): this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr))); } -QmlJS::Snapshot QmlModelManager::snapshot() const +Snapshot QmlModelManager::snapshot() const { QMutexLocker locker(&m_mutex); @@ -75,7 +77,7 @@ QFuture<void> QmlModelManager::refreshSourceFiles(const QStringList &sourceFiles return QFuture<void>(); } - const QMap<QString, QString> workingCopy = buildWorkingCopyList(); + const QMap<QString, WorkingCopy> workingCopy = buildWorkingCopyList(); QFuture<void> result = QtConcurrent::run(&QmlModelManager::parse, workingCopy, sourceFiles, @@ -102,26 +104,29 @@ QFuture<void> QmlModelManager::refreshSourceFiles(const QStringList &sourceFiles return result; } -QMap<QString, QString> QmlModelManager::buildWorkingCopyList() +QMap<QString, QmlModelManager::WorkingCopy> QmlModelManager::buildWorkingCopyList() { - QMap<QString, QString> workingCopy; + QMap<QString, WorkingCopy> workingCopy; Core::EditorManager *editorManager = m_core->editorManager(); foreach (Core::IEditor *editor, editorManager->openedEditors()) { const QString key = editor->file()->fileName(); if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) { - workingCopy[key] = textEditor->contents(); + if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) { + workingCopy[key].contents = ed->toPlainText(); + workingCopy[key].documentRevision = ed->document()->revision(); + } } } return workingCopy; } -void QmlModelManager::emitDocumentUpdated(QmlJS::Document::Ptr doc) +void QmlModelManager::emitDocumentUpdated(Document::Ptr doc) { emit documentUpdated(doc); } -void QmlModelManager::onDocumentUpdated(QmlJS::Document::Ptr doc) +void QmlModelManager::onDocumentUpdated(Document::Ptr doc) { QMutexLocker locker(&m_mutex); @@ -129,10 +134,14 @@ void QmlModelManager::onDocumentUpdated(QmlJS::Document::Ptr doc) } void QmlModelManager::parse(QFutureInterface<void> &future, - QMap<QString, QString> workingCopy, + QMap<QString, WorkingCopy> workingCopy, QStringList files, QmlModelManager *modelManager) { + Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase(); + Core::MimeType jsSourceTy = db->findByType(QLatin1String("application/javascript")); + Core::MimeType qmlSourceTy = db->findByType(QLatin1String("application/x-qml")); + future.setProgressRange(0, files.size()); for (int i = 0; i < files.size(); ++i) { @@ -140,9 +149,12 @@ void QmlModelManager::parse(QFutureInterface<void> &future, const QString fileName = files.at(i); QString contents; + int documentRevision = 0; if (workingCopy.contains(fileName)) { - contents = workingCopy.value(fileName); + WorkingCopy wc = workingCopy.value(fileName); + contents = wc.contents; + documentRevision = wc.documentRevision; } else { QFile inFile(fileName); @@ -153,23 +165,18 @@ void QmlModelManager::parse(QFutureInterface<void> &future, } } - QmlJS::Document::Ptr doc = QmlJS::Document::create(fileName); + Document::Ptr doc = Document::create(fileName); + doc->setDocumentRevision(documentRevision); doc->setSource(contents); - { - Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase(); - Core::MimeType jsSourceTy = db->findByType(QLatin1String("application/javascript")); - Core::MimeType qmlSourceTy = db->findByType(QLatin1String("application/x-qml")); + const QFileInfo fileInfo(fileName); - const QFileInfo fileInfo(fileName); - - if (jsSourceTy.matchesFile(fileInfo)) - doc->parseJavaScript(); - else if (qmlSourceTy.matchesFile(fileInfo)) - doc->parseQml(); - else - qWarning() << "Don't know how to treat" << fileName; - } + if (jsSourceTy.matchesFile(fileInfo)) + doc->parseJavaScript(); + else if (qmlSourceTy.matchesFile(fileInfo)) + doc->parseQml(); + else + qWarning() << "Don't know how to treat" << fileName; modelManager->emitDocumentUpdated(doc); } diff --git a/src/plugins/qmljseditor/qmlmodelmanager.h b/src/plugins/qmljseditor/qmlmodelmanager.h index a015b8ea393..4677248bd86 100644 --- a/src/plugins/qmljseditor/qmlmodelmanager.h +++ b/src/plugins/qmljseditor/qmlmodelmanager.h @@ -66,11 +66,18 @@ private Q_SLOTS: void onDocumentUpdated(QmlJS::Document::Ptr doc); protected: + struct WorkingCopy + { + WorkingCopy(int revision = 0): documentRevision(revision) {} + int documentRevision; + QString contents; + }; + QFuture<void> refreshSourceFiles(const QStringList &sourceFiles); - QMap<QString, QString> buildWorkingCopyList(); + QMap<QString, WorkingCopy> buildWorkingCopyList(); static void parse(QFutureInterface<void> &future, - QMap<QString, QString> workingCopy, + QMap<QString, WorkingCopy> workingCopy, QStringList files, QmlModelManager *modelManager); -- GitLab