Commit f2b7371a authored by Marco Bubke's avatar Marco Bubke

Clang: Reparse only if files are changed

Includes are now watched by a file watcher. Unsaved file changes are
watched too. If they are changed the translation units which depend on
them are set to a state which require a reparse. Later the diagnostics
of this units are collected and send back to creator.

Change-Id: I2fb5c7dd6644687f22399edd8d18edd6215c9505
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@theqtcompany.com>
parent aa6aaee5
......@@ -57,6 +57,7 @@ ConnectionServer::ConnectionServer(const QString &connectionName)
ConnectionServer::~ConnectionServer()
{
killTimer(aliveTimerId);
removeServer();
}
......
......@@ -38,11 +38,9 @@
namespace ClangBackEnd {
DiagnosticsChangedMessage::DiagnosticsChangedMessage(const FileContainer &file,
const QVector<DiagnosticContainer> &diagnostics,
quint32 documentRevision)
const QVector<DiagnosticContainer> &diagnostics)
: file_(file),
diagnostics_(diagnostics),
documentRevision_(documentRevision)
diagnostics_(diagnostics)
{
}
......@@ -56,16 +54,10 @@ const QVector<DiagnosticContainer> &DiagnosticsChangedMessage::diagnostics() con
return diagnostics_;
}
quint32 DiagnosticsChangedMessage::documentRevision() const
{
return documentRevision_;
}
QDataStream &operator<<(QDataStream &out, const DiagnosticsChangedMessage &message)
{
out << message.file_;
out << message.diagnostics_;
out << message.documentRevision_;
return out;
}
......@@ -74,7 +66,6 @@ QDataStream &operator>>(QDataStream &in, DiagnosticsChangedMessage &message)
{
in >> message.file_;
in >> message.diagnostics_;
in >> message.documentRevision_;
return in;
}
......@@ -94,8 +85,7 @@ bool operator<(const DiagnosticsChangedMessage &first, const DiagnosticsChangedM
QDebug operator<<(QDebug debug, const DiagnosticsChangedMessage &message)
{
debug.nospace() << "DiagnosticsChangedMessage("
<< message.file_ << QStringLiteral(", ")
<< message.documentRevision_
<< message.file_
<< ")";
return debug;
......
......@@ -51,17 +51,14 @@ class CMBIPC_EXPORT DiagnosticsChangedMessage
public:
DiagnosticsChangedMessage() = default;
DiagnosticsChangedMessage(const FileContainer &file,
const QVector<DiagnosticContainer> &diagnostics,
quint32 documentRevision);
const QVector<DiagnosticContainer> &diagnostics);
const FileContainer &file() const;
const QVector<DiagnosticContainer> &diagnostics() const;
quint32 documentRevision() const;
private:
FileContainer file_;
QVector<DiagnosticContainer> diagnostics_;
quint32 documentRevision_;
};
CMBIPC_EXPORT QDataStream &operator<<(QDataStream &out, const DiagnosticsChangedMessage &message);
......
......@@ -39,17 +39,29 @@
namespace ClangBackEnd {
FileContainer::FileContainer(const Utf8String &fileName,
FileContainer::FileContainer(const Utf8String &filePath,
const Utf8String &projectPartId,
const Utf8String &unsavedFileContent,
bool hasUnsavedFileContent)
: filePath_(fileName),
bool hasUnsavedFileContent,
quint32 documentRevision)
: filePath_(filePath),
projectPartId_(projectPartId),
unsavedFileContent_(unsavedFileContent),
documentRevision_(documentRevision),
hasUnsavedFileContent_(hasUnsavedFileContent)
{
}
FileContainer::FileContainer(const Utf8String &filePath,
const Utf8String &projectPartId,
quint32 documentRevision)
: filePath_(filePath),
projectPartId_(projectPartId),
documentRevision_(documentRevision),
hasUnsavedFileContent_(false)
{
}
const Utf8String &FileContainer::filePath() const
{
return filePath_;
......@@ -70,11 +82,17 @@ bool FileContainer::hasUnsavedFileContent() const
return hasUnsavedFileContent_;
}
quint32 FileContainer::documentRevision() const
{
return documentRevision_;
}
QDataStream &operator<<(QDataStream &out, const FileContainer &container)
{
out << container.filePath_;
out << container.projectPartId_;
out << container.unsavedFileContent_;
out << container.documentRevision_;
out << container.hasUnsavedFileContent_;
return out;
......@@ -85,6 +103,7 @@ QDataStream &operator>>(QDataStream &in, FileContainer &container)
in >> container.filePath_;
in >> container.projectPartId_;
in >> container.unsavedFileContent_;
in >> container.documentRevision_;
in >> container.hasUnsavedFileContent_;
return in;
......@@ -106,9 +125,9 @@ bool operator<(const FileContainer &first, const FileContainer &second)
QDebug operator<<(QDebug debug, const FileContainer &container)
{
debug.nospace() << "FileContainer("
<< container.filePath()
<< ", "
<< container.projectPartId();
<< container.filePath() << ", "
<< container.projectPartId() << ", "
<< container.documentRevision();
if (container.hasUnsavedFileContent()) {
const Utf8String fileWithContent = debugWriteFileForInspection(
......@@ -126,9 +145,9 @@ QDebug operator<<(QDebug debug, const FileContainer &container)
void PrintTo(const FileContainer &container, ::std::ostream* os)
{
*os << "FileContainer("
<< container.filePath().constData()
<< ", "
<< container.projectPartId().constData();
<< container.filePath().constData() << ", "
<< container.projectPartId().constData() << ", "
<< container.documentRevision();
if (container.hasUnsavedFileContent())
*os << ", "
......
......@@ -50,17 +50,23 @@ public:
FileContainer(const Utf8String &filePath,
const Utf8String &projectPartId,
const Utf8String &unsavedFileContent = Utf8String(),
bool hasUnsavedFileContent = false);
bool hasUnsavedFileContent = false,
quint32 documentRevision = 0);
FileContainer(const Utf8String &filePath,
const Utf8String &projectPartId,
quint32 documentRevision);
const Utf8String &filePath() const;
const Utf8String &projectPartId() const;
const Utf8String &unsavedFileContent() const;
bool hasUnsavedFileContent() const;
quint32 documentRevision() const;
private:
Utf8String filePath_;
Utf8String projectPartId_;
Utf8String unsavedFileContent_;
quint32 documentRevision_;
bool hasUnsavedFileContent_ = false;
};
......
......@@ -35,10 +35,8 @@
namespace ClangBackEnd {
RequestDiagnosticsMessage::RequestDiagnosticsMessage(const FileContainer &file,
quint32 documentRevision)
: file_(file),
documentRevision_(documentRevision)
RequestDiagnosticsMessage::RequestDiagnosticsMessage(const FileContainer &file)
: file_(file)
{
}
......@@ -47,15 +45,9 @@ const FileContainer RequestDiagnosticsMessage::file() const
return file_;
}
quint32 RequestDiagnosticsMessage::documentRevision() const
{
return documentRevision_;
}
QDataStream &operator<<(QDataStream &out, const RequestDiagnosticsMessage &message)
{
out << message.file_;
out << message.documentRevision_;
return out;
}
......@@ -63,14 +55,13 @@ QDataStream &operator<<(QDataStream &out, const RequestDiagnosticsMessage &messa
QDataStream &operator>>(QDataStream &in, RequestDiagnosticsMessage &message)
{
in >> message.file_;
in >> message.documentRevision_;
return in;
}
bool operator==(const RequestDiagnosticsMessage &first, const RequestDiagnosticsMessage &second)
{
return first.file_ == second.file_ && first.documentRevision_ == second.documentRevision_;
return first.file_ == second.file_;
}
bool operator<(const RequestDiagnosticsMessage &first, const RequestDiagnosticsMessage &second)
......@@ -81,8 +72,7 @@ bool operator<(const RequestDiagnosticsMessage &first, const RequestDiagnosticsM
QDebug operator<<(QDebug debug, const RequestDiagnosticsMessage &message)
{
debug.nospace() << "RequestDiagnosticsMessage("
<< message.file() << ", "
<< message.documentRevision()
<< message.file()
<< ")";
return debug;
......
......@@ -46,10 +46,9 @@ class CMBIPC_EXPORT RequestDiagnosticsMessage
public:
RequestDiagnosticsMessage() = default;
RequestDiagnosticsMessage(const FileContainer &file, quint32 documentRevision);
RequestDiagnosticsMessage(const FileContainer &file);
const FileContainer file() const;
quint32 documentRevision() const;
private:
FileContainer file_;
......
......@@ -263,3 +263,8 @@ void PrintTo(const Utf8String &text, ::std::ostream* os)
{
*os << "\"" << text.toByteArray().data() << "\"";
}
uint qHash(const Utf8String &utf8String)
{
return qHash(utf8String.byteArray);
}
......@@ -54,6 +54,8 @@ class SQLITE_EXPORT Utf8String
friend SQLITE_EXPORT QDataStream &operator<<(QDataStream &datastream, const Utf8String &text);
friend SQLITE_EXPORT QDataStream &operator>>(QDataStream &datastream, Utf8String &text);
friend SQLITE_EXPORT uint qHash(const Utf8String &utf8String);
public:
Utf8String() = default;
explicit Utf8String(const char *utf8Text, int size);
......@@ -118,6 +120,8 @@ SQLITE_EXPORT QDataStream &operator>>(QDataStream &datastream, Utf8String &text)
SQLITE_EXPORT QDebug operator<<(QDebug debug, const Utf8String &text);
SQLITE_EXPORT void PrintTo(const Utf8String &text, ::std::ostream* os);
SQLITE_EXPORT uint qHash(const Utf8String &utf8String);
#define Utf8StringLiteral(str) Utf8String::fromByteArray(QByteArrayLiteral(str))
Q_DECLARE_METATYPE(Utf8String)
......
......@@ -167,7 +167,7 @@ void IpcReceiver::diagnosticsChanged(const DiagnosticsChangedMessage &message)
const QString diagnosticsProjectPartId = message.file().projectPartId();
const QString documentProjectPartId = processor->projectPart()->id();
if (diagnosticsProjectPartId == documentProjectPartId)
processor->updateCodeWarnings(message.diagnostics(), message.documentRevision());
processor->updateCodeWarnings(message.diagnostics(), message.file().documentRevision());
}
}
......@@ -317,7 +317,7 @@ void IpcCommunicator::registerCurrrentCodeModelUiHeaders()
const auto editorSupports = CppModelManager::instance()->abstractEditorSupports();
foreach (const AbstractEditorSupport *es, editorSupports)
updateUnsavedFile(es->fileName(), es->contents());
updateUnsavedFile(es->fileName(), es->contents(), es->revision());
}
static QStringList projectPartMessageLine(const CppTools::ProjectPart::Ptr &projectPart)
......@@ -355,12 +355,14 @@ void IpcCommunicator::registerProjectsParts(const QList<CppTools::ProjectPart::P
void IpcCommunicator::updateUnsavedFileFromCppEditorDocument(const QString &filePath)
{
const QByteArray unsavedContent = CppTools::CppModelManager::instance()
->cppEditorDocument(filePath)->contents();
updateUnsavedFile(filePath, unsavedContent);
const auto document = CppTools::CppModelManager::instance()->cppEditorDocument(filePath);
updateUnsavedFile(filePath, document->contents(), document->revision());
}
void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArray &contents)
void IpcCommunicator::updateUnsavedFile(const QString &filePath,
const QByteArray &contents,
uint documentRevision)
{
const QString projectPartId = Utils::projectPartIdForFile(filePath);
const bool hasUnsavedContent = true;
......@@ -369,13 +371,14 @@ void IpcCommunicator::updateUnsavedFile(const QString &filePath, const QByteArra
registerFilesForCodeCompletion({{filePath,
projectPartId,
Utf8String::fromByteArray(contents),
hasUnsavedContent}});
hasUnsavedContent,
documentRevision}});
}
void IpcCommunicator::requestDiagnostics(const FileContainer &fileContainer, uint documentRevision)
void IpcCommunicator::requestDiagnostics(const FileContainer &fileContainer)
{
registerFilesForCodeCompletion({fileContainer});
m_ipcSender->requestDiagnostics({fileContainer, documentRevision});
m_ipcSender->requestDiagnostics({fileContainer});
}
void IpcCommunicator::updateUnsavedFileIfNotCurrentDocument(Core::IDocument *document)
......
......@@ -128,8 +128,8 @@ public:
void updateUnsavedFileIfNotCurrentDocument(Core::IDocument *document);
void updateUnsavedFileFromCppEditorDocument(const QString &filePath);
void updateUnsavedFile(const QString &filePath, const QByteArray &contents);
void requestDiagnostics(const ClangBackEnd::FileContainer &fileContainer, uint documentRevision);
void updateUnsavedFile(const QString &filePath, const QByteArray &contents, uint documentRevision);
void requestDiagnostics(const ClangBackEnd::FileContainer &fileContainer);
public: // for tests
IpcSenderInterface *setIpcSender(IpcSenderInterface *ipcSender);
......
......@@ -225,14 +225,20 @@ void ClangEditorDocumentProcessor::updateTranslationUnitForCompletion(CppTools::
if (m_projectPart) {
if (projectPart.id() != m_projectPart->id()) {
auto container1 = ClangBackEnd::FileContainer(filePath(), m_projectPart->id());
auto container1 = ClangBackEnd::FileContainer(filePath(),
m_projectPart->id(),
revision());
ipcCommunicator.unregisterFilesForCodeCompletion({container1});
auto container2 = ClangBackEnd::FileContainer(filePath(), projectPart.id());
auto container2 = ClangBackEnd::FileContainer(filePath(),
projectPart.id(),
revision());
ipcCommunicator.registerFilesForCodeCompletion({container2});
}
} else {
auto container = ClangBackEnd::FileContainer(filePath(), projectPart.id());
auto container = ClangBackEnd::FileContainer(filePath(),
projectPart.id(),
revision());
ipcCommunicator.registerFilesForCodeCompletion({container});
}
}
......@@ -458,8 +464,11 @@ void ClangEditorDocumentProcessor::requestDiagnostics(CppTools::ProjectPart &pro
if (!m_projectPart || projectPart.id() != m_projectPart->id()) {
IpcCommunicator &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
ipcCommunicator.requestDiagnostics({filePath(), projectPart.id()},
revision());
ipcCommunicator.requestDiagnostics({filePath(),
projectPart.id(),
Utf8String(),
false,
revision()});
}
}
......@@ -469,10 +478,10 @@ void ClangEditorDocumentProcessor::requestDiagnostics()
if (m_projectPart) {
auto &ipcCommunicator = m_modelManagerSupport->ipcCommunicator();
ipcCommunicator.requestDiagnostics({filePath(),
m_projectPart->id(),
baseTextDocument()->plainText(),
true},
revision());
m_projectPart->id(),
baseTextDocument()->plainText(),
true,
revision()});
}
}
......
......@@ -152,7 +152,7 @@ void ModelManagerSupportClang::onAbstractEditorSupportContentsUpdated(const QStr
const QByteArray &content)
{
QTC_ASSERT(!filePath.isEmpty(), return);
m_ipcCommunicator.updateUnsavedFile(filePath, content);
m_ipcCommunicator.updateUnsavedFile(filePath, content, 0);
}
void ModelManagerSupportClang::onAbstractEditorSupportRemoved(const QString &filePath)
......
......@@ -22,7 +22,8 @@ HEADERS += $$PWD/clangipcserver.h \
$$PWD/sourcelocation.h \
$$PWD/sourcerange.h \
$$PWD/fixit.h \
$$PWD/diagnosticsetiterator.h
$$PWD/diagnosticsetiterator.h \
$$PWD/clangfilesystemwatcher.h
SOURCES += $$PWD/clangipcserver.cpp \
$$PWD/codecompleter.cpp \
......@@ -45,4 +46,5 @@ SOURCES += $$PWD/clangipcserver.cpp \
$$PWD/diagnostic.cpp \
$$PWD/sourcelocation.cpp \
$$PWD/sourcerange.cpp \
$$PWD/fixit.cpp
$$PWD/fixit.cpp \
$$PWD/clangfilesystemwatcher.cpp
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "clangfilesystemwatcher.h"
#include "translationunits.h"
#include <utf8stringvector.h>
#include <QStringList>
#include <algorithm>
#include <iterator>
namespace ClangBackEnd {
namespace {
QStringList toStringList(const QSet<Utf8String> &files)
{
QStringList resultList;
std::copy(files.cbegin(), files.cend(), std::back_inserter(resultList));
return resultList;
}
}
ClangFileSystemWatcher::ClangFileSystemWatcher(TranslationUnits &translationUnits)
: translationUnits(translationUnits)
{
connect(&watcher,
&QFileSystemWatcher::fileChanged,
this,
&ClangFileSystemWatcher::updateTranslationUnitsWithChangedDependencies);
connect(&changedDiagnosticsTimer,
&QTimer::timeout,
this,
&ClangFileSystemWatcher::sendChangedDiagnostics);
changedDiagnosticsTimer.setSingleShot(true);
changedDiagnosticsTimer.setInterval(100);
}
void ClangFileSystemWatcher::addFiles(const QSet<Utf8String> &filePaths)
{
watcher.addPaths(toStringList(filePaths));
}
void ClangFileSystemWatcher::updateTranslationUnitsWithChangedDependencies(const QString &filePath)
{
translationUnits.updateTranslationUnitsWithChangedDependencies(filePath);
changedDiagnosticsTimer.start();
watcher.addPath(filePath);
}
void ClangFileSystemWatcher::sendChangedDiagnostics()
{
translationUnits.sendChangedDiagnostics();
}
} // namespace ClangBackEnd
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef CLANGFILESYSTEMWATCHER_H
#define CLANGFILESYSTEMWATCHER_H
#include <QFileSystemWatcher>
#include <QSet>
#include <QTimer>
class Utf8String;
namespace ClangBackEnd {
class TranslationUnits;
class ClangFileSystemWatcher : public QObject
{
Q_OBJECT
public:
ClangFileSystemWatcher(TranslationUnits &translationUnits);
void addFiles(const QSet<Utf8String> &filePaths);
private:
void updateTranslationUnitsWithChangedDependencies(const QString &filePath);
void sendChangedDiagnostics();
private:
QFileSystemWatcher watcher;
QTimer changedDiagnosticsTimer;
TranslationUnits &translationUnits;
};
} // namespace ClangBackEnd
#endif // CLANGFILESYSTEMWATCHER_H
......@@ -59,6 +59,10 @@ namespace ClangBackEnd {
ClangIpcServer::ClangIpcServer()
: translationUnits(projects, unsavedFiles)
{
translationUnits.setSendChangeDiagnosticsCallback([this] (const DiagnosticsChangedMessage &message)
{
client()->diagnosticsChanged(message);
});
}
void ClangIpcServer::end()
......@@ -71,8 +75,11 @@ void ClangIpcServer::registerTranslationUnitsForCodeCompletion(const ClangBackEn
TIME_SCOPE_DURATION("ClangIpcServer::registerTranslationUnitsForCodeCompletion");
try {
translationUnits.createOrUpdate(message.fileContainers());
unsavedFiles.createOrUpdate(message.fileContainers());
const auto newerFileContainers = translationUnits.newerFileContainers(message.fileContainers());
if (newerFileContainers.size() > 0) {
unsavedFiles.createOrUpdate(newerFileContainers);
translationUnits.createOrUpdate(newerFileContainers);
}
} catch (const ProjectPartDoNotExistException &exception) {
client()->projectPartsDoNotExist(ProjectPartsDoNotExistMessage(exception.projectPartIds()));
} catch (const std::exception &exception) {
......@@ -85,6 +92,7 @@ void ClangIpcServer::unregisterTranslationUnitsForCodeCompletion(const ClangBack
TIME_SCOPE_DURATION("ClangIpcServer::unregisterTranslationUnitsForCodeCompletion");
try {
unsavedFiles.remove(message.fileContainers());
translationUnits.remove(message.fileContainers());
} catch (const TranslationUnitDoesNotExistException &exception) {
client()->translationUnitDoesNotExist(TranslationUnitDoesNotExistMessage(exception.fileContainer()));
......@@ -147,8 +155,7 @@ void ClangIpcServer::requestDiagnostics(const RequestDiagnosticsMessage &message
message.file().projectPartId());
client()->diagnosticsChanged(DiagnosticsChangedMessage(translationUnit.fileContainer(),
translationUnit.diagnostics().toDiagnosticContainers(),
message.documentRevision()));
translationUnit.diagnostics().toDiagnosticContainers()));
} catch (const TranslationUnitDoesNotExistException &exception) {
client()->translationUnitDoesNotExist(TranslationUnitDoesNotExistMessage(exception.fileContainer()));
} catch (const ProjectPartDoNotExistException &exception) {
......
......@@ -49,8 +49,6 @@ CodeCompleter::CodeCompleter(TranslationUnit translationUnit)
CodeCompletions CodeCompleter::complete(uint line, uint column)
{
translationUnit.reparse();
ClangCodeCompleteResults completeResults(clang_codeCompleteAt(translationUnit.cxTranslationUnit(),
translationUnit.filePath().constData(),
line,
......
......@@ -30,19 +30,24 @@
#include "translationunit.h"
#include "clangstring.h"
#include "codecompleter.h"
#include "diagnosticset.h"
#include "projectpart.h"
#include "translationunitfilenotexitexception.h"
#include "translationunitisnullexception.h"
#include "translationunitparseerrorexception.h"
#include "translationunits.h"
#include "unsavedfiles.h"
#include <utf8string.h>
#include <QDebug>
#include <QFileInfo>