forked from qt-creator/qt-creator
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:
committed by
Leandro Melo
parent
fa6c3cc1ec
commit
56bf0e3023
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
Reference in New Issue
Block a user