Clang: Add revision and completion management

Reparsing a document is expensive so we should avoid it by all means. In
this patch we prevent that the same document is send again. It isn't send
too in advance of a code completion if there was no changes before the
the completion position.

Change-Id: I0bb786ba1d4e7ce08611a518cb32f8cf8f4d0037
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Marco Bubke
2015-09-28 17:42:43 +02:00
parent 803cca4086
commit b412eb81ea
18 changed files with 540 additions and 22 deletions

View File

@@ -382,19 +382,50 @@ void IpcCommunicator::updateUnsavedFileFromCppEditorDocument(const QString &file
updateUnsavedFile(filePath, document->contents(), document->revision());
}
namespace {
CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath)
{
return CppTools::CppModelManager::instance()->cppEditorDocument(filePath);
}
bool documentHasChanged(const QString &filePath, const QString &projectPartId)
{
auto *document = cppDocument(filePath);
if (document)
return document->sendTracker(projectPartId).shouldSendRevision(document->revision());
return true;
}
void setLastSentDocumentRevision(const QString &filePath,
const QString &projectPartId,
uint revision)
{
auto *document = cppDocument(filePath);
if (document)
document->sendTracker(projectPartId).setLastSentRevision(int(revision));
}
}
void IpcCommunicator::updateTranslationUnit(const QString &filePath,
const QByteArray &contents,
uint documentRevision)
{
const QString projectPartId = Utils::projectPartIdForFile(filePath);
const bool hasUnsavedContent = true;
// TODO: Send new only if changed
registerTranslationUnitsForEditor({{filePath,
projectPartId,
Utf8String::fromByteArray(contents),
hasUnsavedContent,
documentRevision}});
if (documentHasChanged(filePath, projectPartId)) {
const bool hasUnsavedContent = true;
registerTranslationUnitsForEditor({{filePath,
projectPartId,
Utf8String::fromByteArray(contents),
hasUnsavedContent,
documentRevision}});
setLastSentDocumentRevision(filePath, projectPartId, documentRevision);
}
}
void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArray &contents, uint documentRevision)
@@ -412,8 +443,23 @@ void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArra
void IpcCommunicator::requestDiagnostics(const FileContainer &fileContainer)
{
registerTranslationUnitsForEditor({fileContainer});
m_ipcSender->requestDiagnostics({fileContainer});
if (documentHasChanged(fileContainer.filePath(), fileContainer.projectPartId())) {
registerTranslationUnitsForEditor({fileContainer});
m_ipcSender->requestDiagnostics({fileContainer});
setLastSentDocumentRevision(fileContainer.filePath(),
fileContainer.projectPartId(),
fileContainer.documentRevision());
}
}
void IpcCommunicator::updateChangeContentStartPosition(const QString &filePath, int position)
{
auto *document = cppDocument(filePath);
if (document) {
const QString projectPartId = Utils::projectPartIdForFile(filePath);
document->sendTracker(projectPartId).applyContentChange(position);
}
}
void IpcCommunicator::updateTranslationUnitIfNotCurrentDocument(Core::IDocument *document)

View File

@@ -137,6 +137,7 @@ public:
void updateTranslationUnit(const QString &filePath, const QByteArray &contents, uint documentRevision);
void updateUnsavedFile(const QString &filePath, const QByteArray &contents, uint documentRevision);
void requestDiagnostics(const ClangBackEnd::FileContainer &fileContainer);
void updateChangeContentStartPosition(const QString &filePath, int position);
public: // for tests
IpcSenderInterface *setIpcSender(IpcSenderInterface *ipcSender);
@@ -159,6 +160,7 @@ private:
void onEditorAboutToClose(Core::IEditor *editor);
void onCoreAboutToClose();
private:
IpcReceiver m_ipcReceiver;
ClangBackEnd::ConnectionClient m_connection;
QScopedPointer<IpcSenderInterface> m_ipcSender;

View File

