forked from qt-creator/qt-creator
TextEditor: fix crash on reload
Since TextDocument::openImpl potentially processes events it could delete TextMarks. So tracking them in TextDocument::reload can be considered unsafe. Track them in TextDocumentLayout instead and remove the tracked mark if it gets deleted while reloading the document. Task-number: QTCREATORBUG-29004 Change-Id: I9d0478e9c763b49f145c1bbaeed1a0b602757014 Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
This commit is contained in:
@@ -836,15 +836,14 @@ bool TextDocument::reload(QString *errorString, const FilePath &realFilePath)
|
|||||||
emit aboutToReload();
|
emit aboutToReload();
|
||||||
auto documentLayout =
|
auto documentLayout =
|
||||||
qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
|
qobject_cast<TextDocumentLayout*>(d->m_document.documentLayout());
|
||||||
TextMarks marks;
|
|
||||||
if (documentLayout)
|
if (documentLayout)
|
||||||
marks = documentLayout->documentClosing(); // removes text marks non-permanently
|
documentLayout->documentAboutToReload(); // removes text marks non-permanently
|
||||||
|
|
||||||
bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true)
|
bool success = openImpl(errorString, filePath(), realFilePath, /*reload =*/true)
|
||||||
== OpenResult::Success;
|
== OpenResult::Success;
|
||||||
|
|
||||||
if (documentLayout)
|
if (documentLayout)
|
||||||
documentLayout->documentReloaded(marks, this); // re-adds text marks
|
documentLayout->documentReloaded(this); // re-adds text marks
|
||||||
emit reloadFinished(success);
|
emit reloadFinished(success);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@@ -656,6 +656,7 @@ QSizeF TextDocumentLayout::documentSize() const
|
|||||||
|
|
||||||
TextMarks TextDocumentLayout::documentClosing()
|
TextMarks TextDocumentLayout::documentClosing()
|
||||||
{
|
{
|
||||||
|
QTC_ASSERT(m_reloadMarks.isEmpty(), resetReloadMarks());
|
||||||
TextMarks marks;
|
TextMarks marks;
|
||||||
for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
|
for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
|
||||||
if (auto data = static_cast<TextBlockUserData *>(block.userData()))
|
if (auto data = static_cast<TextBlockUserData *>(block.userData()))
|
||||||
@@ -664,9 +665,17 @@ TextMarks TextDocumentLayout::documentClosing()
|
|||||||
return marks;
|
return marks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTextDocument)
|
void TextDocumentLayout::documentAboutToReload()
|
||||||
{
|
{
|
||||||
for (TextMark *mark : std::as_const(marks)) {
|
m_reloadMarks = documentClosing();
|
||||||
|
for (TextMark *mark : std::as_const(m_reloadMarks))
|
||||||
|
mark->setDeleteCallback([this, mark] { m_reloadMarks.removeOne(mark); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextDocumentLayout::documentReloaded(TextDocument *baseTextDocument)
|
||||||
|
{
|
||||||
|
for (TextMark *mark : std::as_const(m_reloadMarks)) {
|
||||||
|
mark->setDeleteCallback({});
|
||||||
int blockNumber = mark->lineNumber() - 1;
|
int blockNumber = mark->lineNumber() - 1;
|
||||||
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
QTextBlock block = document()->findBlockByNumber(blockNumber);
|
||||||
if (block.isValid()) {
|
if (block.isValid()) {
|
||||||
@@ -680,6 +689,7 @@ void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTex
|
|||||||
mark->removedFromEditor();
|
mark->removedFromEditor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_reloadMarks.clear();
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,6 +731,13 @@ void TextDocumentLayout::requestUpdateNow()
|
|||||||
requestUpdate();
|
requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextDocumentLayout::resetReloadMarks()
|
||||||
|
{
|
||||||
|
for (TextMark *mark : std::as_const(m_reloadMarks))
|
||||||
|
mark->setDeleteCallback({});
|
||||||
|
m_reloadMarks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
static QRectF replacementBoundingRect(const QTextDocument *replacement)
|
static QRectF replacementBoundingRect(const QTextDocument *replacement)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(replacement, return {});
|
QTC_ASSERT(replacement, return {});
|
||||||
|
@@ -245,7 +245,8 @@ public:
|
|||||||
QRectF blockBoundingRect(const QTextBlock &block) const override;
|
QRectF blockBoundingRect(const QTextBlock &block) const override;
|
||||||
|
|
||||||
TextMarks documentClosing();
|
TextMarks documentClosing();
|
||||||
void documentReloaded(TextMarks marks, TextDocument *baseextDocument);
|
void documentAboutToReload();
|
||||||
|
void documentReloaded(TextDocument *baseextDocument);
|
||||||
void updateMarksLineNumber();
|
void updateMarksLineNumber();
|
||||||
void updateMarksBlock(const QTextBlock &block);
|
void updateMarksBlock(const QTextBlock &block);
|
||||||
void scheduleUpdate();
|
void scheduleUpdate();
|
||||||
@@ -253,6 +254,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_updateScheduled = false;
|
bool m_updateScheduled = false;
|
||||||
|
TextMarks m_reloadMarks;
|
||||||
|
void resetReloadMarks();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateExtraArea();
|
void updateExtraArea();
|
||||||
|
@@ -81,6 +81,8 @@ TextMark::~TextMark()
|
|||||||
TextMarkRegistry::remove(this);
|
TextMarkRegistry::remove(this);
|
||||||
if (m_baseTextDocument)
|
if (m_baseTextDocument)
|
||||||
m_baseTextDocument->removeMark(this);
|
m_baseTextDocument->removeMark(this);
|
||||||
|
if (m_deleteCallback)
|
||||||
|
m_deleteCallback();
|
||||||
m_baseTextDocument = nullptr;
|
m_baseTextDocument = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -118,12 +118,15 @@ public:
|
|||||||
bool isLocationMarker() const;;
|
bool isLocationMarker() const;;
|
||||||
void setIsLocationMarker(bool newIsLocationMarker);
|
void setIsLocationMarker(bool newIsLocationMarker);
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setSettingsPage(Utils::Id settingsPage);
|
void setSettingsPage(Utils::Id settingsPage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(TextMark)
|
Q_DISABLE_COPY(TextMark)
|
||||||
|
|
||||||
|
void setDeleteCallback(const std::function<void()> &callback) { m_deleteCallback = callback; };
|
||||||
|
|
||||||
TextDocument *m_baseTextDocument = nullptr;
|
TextDocument *m_baseTextDocument = nullptr;
|
||||||
Utils::FilePath m_fileName;
|
Utils::FilePath m_fileName;
|
||||||
int m_lineNumber = 0;
|
int m_lineNumber = 0;
|
||||||
@@ -141,6 +144,9 @@ private:
|
|||||||
QVector<QAction *> m_actions; // FIXME Remove in master
|
QVector<QAction *> m_actions; // FIXME Remove in master
|
||||||
std::function<QList<QAction *>()> m_actionsProvider;
|
std::function<QList<QAction *>()> m_actionsProvider;
|
||||||
Utils::Id m_settingsPage;
|
Utils::Id m_settingsPage;
|
||||||
|
std::function<void()> m_deleteCallback;
|
||||||
|
|
||||||
|
friend class TextDocumentLayout;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
Reference in New Issue
Block a user