QmlJS editor: Simplify document/semInfo updating significantly.

* Instead of having SemanticInfoUpdater reparse documents itself, it now
  relies on the ModelManager doing that.
* SemanticInfoUpdater now takes a doc and snapshot to generate the
  convenience Context / ScopeChain. Could be converted into a future
  to avoid having a (99% idle) thread per editor.
* Renamed several functions in QmlJSTextEditorWidget to better indicate
  their behavior.

Change-Id: I8af6ccab099130fa7fa227e44864561ca2c3f9e0
Reviewed-by: Leandro Melo <leandro.melo@nokia.com>
This commit is contained in:
Christian Kamm
2011-11-18 13:25:09 +01:00
committed by Leandro Melo
parent fa6c3cc1ec
commit 56bf0e3023
7 changed files with 95 additions and 158 deletions

View File

@@ -677,19 +677,19 @@ QmlJSTextEditorWidget::QmlJSTextEditorWidget(QWidget *parent) :
m_updateDocumentTimer = new QTimer(this); m_updateDocumentTimer = new QTimer(this);
m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL); m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
m_updateDocumentTimer->setSingleShot(true); m_updateDocumentTimer->setSingleShot(true);
connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(updateDocumentNow())); connect(m_updateDocumentTimer, SIGNAL(timeout()), this, SLOT(reparseDocumentNow()));
m_updateUsesTimer = new QTimer(this); m_updateUsesTimer = new QTimer(this);
m_updateUsesTimer->setInterval(UPDATE_USES_DEFAULT_INTERVAL); m_updateUsesTimer->setInterval(UPDATE_USES_DEFAULT_INTERVAL);
m_updateUsesTimer->setSingleShot(true); m_updateUsesTimer->setSingleShot(true);
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow())); connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
m_localReparseTimer = new QTimer(this); m_updateSemanticInfoTimer = new QTimer(this);
m_localReparseTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL); m_updateSemanticInfoTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL);
m_localReparseTimer->setSingleShot(true); m_updateSemanticInfoTimer->setSingleShot(true);
connect(m_localReparseTimer, SIGNAL(timeout()), this, SLOT(forceReparseIfCurrentEditor())); connect(m_updateSemanticInfoTimer, SIGNAL(timeout()), this, SLOT(updateSemanticInfoNow()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateDocument())); connect(this, SIGNAL(textChanged()), this, SLOT(reparseDocument()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses())); connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses())); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses()));
@@ -723,16 +723,15 @@ QmlJSTextEditorWidget::QmlJSTextEditorWidget(QWidget *parent) :
m_oldCursorPosition = -1; m_oldCursorPosition = -1;
if (m_modelManager) { if (m_modelManager) {
m_semanticInfoUpdater->setModelManager(m_modelManager);
connect(m_modelManager, SIGNAL(documentUpdated(QmlJS::Document::Ptr)), connect(m_modelManager, SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr))); this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)), connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
this, SLOT(forceReparseIfCurrentEditor())); this, SLOT(updateSemanticInfo()));
connect(this->document(), SIGNAL(modificationChanged(bool)), this, SLOT(modificationChanged(bool))); connect(this->document(), SIGNAL(modificationChanged(bool)), this, SLOT(modificationChanged(bool)));
} }
connect(m_semanticInfoUpdater, SIGNAL(updated(QmlJSEditor::SemanticInfo)), connect(m_semanticInfoUpdater, SIGNAL(updated(QmlJSEditor::SemanticInfo)),
this, SLOT(updateSemanticInfo(QmlJSEditor::SemanticInfo))); this, SLOT(acceptNewSemanticInfo(QmlJSEditor::SemanticInfo)));
connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)), connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)),
SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker))); SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker)));
@@ -757,7 +756,7 @@ int QmlJSTextEditorWidget::editorRevision() const
return document()->revision(); return document()->revision();
} }
bool QmlJSTextEditorWidget::isOutdated() const bool QmlJSTextEditorWidget::isSemanticInfoOutdated() const
{ {
if (m_semanticInfo.revision() != editorRevision()) if (m_semanticInfo.revision() != editorRevision())
return true; return true;
@@ -799,19 +798,16 @@ bool QmlJSEditorEditable::open(QString *errorString, const QString &fileName, co
return b; return b;
} }
void QmlJSTextEditorWidget::updateDocument() void QmlJSTextEditorWidget::reparseDocument()
{ {
m_updateDocumentTimer->start(UPDATE_DOCUMENT_DEFAULT_INTERVAL); m_updateDocumentTimer->start();
} }
void QmlJSTextEditorWidget::updateDocumentNow() void QmlJSTextEditorWidget::reparseDocumentNow()
{ {
// ### move in the parser thread.
m_updateDocumentTimer->stop(); m_updateDocumentTimer->stop();
const QString fileName = file()->fileName(); const QString fileName = file()->fileName();
m_modelManager->updateSourceFiles(QStringList() << fileName, false); m_modelManager->updateSourceFiles(QStringList() << fileName, false);
} }
@@ -892,19 +888,23 @@ static void appendExtraSelectionsForMessages(
void QmlJSTextEditorWidget::onDocumentUpdated(QmlJS::Document::Ptr doc) void QmlJSTextEditorWidget::onDocumentUpdated(QmlJS::Document::Ptr doc)
{ {
if (file()->fileName() != doc->fileName() if (file()->fileName() != doc->fileName())
|| doc->editorRevision() != document()->revision()) { return;
// maybe a dependency changed: schedule a potential rehighlight
// will not rehighlight if the current editor changes away from this file if (doc->editorRevision() != document()->revision()) {
m_localReparseTimer->start(); // Maybe a dependency changed and our semantic info is now outdated.
// Ignore 0-revision documents though, we get them when a file is initially opened
// in an editor.
if (doc->editorRevision() != 0)
updateSemanticInfo();
return; return;
} }
//qDebug() << doc->fileName() << "was reparsed";
if (doc->ast()) { if (doc->ast()) {
// got a correctly parsed (or recovered) file. // got a correctly parsed (or recovered) file.
m_semanticInfoUpdater->update(SemanticInfoUpdaterSource(doc, m_modelManager->snapshot()));
const SemanticInfoUpdaterSource source = currentSource(/*force = */ true);
m_semanticInfoUpdater->update(source);
} else { } else {
// show parsing errors // show parsing errors
QList<QTextEdit::ExtraSelection> selections; QList<QTextEdit::ExtraSelection> selections;
@@ -1063,7 +1063,7 @@ void QmlJSTextEditorWidget::setUpdateSelectedElements(bool value)
void QmlJSTextEditorWidget::updateUsesNow() void QmlJSTextEditorWidget::updateUsesNow()
{ {
if (document()->revision() != m_semanticInfo.revision()) { if (isSemanticInfoOutdated()) {
updateUses(); updateUses();
return; return;
} }
@@ -1427,7 +1427,7 @@ void QmlJSTextEditorWidget::contextMenuEvent(QContextMenuEvent *e)
QSignalMapper mapper; QSignalMapper mapper;
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int))); connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
if (! isOutdated()) { if (! isSemanticInfoOutdated()) {
TextEditor::IAssistInterface *interface = TextEditor::IAssistInterface *interface =
createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked); createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked);
if (interface) { if (interface) {
@@ -1531,31 +1531,43 @@ void QmlJSTextEditorWidget::setTabSettings(const TextEditor::TabSettings &ts)
TextEditor::BaseTextEditorWidget::setTabSettings(ts); TextEditor::BaseTextEditorWidget::setTabSettings(ts);
} }
void QmlJSTextEditorWidget::forceReparse() void QmlJSTextEditorWidget::updateSemanticInfo()
{ {
m_semanticInfoUpdater->update(currentSource(/* force = */ true)); // If the document is already out of date, new semantic infos
} // won't be accepted anyway. What we need is a reparse.
if (isSemanticInfoOutdated())
return;
void QmlJSEditor::QmlJSTextEditorWidget::forceReparseIfCurrentEditor() // Save time by not doing it for non-active editors.
{
Core::EditorManager *editorManager = Core::EditorManager::instance(); Core::EditorManager *editorManager = Core::EditorManager::instance();
if (editorManager->currentEditor() == editor()) if (editorManager->currentEditor() != editor())
forceReparse(); return;
m_updateSemanticInfoTimer->start();
} }
void QmlJSTextEditorWidget::reparse() void QmlJSTextEditorWidget::updateSemanticInfoNow()
{ {
m_semanticInfoUpdater->update(currentSource()); // If the document is already out of date, new semantic infos
// won't be accepted anyway. What we need is a reparse.
if (isSemanticInfoOutdated())
return;
m_updateSemanticInfoTimer->stop();
m_semanticInfoUpdater->update(
SemanticInfoUpdaterSource(m_semanticInfo.document, m_semanticInfo.snapshot));
} }
void QmlJSTextEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo) void QmlJSTextEditorWidget::acceptNewSemanticInfo(const SemanticInfo &semanticInfo)
{ {
if (semanticInfo.revision() != document()->revision()) { if (semanticInfo.document->editorRevision() != document()->revision()) {
// got outdated semantic info // ignore outdated semantic infos
reparse();
return; return;
} }
//qDebug() << file()->fileName() << "got new semantic info";
m_semanticInfo = semanticInfo; m_semanticInfo = semanticInfo;
Document::Ptr doc = semanticInfo.document; Document::Ptr doc = semanticInfo.document;
@@ -1660,25 +1672,6 @@ QVector<QString> QmlJSTextEditorWidget::highlighterFormatCategories()
return categories; return categories;
} }
SemanticInfoUpdaterSource QmlJSTextEditorWidget::currentSource(bool force)
{
int line = 0, column = 0;
convertPosition(position(), &line, &column);
const Snapshot snapshot = m_modelManager->snapshot();
const QString fileName = file()->fileName();
QString code;
if (force || m_semanticInfo.revision() != document()->revision())
code = toPlainText(); // get the source code only when needed.
const unsigned revision = document()->revision();
SemanticInfoUpdaterSource source(snapshot, fileName, code,
line, column, revision);
source.force = force;
return source;
}
TextEditor::IAssistInterface *QmlJSTextEditorWidget::createAssistInterface( TextEditor::IAssistInterface *QmlJSTextEditorWidget::createAssistInterface(
TextEditor::AssistKind assistKind, TextEditor::AssistKind assistKind,
TextEditor::AssistReason reason) const TextEditor::AssistReason reason) const

View File

@@ -156,8 +156,8 @@ public:
virtual void unCommentSelection(); virtual void unCommentSelection();
SemanticInfo semanticInfo() const; SemanticInfo semanticInfo() const;
bool isSemanticInfoOutdated() const;
int editorRevision() const; int editorRevision() const;
bool isOutdated() const;
Internal::QmlOutlineModel *outlineModel() const; Internal::QmlOutlineModel *outlineModel() const;
QModelIndex outlineModelIndex(); QModelIndex outlineModelIndex();
@@ -172,7 +172,10 @@ public:
public slots: public slots:
virtual void setTabSettings(const TextEditor::TabSettings &ts); virtual void setTabSettings(const TextEditor::TabSettings &ts);
void forceReparse(); void reparseDocument();
void reparseDocumentNow();
void updateSemanticInfo();
void updateSemanticInfoNow();
void followSymbolUnderCursor(); void followSymbolUnderCursor();
void findUsages(); void findUsages();
void renameUsages(); void renameUsages();
@@ -188,8 +191,6 @@ private slots:
void onDocumentUpdated(QmlJS::Document::Ptr doc); void onDocumentUpdated(QmlJS::Document::Ptr doc);
void modificationChanged(bool); void modificationChanged(bool);
void updateDocument();
void updateDocumentNow();
void jumpToOutlineElement(int index); void jumpToOutlineElement(int index);
void updateOutlineNow(); void updateOutlineNow();
void updateOutlineIndexNow(); void updateOutlineIndexNow();
@@ -200,9 +201,7 @@ private slots:
void updateUses(); void updateUses();
void updateUsesNow(); void updateUsesNow();
void reparse(); void acceptNewSemanticInfo(const QmlJSEditor::SemanticInfo &semanticInfo);
void forceReparseIfCurrentEditor();
void updateSemanticInfo(const QmlJSEditor::SemanticInfo &semanticInfo);
void onCursorPositionChanged(); void onCursorPositionChanged();
void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker); void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker);
@@ -225,7 +224,6 @@ private:
void setSelectedElements(); void setSelectedElements();
QString wordUnderCursor() const; QString wordUnderCursor() const;
Internal::SemanticInfoUpdaterSource currentSource(bool force = false);
QModelIndex indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex = QModelIndex()) const; QModelIndex indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex = QModelIndex()) const;
bool hideContextPane(); bool hideContextPane();
@@ -233,7 +231,7 @@ private:
QTimer *m_updateDocumentTimer; QTimer *m_updateDocumentTimer;
QTimer *m_updateUsesTimer; QTimer *m_updateUsesTimer;
QTimer *m_localReparseTimer; QTimer *m_updateSemanticInfoTimer;
QTimer *m_updateOutlineTimer; QTimer *m_updateOutlineTimer;
QTimer *m_updateOutlineIndexTimer; QTimer *m_updateOutlineIndexTimer;
QTimer *m_cursorPositionTimer; QTimer *m_cursorPositionTimer;

