forked from qt-creator/qt-creator
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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user