Clang: Fix delayed reparse of dirty and visible but not current documents

1. Open document foo.h
2. Create a split and open foo.cpp (#including "foo.h")
3. Edit foo.h (e.g. by introducing a syntax error, so that foo.cpp will
   indicate header errors in the toolbar or as info bar)
=> Actual: foo.cpp will be reparsed immediately.
 Expected: foo.cpp should be reparsed after a delay.

This saves resources (cpu time) and minimizes poping up of the header
info bar while editing header files in splits.

Regression introduced by

    commit 380d756a03
    Clang: Hook up supportive translation unit on first edit

Change-Id: Ib5fd90e49415dfc3aefacab7cd627b0e1937f5fc
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Nikolai Kosjar
2016-10-14 13:05:44 +02:00
parent d29781fcd8
commit 4fbdbdb1ee
6 changed files with 121 additions and 15 deletions

View File

@@ -50,6 +50,8 @@
#include <updatetranslationunitsforeditormessage.h> #include <updatetranslationunitsforeditormessage.h>
#include <updatevisibletranslationunitsmessage.h> #include <updatevisibletranslationunitsmessage.h>
#include <utils/qtcassert.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
@@ -57,16 +59,21 @@ namespace ClangBackEnd {
ClangCodeModelServer::ClangCodeModelServer() ClangCodeModelServer::ClangCodeModelServer()
: documents(projects, unsavedFiles) : documents(projects, unsavedFiles)
, updateDocumentAnnotationsTimeOutInMs(1500)
{ {
updateDocumentAnnotationsTimer.setSingleShot(true); updateDocumentAnnotationsTimer.setSingleShot(true);
QObject::connect(&updateDocumentAnnotationsTimer, QObject::connect(&updateDocumentAnnotationsTimer,
&QTimer::timeout, &QTimer::timeout,
[this]() { [this]() {
processJobsForDirtyAndVisibleDocuments(); processJobsForDirtyAndVisibleDocuments();
}); });
updateVisibleButNotCurrentDocumentsTimer.setSingleShot(true);
QObject::connect(&updateVisibleButNotCurrentDocumentsTimer,
&QTimer::timeout,
[this]() {
processJobsForDirtyAndVisibleButNotCurrentDocuments();
});
QObject::connect(documents.clangFileSystemWatcher(), QObject::connect(documents.clangFileSystemWatcher(),
&ClangFileSystemWatcher::fileChanged, &ClangFileSystemWatcher::fileChanged,
[this](const Utf8String &filePath) { [this](const Utf8String &filePath) {
@@ -265,16 +272,44 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const
void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments()
{ {
for (const auto &document : documents.documents()) { processJobsForDirtyCurrentDocument();
if (document.isNeedingReparse() && document.isVisibleInEditor()) { processTimerForVisibleButNotCurrentDocuments();
DocumentProcessor processor = documentProcessors().processor(document); }
processor.addJob(createJobRequest(document,
JobRequest::Type::UpdateDocumentAnnotations,
PreferredTranslationUnit::PreviouslyParsed));
}
}
documentProcessors().process(); void ClangCodeModelServer::processJobsForDirtyCurrentDocument()
{
const auto currentDirtyDocuments = documents.filtered([](const Document &document) {
return document.isNeedingReparse() && document.isUsedByCurrentEditor();
});
QTC_CHECK(currentDirtyDocuments.size() <= 1);
addAndRunUpdateJobs(currentDirtyDocuments);
}
void ClangCodeModelServer::addAndRunUpdateJobs(const std::vector<Document> &documents)
{
for (const auto &document : documents) {
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(createJobRequest(document,
JobRequest::Type::UpdateDocumentAnnotations,
PreferredTranslationUnit::PreviouslyParsed));
processor.process();
}
}
void ClangCodeModelServer::processTimerForVisibleButNotCurrentDocuments()
{
if (documents.dirtyAndVisibleButNotCurrentDocuments().empty()) {
updateVisibleButNotCurrentDocumentsTimer.stop();
} else {
updateVisibleButNotCurrentDocumentsTimer.start(
updateVisibleButNotCurrentDocumentsTimeOutInMs);
}
}
void ClangCodeModelServer::processJobsForDirtyAndVisibleButNotCurrentDocuments()
{
addAndRunUpdateJobs(documents.dirtyAndVisibleButNotCurrentDocuments());
} }
void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents) void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents)
@@ -332,6 +367,11 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i
updateDocumentAnnotationsTimeOutInMs = value; updateDocumentAnnotationsTimeOutInMs = value;
} }
void ClangCodeModelServer::setUpdateVisibleButNotCurrentDocumentsTimeOutInMsForTestsOnly(int value)
{
updateVisibleButNotCurrentDocumentsTimeOutInMs = value;
}
DocumentProcessors &ClangCodeModelServer::documentProcessors() DocumentProcessors &ClangCodeModelServer::documentProcessors()
{ {
if (!documentProcessors_) { if (!documentProcessors_) {

View File

@@ -65,16 +65,22 @@ public: // for tests
int queueSizeForTestsOnly(); int queueSizeForTestsOnly();
bool isTimerRunningForTestOnly() const; bool isTimerRunningForTestOnly() const;
void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value);
void setUpdateVisibleButNotCurrentDocumentsTimeOutInMsForTestsOnly(int value);
DocumentProcessors &documentProcessors(); DocumentProcessors &documentProcessors();
private: private:
void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath);
void addJobRequestsForDirtyAndVisibleDocuments();
void processJobsForDirtyAndVisibleDocuments();
void processInitialJobsForDocuments(const std::vector<Document> &documents); void processInitialJobsForDocuments(const std::vector<Document> &documents);
void startInitializingSupportiveTranslationUnits(const std::vector<Document> &documents); void startInitializingSupportiveTranslationUnits(const std::vector<Document> &documents);
void processJobsForDirtyAndVisibleDocuments();
void processJobsForDirtyCurrentDocument();
void processTimerForVisibleButNotCurrentDocuments();
void processJobsForDirtyAndVisibleButNotCurrentDocuments();
void addAndRunUpdateJobs(const std::vector<Document> &documents);
JobRequest createJobRequest(const Document &document, JobRequest createJobRequest(const Document &document,
JobRequest::Type type, JobRequest::Type type,
PreferredTranslationUnit preferredTranslationUnit PreferredTranslationUnit preferredTranslationUnit
@@ -88,7 +94,10 @@ private:
QScopedPointer<DocumentProcessors> documentProcessors_; // Delayed initialization QScopedPointer<DocumentProcessors> documentProcessors_; // Delayed initialization
QTimer updateDocumentAnnotationsTimer; QTimer updateDocumentAnnotationsTimer;
int updateDocumentAnnotationsTimeOutInMs; int updateDocumentAnnotationsTimeOutInMs = 1500;
QTimer updateVisibleButNotCurrentDocumentsTimer;
int updateVisibleButNotCurrentDocumentsTimeOutInMs = 2000;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -33,6 +33,8 @@
#include <skippedsourceranges.h> #include <skippedsourceranges.h>
#include <unsavedfiles.h> #include <unsavedfiles.h>
#include <utils/algorithm.h>
#include <QDebug> #include <QDebug>
#include <algorithm> #include <algorithm>
@@ -143,6 +145,20 @@ const std::vector<Document> &Documents::documents() const
return documents_; return documents_;
} }
const std::vector<Document> Documents::filtered(const IsMatchingDocument &isMatchingDocument) const
{
return Utils::filtered(documents_, isMatchingDocument);
}
std::vector<Document> Documents::dirtyAndVisibleButNotCurrentDocuments() const
{
return filtered([](const Document &document) {
return document.isNeedingReparse()
&& document.isVisibleInEditor()
&& !document.isUsedByCurrentEditor();
});
}
UnsavedFiles Documents::unsavedFiles() const UnsavedFiles Documents::unsavedFiles() const
{ {
return unsavedFiles_; return unsavedFiles_;

View File

@@ -32,6 +32,7 @@
#include <QVector> #include <QVector>
#include <functional>
#include <vector> #include <vector>
namespace ClangBackEnd { namespace ClangBackEnd {
@@ -57,6 +58,9 @@ public:
bool hasDocumentWithFilePath(const Utf8String &filePath) const; bool hasDocumentWithFilePath(const Utf8String &filePath) const;
const std::vector<Document> &documents() const; const std::vector<Document> &documents() const;
using IsMatchingDocument = std::function<bool(const Document &document)>;
const std::vector<Document> filtered(const IsMatchingDocument &isMatchingDocument) const;
std::vector<Document> dirtyAndVisibleButNotCurrentDocuments() const;
UnsavedFiles unsavedFiles() const; UnsavedFiles unsavedFiles() const;

View File

@@ -289,6 +289,42 @@ TEST_F(Documents, HasNotDocument)
ASSERT_FALSE(documents.hasDocument(filePath, projectPartId)); ASSERT_FALSE(documents.hasDocument(filePath, projectPartId));
} }
TEST_F(Documents, FilteredPositive)
{
documents.create({{filePath, projectPartId}});
const auto isMatchingFilePath = [this](const Document &document) {
return document.filePath() == filePath;
};
const bool hasMatches = !documents.filtered(isMatchingFilePath).empty();
ASSERT_TRUE(hasMatches);
}
TEST_F(Documents, FilteredNegative)
{
documents.create({{filePath, projectPartId}});
const auto isMatchingNothing = [](const Document &) {
return false;
};
const bool hasMatches = !documents.filtered(isMatchingNothing).empty();
ASSERT_FALSE(hasMatches);
}
TEST_F(Documents, DirtyAndVisibleButNotCurrentDocuments)
{
documents.create({{filePath, projectPartId}});
documents.updateDocumentsWithChangedDependency(filePath);
documents.setVisibleInEditors({filePath});
documents.setUsedByCurrentEditor(Utf8String());
const bool hasMatches = !documents.dirtyAndVisibleButNotCurrentDocuments().empty();
ASSERT_TRUE(hasMatches);
}
TEST_F(Documents, isUsedByCurrentEditor) TEST_F(Documents, isUsedByCurrentEditor)
{ {
documents.create({fileContainer}); documents.create({fileContainer});

View File

@@ -375,6 +375,7 @@ void ClangClangCodeModelServer::SetUp()
{ {
clangServer.setClient(&mockClangCodeModelClient); clangServer.setClient(&mockClangCodeModelClient);
clangServer.setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(0); clangServer.setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(0);
clangServer.setUpdateVisibleButNotCurrentDocumentsTimeOutInMsForTestsOnly(0);
} }
void ClangClangCodeModelServer::TearDown() void ClangClangCodeModelServer::TearDown()