View File

@@ -311,7 +311,7 @@ void QmlJSEditorPlugin::reformatFile()
{ {
Core::EditorManager *em = Core::EditorManager::instance(); Core::EditorManager *em = Core::EditorManager::instance();
if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget())) { if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget())) {
QTC_ASSERT(!editor->isOutdated(), return); QTC_ASSERT(!editor->isSemanticInfoOutdated(), return);
const QString &newText = QmlJS::reformat(editor->semanticInfo().document); const QString &newText = QmlJS::reformat(editor->semanticInfo().document);
QTextCursor tc(editor->textCursor()); QTextCursor tc(editor->textCursor());
@@ -364,7 +364,7 @@ void QmlJSEditorPlugin::currentEditorChanged(Core::IEditor *editor)
this, SLOT(checkCurrentEditorSemanticInfoUpToDate())); this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
connect(newTextEditor, SIGNAL(semanticInfoUpdated()), connect(newTextEditor, SIGNAL(semanticInfoUpdated()),
this, SLOT(checkCurrentEditorSemanticInfoUpToDate())); this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
newTextEditor->forceReparse(); newTextEditor->reparseDocumentNow();
} }
} }
@@ -378,7 +378,7 @@ void QmlJSEditorPlugin::runSemanticScan()
void QmlJSEditorPlugin::checkCurrentEditorSemanticInfoUpToDate() void QmlJSEditorPlugin::checkCurrentEditorSemanticInfoUpToDate()
{ {
const bool semanticInfoUpToDate = m_currentEditor && !m_currentEditor->isOutdated(); const bool semanticInfoUpToDate = m_currentEditor && !m_currentEditor->isSemanticInfoOutdated();
m_reformatFileAction->setEnabled(semanticInfoUpToDate); m_reformatFileAction->setEnabled(semanticInfoUpToDate);
} }