@@ -35,12 +35,15 @@
#include "clangassistproposalmodel.h"
#include "clangcompletionassistprocessor.h"
#include "clangcompletioncontextanalyzer.h"
#include "clangeditordocumentprocessor.h"
#include "clangfunctionhintmodel.h"
#include "clangutils.h"
#include "completionchunkstotextconverter.h"
#include <utils/qtcassert.h>
#include <cpptools/editordocumenthandle.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
@@ -680,6 +683,51 @@ void ClangCompletionAssistProcessor::sendFileContent(const QString &projectPartI
info.isDocumentModified,
uint(m_interface->textDocument()->revision())}});
}
namespace {
CppTools::CppEditorDocumentHandle *cppDocument(const QString &filePath)
{
return CppTools::CppModelManager::instance()->cppEditorDocument(filePath);
}
bool shouldSendDocumentForCompletion(const QString &filePath,
const QString &projectPartId,
int completionPosition)
{
auto *document = cppDocument(filePath);
if (document) {
auto &sendTracker = document->sendTracker(projectPartId);
return sendTracker.shouldSendRevisionWithCompletionPosition(document->revision(),
completionPosition);
}
return true;
}
void setLastCompletionPositionAndDocumentRevision(const QString &filePath,
const QString &projectPartId,
int completionPosition)
{
auto *document = cppDocument(filePath);
if (document) {
document->sendTracker(projectPartId).setLastCompletionPosition(completionPosition);
document->sendTracker(projectPartId).setLastSentRevision(document->revision());
}
}
QString projectPartIdForEditorDocument(const QString &filePath)
{
auto projectPart = ClangEditorDocumentProcessor::get(filePath)->projectPart();
QString projectPartId;
if (projectPart)
projectPartId = projectPart->id();
return projectPartId;
}
}
void ClangCompletionAssistProcessor::sendCompletionRequest(int position,
const QByteArray &customFileContent)
@@ -689,8 +737,13 @@ void ClangCompletionAssistProcessor::sendCompletionRequest(int position,
++column;
const QString filePath = m_interface->fileName();
const QString projectPartId = Utils::projectPartIdForFile(filePath);
sendFileContent(projectPartId, customFileContent);
const QString projectPartId = projectPartIdForEditorDocument(filePath);
if (shouldSendDocumentForCompletion(filePath, projectPartId, position)) {
sendFileContent(projectPartId, customFileContent);
setLastCompletionPositionAndDocumentRevision(filePath, projectPartId, position);
}
m_interface->ipcCommunicator().completeCode(this,
filePath,
uint(line),

View File

@@ -119,7 +119,7 @@ void ModelManagerSupportClang::connectTextDocumentToTranslationUnit(TextEditor::
Qt::UniqueConnection);
// Handle changes from e.g. refactoring actions
connect(textDocument, &TextEditor::TextDocument::contentsChanged,
connect(textDocument, &TextEditor::TextDocument::contentsChangedWithPosition,
this, &ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit,
Qt::UniqueConnection);
}
@@ -162,9 +162,14 @@ void ModelManagerSupportClang::onCppDocumentReloadFinishedOnTranslationUnit(bool
}
}
void ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit()
void ModelManagerSupportClang::onCppDocumentContentsChangedOnTranslationUnit(int position,
int /*charsRemoved*/,
int /*charsAdded*/)
{
Core::IDocument *document = qobject_cast<Core::IDocument *>(sender());
m_ipcCommunicator.updateChangeContentStartPosition(document->filePath().toString(),
position);
m_ipcCommunicator.updateTranslationUnitIfNotCurrentDocument(document);
}

View File

@@ -67,7 +67,9 @@ private:
void onEditorOpened(Core::IEditor *editor);
void onCurrentEditorChanged(Core::IEditor *newCurrent);
void onCppDocumentReloadFinishedOnTranslationUnit(bool success);
void onCppDocumentContentsChangedOnTranslationUnit();
void onCppDocumentContentsChangedOnTranslationUnit(int position,
int charsRemoved,
int charsAdded);
void onCppDocumentReloadFinishedOnUnsavedFile(bool success);
void onCppDocumentContentsChangedOnUnsavedFile();

View File

@@ -512,6 +512,8 @@ public:
bool succeeded() const { return m_editor && m_backendIsNotified; }
bool waitUntilBackendIsNotified(int timeout = 10000);
bool waitUntilProjectPartChanged(const QString &newProjectPartId, int timeout = 10000);
bool waitUntil(const std::function<bool()> &condition, int timeout);
TextEditor::BaseTextEditor *editor() const { return m_editor; }
private:
@@ -536,12 +538,37 @@ OpenEditorAtCursorPosition::~OpenEditorAtCursorPosition()
Core::EditorManager::closeEditor(m_editor, /* askAboutModifiedEditors= */ false);
}
bool OpenEditorAtCursorPosition::waitUntilBackendIsNotified(int timeout)
{
QTC_ASSERT(m_editor, return false);
const QString filePath = m_editor->document()->filePath().toString();
auto condition = [filePath] () {
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
return processor && processor->projectPart();
};
return waitUntil(condition, timeout);
}
bool OpenEditorAtCursorPosition::waitUntilProjectPartChanged(const QString &newProjectPartId,
int timeout)
{
const QString filePath = m_editor->document()->filePath().toString();
auto condition = [newProjectPartId, filePath] () {
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
return processor
&& processor->projectPart()
&& processor->projectPart()->id() == newProjectPartId;
};
return waitUntil(condition, timeout);
}
bool OpenEditorAtCursorPosition::waitUntil(const std::function<bool ()> &condition, int timeout)
{
QTime time;
time.start();
@@ -549,8 +576,7 @@ bool OpenEditorAtCursorPosition::waitUntilBackendIsNotified(int timeout)
if (time.elapsed() > timeout)
return false;
const auto *processor = ClangEditorDocumentProcessor::get(filePath);
if (processor && processor->projectPart())
if (condition())
return true;
QCoreApplication::processEvents();
@@ -968,6 +994,8 @@ void ClangCodeCompletionTest::testChangingProjectDependentCompletion()
_("#define PROJECT_CONFIGURATION_1\n"),
/* testOnlyForCleanedProjects= */ true);
QVERIFY(projectLoader.load());
openEditor.waitUntilProjectPartChanged(QLatin1String("myproject.project"));
proposal = completionResults(openEditor.editor());
QVERIFY(hasItem(proposal, "projectConfiguration1"));
@@ -1045,6 +1073,8 @@ void ClangCodeCompletionTest::testUnsavedFilesTrackingByModifyingIncludedFileInN
void ClangCodeCompletionTest::testUnsavedFilesTrackingByModifyingIncludedFileExternally()
{
QSKIP("The file system watcher is doing it in backend process but we wait not long enough");
ChangeDocumentReloadSetting reloadSettingsChanger(Core::IDocument::ReloadUnmodified);
CppTools::Tests::TemporaryDir temporaryDir;