View File

@@ -119,7 +119,7 @@ void HoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos)
return; return;
const QmlJSEditor::SemanticInfo &semanticInfo = qmlEditor->semanticInfo(); const QmlJSEditor::SemanticInfo &semanticInfo = qmlEditor->semanticInfo();
if (! semanticInfo.isValid() || semanticInfo.revision() != qmlEditor->editorRevision()) if (! semanticInfo.isValid() || qmlEditor->isSemanticInfoOutdated())
return; return;
QList<AST::Node *> rangePath = semanticInfo.rangePath(pos); QList<AST::Node *> rangePath = semanticInfo.rangePath(pos);

View File

@@ -43,9 +43,8 @@ namespace QmlJSEditor {
namespace Internal { namespace Internal {
SemanticInfoUpdater::SemanticInfoUpdater(QObject *parent) SemanticInfoUpdater::SemanticInfoUpdater(QObject *parent)
: QThread(parent), : QThread(parent)
m_done(false), , m_wasCancelled(false)
m_modelManager(0)
{ {
} }
@@ -56,7 +55,7 @@ SemanticInfoUpdater::~SemanticInfoUpdater()
void SemanticInfoUpdater::abort() void SemanticInfoUpdater::abort()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
m_done = true; m_wasCancelled = true;
m_condition.wakeOne(); m_condition.wakeOne();
} }
@@ -67,13 +66,6 @@ void SemanticInfoUpdater::update(const SemanticInfoUpdaterSource &source)
m_condition.wakeOne(); m_condition.wakeOne();
} }
bool SemanticInfoUpdater::isOutdated()
{
QMutexLocker locker(&m_mutex);
const bool outdated = ! m_source.fileName.isEmpty() || m_done;
return outdated;
}
void SemanticInfoUpdater::run() void SemanticInfoUpdater::run()
{ {
setPriority(QThread::LowestPriority); setPriority(QThread::LowestPriority);
@@ -81,10 +73,10 @@ void SemanticInfoUpdater::run()
forever { forever {
m_mutex.lock(); m_mutex.lock();
while (! (m_done || ! m_source.fileName.isEmpty())) while (! (m_wasCancelled || m_source.isValid()))
m_condition.wait(&m_mutex); m_condition.wait(&m_mutex);
const bool done = m_done; const bool done = m_wasCancelled;
const SemanticInfoUpdaterSource source = m_source; const SemanticInfoUpdaterSource source = m_source;
m_source.clear(); m_source.clear();
@@ -93,73 +85,42 @@ void SemanticInfoUpdater::run()
if (done) if (done)
break; break;
const SemanticInfo info = semanticInfo(source); const SemanticInfo info = makeNewSemanticInfo(source);
if (! isOutdated()) {
m_mutex.lock(); m_mutex.lock();
m_lastSemanticInfo = info; const bool cancelledOrNewData = m_wasCancelled || m_source.isValid();
m_mutex.unlock(); m_mutex.unlock();
if (! cancelledOrNewData) {
m_lastSemanticInfo = info;
emit updated(info); emit updated(info);
} }
} }
} }
SemanticInfo SemanticInfoUpdater::semanticInfo(const SemanticInfoUpdaterSource &source) SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const SemanticInfoUpdaterSource &source)
{ {
m_mutex.lock(); using namespace QmlJS;
const int revision = m_lastSemanticInfo.revision();
m_mutex.unlock();
QmlJS::Snapshot snapshot;
QmlJS::Document::Ptr doc;
if (! source.force && revision == source.revision) {
m_mutex.lock();
snapshot = m_lastSemanticInfo.snapshot;
doc = m_lastSemanticInfo.document;
m_mutex.unlock();
}
if (! doc) {
snapshot = source.snapshot;
QmlJS::Document::Language language;
if (m_lastSemanticInfo.document)
language = m_lastSemanticInfo.document->language();
else
language = QmlJSTools::languageOfFile(source.fileName);
QmlJS::Document::MutablePtr newDoc = snapshot.documentFromSource(
source.code, source.fileName, language);
newDoc->setEditorRevision(source.revision);
newDoc->parse();
snapshot.insert(newDoc);
doc = newDoc;
}
SemanticInfo semanticInfo; SemanticInfo semanticInfo;
semanticInfo.snapshot = snapshot; const Document::Ptr &doc = semanticInfo.document = source.document;
semanticInfo.document = doc; semanticInfo.snapshot = source.snapshot;
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance(); ModelManagerInterface *modelManager = ModelManagerInterface::instance();
QmlJS::Link link(snapshot, modelManager->importPaths(), modelManager->builtins(doc)); Link link(semanticInfo.snapshot, modelManager->importPaths(), modelManager->builtins(doc));
semanticInfo.context = link(doc, &semanticInfo.semanticMessages); semanticInfo.context = link(doc, &semanticInfo.semanticMessages);
QmlJS::ScopeChain *scopeChain = new QmlJS::ScopeChain(doc, semanticInfo.context); ScopeChain *scopeChain = new ScopeChain(doc, semanticInfo.context);
semanticInfo.m_rootScopeChain = QSharedPointer<const QmlJS::ScopeChain>(scopeChain); semanticInfo.m_rootScopeChain = QSharedPointer<const ScopeChain>(scopeChain);
if (doc->language() != QmlJS::Document::JsonLanguage) { if (doc->language() != Document::JsonLanguage) {
QmlJS::Check checker(doc, semanticInfo.context); Check checker(doc, semanticInfo.context);
semanticInfo.staticAnalysisMessages = checker(); semanticInfo.staticAnalysisMessages = checker();
} }
return semanticInfo; return semanticInfo;
} }
void SemanticInfoUpdater::setModelManager(QmlJS::ModelManagerInterface *modelManager)
{
m_modelManager = modelManager;
}
} // namespace Internal } // namespace Internal
} // namespace QmlJSEditor } // namespace QmlJSEditor

View File

@@ -45,37 +45,25 @@ namespace Internal {
struct SemanticInfoUpdaterSource struct SemanticInfoUpdaterSource
{ {
QmlJS::Document::Ptr document;
QmlJS::Snapshot snapshot; QmlJS::Snapshot snapshot;
QString fileName;
QString code;
int line;
int column;
int revision;
bool force;
SemanticInfoUpdaterSource() SemanticInfoUpdaterSource()
: line(0), column(0), revision(0), force(false)
{ } { }
SemanticInfoUpdaterSource(const QmlJS::Snapshot &snapshot, SemanticInfoUpdaterSource(const QmlJS::Document::Ptr &document,
const QString &fileName, const QmlJS::Snapshot &snapshot)
const QString &code, : document(document)
int line, int column, , snapshot(snapshot)
int revision)
: snapshot(snapshot), fileName(fileName),
code(code), line(line), column(column),
revision(revision), force(false)
{ } { }
bool isValid() const
{ return document; }
void clear() void clear()
{ {
document.clear();
snapshot = QmlJS::Snapshot(); snapshot = QmlJS::Snapshot();
fileName.clear();
code.clear();
line = 0;
column = 0;
revision = 0;
force = false;
} }
}; };
@@ -89,7 +77,6 @@ public:
void abort(); void abort();
void update(const SemanticInfoUpdaterSource &source); void update(const SemanticInfoUpdaterSource &source);
void setModelManager(QmlJS::ModelManagerInterface *modelManager);
Q_SIGNALS: Q_SIGNALS:
void updated(const QmlJSEditor::SemanticInfo &semanticInfo); void updated(const QmlJSEditor::SemanticInfo &semanticInfo);
@@ -98,16 +85,14 @@ protected:
virtual void run(); virtual void run();
private: private:
bool isOutdated(); SemanticInfo makeNewSemanticInfo(const SemanticInfoUpdaterSource &source);
SemanticInfo semanticInfo(const SemanticInfoUpdaterSource &source);
private: private:
QMutex m_mutex; QMutex m_mutex;
QWaitCondition m_condition; QWaitCondition m_condition;
bool m_done; bool m_wasCancelled;
SemanticInfoUpdaterSource m_source; SemanticInfoUpdaterSource m_source;
SemanticInfo m_lastSemanticInfo; SemanticInfo m_lastSemanticInfo;
QmlJS::ModelManagerInterface *m_modelManager;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -408,7 +408,7 @@ Qt::ItemFlags QmlOutlineModel::flags(const QModelIndex &index) const
// only allow drag&drop if we're in sync // only allow drag&drop if we're in sync
if (m_semanticInfo.isValid() if (m_semanticInfo.isValid()
&& m_semanticInfo.revision() == m_textEditor->editorRevision()) { && !m_textEditor->isSemanticInfoOutdated()) {
if (index.parent().isValid()) if (index.parent().isValid())
flags |= Qt::ItemIsDragEnabled; flags |= Qt::ItemIsDragEnabled;
if (index.data(ItemTypeRole) != NonElementBindingType) if (index.data(ItemTypeRole) != NonElementBindingType)