diff options
author | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2016-05-31 16:07:09 +0200 |
---|---|---|
committer | Nikolai Kosjar <nikolai.kosjar@qt.io> | 2016-07-28 09:42:31 +0000 |
commit | 38f72855b61e104d2c597141756d140668f5b20a (patch) | |
tree | 459874eab6481194d47dbc5471cfebb6c1886899 /src/tools | |
parent | 90f94363db0cbaef4841f46db70d4ad5480871c0 (diff) | |
download | qt-creator-38f72855b61e104d2c597141756d140668f5b20a.tar.gz |
Clang: Process distinct documents concurrently
Speed ups the typical use cases that can profit from this:
* Change a header file and switch then to source file
* Open documents one after the other (Follow Symbol)
* Change documents visible in splits (e.g. by refactoring action)
* Restore a session with multiple splits
Fixes the test ClangIpcServer.GetCodeCompletionDependingOnArgumets.
Change-Id: Ia575bd59780df14146dfc091a4d48794e4a0543d
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Diffstat (limited to 'src/tools')
28 files changed, 1984 insertions, 445 deletions
diff --git a/src/tools/clangbackend/ipcsource/clangasyncjob.h b/src/tools/clangbackend/ipcsource/clangasyncjob.h new file mode 100644 index 0000000000..b44227041e --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangasyncjob.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangiasyncjob.h" + +#include <utils/runextensions.h> + +#include <QFutureWatcher> +#include <QObject> + +namespace ClangBackEnd { + +template<class Result> +class AsyncJob : public IAsyncJob +{ +public: + AsyncJob() {} + ~AsyncJob() {} + + using Runner = std::function<Result()>; + Runner runner() const { return m_runner; } + void setRunner(const Runner &runner) { m_runner = runner; } + + Result asyncResult() const { return m_futureWatcher.future().result(); } + + QFuture<void> runAsync() override + { + const auto onFinished = [this]() { + finalizeAsyncRun(); + setIsFinished(true); + finishedHandler()(this); + }; + QObject::connect(&m_futureWatcher, + &QFutureWatcher<Result>::finished, + onFinished); + + const QFuture<Result> future = Utils::runAsync(m_runner); + m_futureWatcher.setFuture(future); + + return future; + } + +private: + Runner m_runner; + QFutureWatcher<Result> m_futureWatcher; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri index 667ed1fb5d..f47655a6ea 100644 --- a/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri +++ b/src/tools/clangbackend/ipcsource/clangbackendclangipc-source.pri @@ -36,9 +36,19 @@ HEADERS += $$PWD/clangcodemodelserver.h \ $$PWD/highlightingmarks.h \ $$PWD/highlightingmarksiterator.h \ $$PWD/utf8positionfromlinecolumn.h \ + $$PWD/clangasyncjob.h \ + $$PWD/clangcompletecodejob.h \ + $$PWD/clangcreateinitialdocumentpreamblejob.h \ $$PWD/clangfilepath.h \ + $$PWD/clangiasyncjob.h \ + $$PWD/clangjobcontext.h \ + $$PWD/clangjobqueue.h \ + $$PWD/clangjobrequest.h \ + $$PWD/clangjobs.h \ + $$PWD/clangrequestdocumentannotationsjob.h \ + $$PWD/clangtranslationunitcore.h \ $$PWD/clangunsavedfilesshallowarguments.h \ - $$PWD/clangtranslationunitcore.h + $$PWD/clangupdatedocumentannotationsjob.h SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/codecompleter.cpp \ @@ -74,6 +84,16 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \ $$PWD/highlightingmark.cpp \ $$PWD/highlightingmarks.cpp \ $$PWD/utf8positionfromlinecolumn.cpp \ + $$PWD/clangcompletecodejob.cpp \ + $$PWD/clangcreateinitialdocumentpreamblejob.cpp \ $$PWD/clangfilepath.cpp \ + $$PWD/clangiasyncjob.cpp \ + $$PWD/clangjobcontext.cpp \ + $$PWD/clangjobqueue.cpp \ + $$PWD/clangjobrequest.cpp \ + $$PWD/clangjobs.cpp \ + $$PWD/clangrequestdocumentannotationsjob.cpp \ + $$PWD/clangtranslationunitcore.cpp \ $$PWD/clangunsavedfilesshallowarguments.cpp \ - $$PWD/clangtranslationunitcore.cpp + $$PWD/clangupdatedocumentannotationsjob.cpp \ + diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp index 5d48966986..5f1161717a 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.cpp @@ -82,41 +82,25 @@ int delayedDocumentAnnotationsTimerInterval() return interval; } -} +} // anonymous ClangCodeModelServer::ClangCodeModelServer() : translationUnits(projects, unsavedFiles) + , updateDocumentAnnotationsTimeOutInMs(delayedDocumentAnnotationsTimerInterval()) { - const auto sendDocumentAnnotations - = [this] (const DocumentAnnotationsChangedMessage &documentAnnotationsChangedMessage) { - client()->documentAnnotationsChanged(documentAnnotationsChangedMessage); - }; - - const auto sendDelayedDocumentAnnotations = [this] () { - try { - auto sendState = translationUnits.sendDocumentAnnotations(); - if (sendState == DocumentAnnotationsSendState::MaybeThereAreDocumentAnnotations) - sendDocumentAnnotationsTimer.setInterval(0); - else - sendDocumentAnnotationsTimer.stop(); - } catch (const std::exception &exception) { - qWarning() << "Error in ClangCodeModelServer::sendDelayedDocumentAnnotationsTimer:" << exception.what(); - } - }; - - const auto onFileChanged = [this] (const Utf8String &filePath) { - startDocumentAnnotationsTimerIfFileIsNotATranslationUnit(filePath); - }; - - translationUnits.setSendDocumentAnnotationsCallback(sendDocumentAnnotations); + updateDocumentAnnotationsTimer.setSingleShot(true); - QObject::connect(&sendDocumentAnnotationsTimer, + QObject::connect(&updateDocumentAnnotationsTimer, &QTimer::timeout, - sendDelayedDocumentAnnotations); + [this]() { + processJobsForDirtyAndVisibleDocuments(); + }); QObject::connect(translationUnits.clangFileSystemWatcher(), &ClangFileSystemWatcher::fileChanged, - onFileChanged); + [this](const Utf8String &filePath) { + ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotATranslationUnit(filePath); + }); } void ClangCodeModelServer::end() @@ -133,8 +117,8 @@ void ClangCodeModelServer::registerTranslationUnitsForEditor(const ClangBackEnd: unsavedFiles.createOrUpdate(message.fileContainers()); translationUnits.setUsedByCurrentEditor(message.currentEditorFilePath()); translationUnits.setVisibleInEditors(message.visibleEditorFilePaths()); - startDocumentAnnotations(); - reparseVisibleDocuments(createdTranslationUnits); + + processInitialJobsForDocuments(createdTranslationUnits); } catch (const ProjectPartDoNotExistException &exception) { client()->projectPartsDoNotExist(ProjectPartsDoNotExistMessage(exception.projectPartIds())); } catch (const std::exception &exception) { @@ -151,7 +135,8 @@ void ClangCodeModelServer::updateTranslationUnitsForEditor(const UpdateTranslati if (newerFileContainers.size() > 0) { translationUnits.update(newerFileContainers); unsavedFiles.createOrUpdate(newerFileContainers); - sendDocumentAnnotationsTimer.start(delayedDocumentAnnotationsTimerInterval()); + + updateDocumentAnnotationsTimer.start(updateDocumentAnnotationsTimeOutInMs); } } catch (const ProjectPartDoNotExistException &exception) { client()->projectPartsDoNotExist(ProjectPartsDoNotExistMessage(exception.projectPartIds())); @@ -185,7 +170,8 @@ void ClangCodeModelServer::registerProjectPartsForEditor(const RegisterProjectPa try { projects.createOrUpdate(message.projectContainers()); translationUnits.setTranslationUnitsDirtyIfProjectPartChanged(); - sendDocumentAnnotationsTimer.start(0); + + processJobsForDirtyAndVisibleDocuments(); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::registerProjectPartsForEditor:" << exception.what(); } @@ -211,7 +197,8 @@ void ClangCodeModelServer::registerUnsavedFilesForEditor(const RegisterUnsavedFi try { unsavedFiles.createOrUpdate(message.fileContainers()); translationUnits.updateTranslationUnitsWithChangedDependencies(message.fileContainers()); - sendDocumentAnnotationsTimer.start(delayedDocumentAnnotationsTimerInterval()); + + updateDocumentAnnotationsTimer.start(updateDocumentAnnotationsTimeOutInMs); } catch (const ProjectPartDoNotExistException &exception) { client()->projectPartsDoNotExist(ProjectPartsDoNotExistMessage(exception.projectPartIds())); } catch (const std::exception &exception) { @@ -240,16 +227,16 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage TIME_SCOPE_DURATION("ClangCodeModelServer::completeCode"); try { - auto translationUnit = translationUnits.translationUnit(message.filePath(), message.projectPartId()); - auto translationUnitCore = translationUnit.translationUnitCore(); + auto translationUnit = translationUnits.translationUnit(message.filePath(), + message.projectPartId()); - CodeCompleter codeCompleter(translationUnitCore, unsavedFiles); + JobRequest jobRequest = createJobRequest(translationUnit, JobRequest::Type::CompleteCode); + jobRequest.line = message.line(); + jobRequest.column = message.column(); + jobRequest.ticketNumber = message.ticketNumber(); - const auto codeCompletions = codeCompleter.complete(message.line(), message.column()); - - client()->codeCompleted(CodeCompletedMessage(codeCompletions, - codeCompleter.neededCorrection(), - message.ticketNumber())); + jobs().add(jobRequest); + jobs().process(); } catch (const TranslationUnitDoesNotExistException &exception) { client()->translationUnitDoesNotExist(TranslationUnitDoesNotExistMessage(exception.fileContainer())); } catch (const ProjectPartDoNotExistException &exception) { @@ -266,13 +253,12 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot try { auto translationUnit = translationUnits.translationUnit(message.fileContainer().filePath(), message.fileContainer().projectPartId()); - auto translationUnitCore = translationUnit.translationUnitCore(); - client()->documentAnnotationsChanged(DocumentAnnotationsChangedMessage( - translationUnit.fileContainer(), - translationUnitCore.mainFileDiagnostics(), - translationUnitCore.highlightingMarks().toHighlightingMarksContainers(), - translationUnitCore.skippedSourceRanges().toSourceRangeContainers())); + const JobRequest jobRequest = createJobRequest(translationUnit, + JobRequest::Type::RequestDocumentAnnotations); + + jobs().add(jobRequest); + jobs().process(); } catch (const TranslationUnitDoesNotExistException &exception) { client()->translationUnitDoesNotExist(TranslationUnitDoesNotExistMessage(exception.fileContainer())); } catch (const ProjectPartDoNotExistException &exception) { @@ -289,7 +275,7 @@ void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTran try { translationUnits.setUsedByCurrentEditor(message.currentEditorFilePath()); translationUnits.setVisibleInEditors(message.visibleEditorFilePaths()); - sendDocumentAnnotationsTimer.start(0); + updateDocumentAnnotationsTimer.start(0); } catch (const std::exception &exception) { qWarning() << "Error in ClangCodeModelServer::updateVisibleTranslationUnits:" << exception.what(); } @@ -302,23 +288,80 @@ const TranslationUnits &ClangCodeModelServer::translationUnitsForTestOnly() cons void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotATranslationUnit(const Utf8String &filePath) { - if (!translationUnits.hasTranslationUnit(filePath)) - sendDocumentAnnotationsTimer.start(0); + if (!translationUnits.hasTranslationUnitWithFilePath(filePath)) + updateDocumentAnnotationsTimer.start(0); +} + +const Jobs &ClangCodeModelServer::jobsForTestOnly() +{ + return jobs(); +} + +bool ClangCodeModelServer::isTimerRunningForTestOnly() const +{ + return updateDocumentAnnotationsTimer.isActive(); } -void ClangCodeModelServer::startDocumentAnnotations() +void ClangCodeModelServer::addJobRequestsForDirtyAndVisibleDocuments() { - DocumentAnnotationsSendState sendState = DocumentAnnotationsSendState::MaybeThereAreDocumentAnnotations; + for (const auto &translationUnit : translationUnits.translationUnits()) { + if (translationUnit.isNeedingReparse() && translationUnit.isVisibleInEditor()) { + jobs().add(createJobRequest(translationUnit, + JobRequest::Type::UpdateDocumentAnnotations)); + } + } +} + +void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() +{ + addJobRequestsForDirtyAndVisibleDocuments(); + jobs().process(); +} + +void ClangCodeModelServer::processInitialJobsForDocuments( + const std::vector<TranslationUnit> &translationUnits) +{ + for (const auto &translationUnit : translationUnits) { + jobs().add(createJobRequest(translationUnit, + JobRequest::Type::UpdateDocumentAnnotations)); + jobs().add(createJobRequest(translationUnit, + JobRequest::Type::CreateInitialDocumentPreamble)); + } + + jobs().process(); +} - while (sendState == DocumentAnnotationsSendState::MaybeThereAreDocumentAnnotations) - sendState = translationUnits.sendDocumentAnnotations(); +JobRequest ClangCodeModelServer::createJobRequest(const TranslationUnit &translationUnit, + JobRequest::Type type) const +{ + JobRequest jobRequest; + jobRequest.type = type; + jobRequest.requirements = JobRequest::requirementsForType(type); + jobRequest.filePath = translationUnit.filePath(); + jobRequest.projectPartId = translationUnit.projectPartId(); + jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); + jobRequest.documentRevision = translationUnit.documentRevision(); + const ProjectPart &projectPart = projects.project(translationUnit.projectPartId()); + jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint(); + + return jobRequest; } -void ClangCodeModelServer::reparseVisibleDocuments(std::vector<TranslationUnit> &translationUnits) +void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value) { - for (TranslationUnit &translationUnit : translationUnits) - if (translationUnit.isVisibleInEditor()) - translationUnit.reparse(); + updateDocumentAnnotationsTimeOutInMs = value; } +Jobs &ClangCodeModelServer::jobs() +{ + if (!jobs_) { + // Jobs needs a reference to the client, but the client is not known at + // construction time of ClangCodeModelServer, so construct Jobs in a + // lazy manner. + jobs_.reset(new Jobs(translationUnits, unsavedFiles, projects, *client())); + } + + return *jobs_.data(); } + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h index 7dd52e0b77..82c64b560d 100644 --- a/src/tools/clangbackend/ipcsource/clangcodemodelserver.h +++ b/src/tools/clangbackend/ipcsource/clangcodemodelserver.h @@ -32,10 +32,11 @@ #include "clangtranslationunit.h" #include "translationunits.h" #include "unsavedfiles.h" +#include "clangjobs.h" #include <utf8string.h> -#include <QMap> +#include <QScopedPointer> #include <QTimer> namespace ClangBackEnd { @@ -57,18 +58,31 @@ public: void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; +public /*for tests*/: const TranslationUnits &translationUnitsForTestOnly() const; + const Jobs &jobsForTestOnly(); + bool isTimerRunningForTestOnly() const; + void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); private: + Jobs &jobs(); + void startDocumentAnnotationsTimerIfFileIsNotATranslationUnit(const Utf8String &filePath); - void startDocumentAnnotations(); - void reparseVisibleDocuments(std::vector<TranslationUnit> &translationUnits); + void addJobRequestsForDirtyAndVisibleDocuments(); + void processJobsForDirtyAndVisibleDocuments(); + void processInitialJobsForDocuments(const std::vector<TranslationUnit> &translationUnits); + + JobRequest createJobRequest(const TranslationUnit &translationUnit, + JobRequest::Type type) const; private: ProjectParts projects; UnsavedFiles unsavedFiles; TranslationUnits translationUnits; - QTimer sendDocumentAnnotationsTimer; + QScopedPointer<Jobs> jobs_; + + QTimer updateDocumentAnnotationsTimer; + int updateDocumentAnnotationsTimeOutInMs; }; } // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp new file mode 100644 index 0000000000..d85c93190c --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangcompletecodejob.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangcompletecodejob.h" + +#include <clangbackendipc/clangbackendipcdebugutils.h> +#include <clangbackendipc/clangcodemodelclientinterface.h> +#include <clangbackendipc/cmbcodecompletedmessage.h> + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnitCore &translationUnitCore, + UnsavedFiles unsavedFiles, + quint32 line, + quint32 column) +{ + TIME_SCOPE_DURATION("CompleteCodeJobRunner"); + + CompleteCodeJob::AsyncResult asyncResult; + + try { + const TranslationUnitCore::CodeCompletionResult results + = translationUnitCore.complete(unsavedFiles, line, column); + + asyncResult.completions = results.completions; + asyncResult.correction = results.correction; + } catch (const std::exception &exception) { + qWarning() << "Error in CompleteCodeJobRunner:" << exception.what(); + } + + return asyncResult; +} + +bool CompleteCodeJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::CompleteCode, return false); + + try { + m_pinnedTranslationUnit = context().translationUnitForJobRequest(); + + const TranslationUnitCore translationUnitCore = m_pinnedTranslationUnit.translationUnitCore(); + const UnsavedFiles unsavedFiles = *context().unsavedFiles; + const quint32 line = jobRequest.line; + const quint32 column = jobRequest.column; + setRunner([translationUnitCore, unsavedFiles, line, column]() { + return runAsyncHelper(translationUnitCore, unsavedFiles, line, column); + }); + + + } catch (const std::exception &exception) { + qWarning() << "Error in CompleteCodeJob::prepareAsyncRun:" << exception.what(); + return false; + } + + return true; +} + +void CompleteCodeJob::finalizeAsyncRun() +{ + if (context().isDocumentOpen()) { + const AsyncResult result = asyncResult(); + + const CodeCompletedMessage message(result.completions, + result.correction, + context().jobRequest.ticketNumber); + context().client->codeCompleted(message); + } +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcompletecodejob.h b/src/tools/clangbackend/ipcsource/clangcompletecodejob.h new file mode 100644 index 0000000000..d3ee9dbc6b --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangcompletecodejob.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangasyncjob.h" +#include "clangtranslationunit.h" + +#include <clangbackendipc/codecompletion.h> + +namespace ClangBackEnd { + +struct CompleteCodeJobResult +{ + CodeCompletions completions; + CompletionCorrection correction = CompletionCorrection::NoCorrection; +}; + +class CompleteCodeJob : public AsyncJob<CompleteCodeJobResult> +{ +public: + using AsyncResult = CompleteCodeJobResult; + + bool prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + TranslationUnit m_pinnedTranslationUnit; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp new file mode 100644 index 0000000000..77ee981518 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangcreateinitialdocumentpreamblejob.h" + +#include <clangbackendipc/clangbackendipcdebugutils.h> + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +static void runAsyncHelper(const TranslationUnitCore &translationUnitCore, + const TranslationUnitUpdateInput &translationUnitUpdateInput) +{ + TIME_SCOPE_DURATION("CreateInitialDocumentPreambleJobRunner"); + + try { + translationUnitCore.reparse(translationUnitUpdateInput); + } catch (const std::exception &exception) { + qWarning() << "Error in CreateInitialDocumentPreambleJobRunner:" << exception.what(); + } +} + +bool CreateInitialDocumentPreambleJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::CreateInitialDocumentPreamble, return false); + + try { + m_pinnedTranslationUnit = context().translationUnitForJobRequest(); + m_pinnedFileContainer = m_pinnedTranslationUnit.fileContainer(); + + const TranslationUnitCore translationUnitCore = m_pinnedTranslationUnit.translationUnitCore(); + const TranslationUnitUpdateInput updateInput = m_pinnedTranslationUnit.createUpdateInput(); + setRunner([translationUnitCore, updateInput]() { + return runAsyncHelper(translationUnitCore, updateInput); + }); + + } catch (const std::exception &exception) { + qWarning() << "Error in CreateInitialDocumentPreambleJob::prepareAsyncRun:" + << exception.what(); + return false; + } + + return true; +} + +void CreateInitialDocumentPreambleJob::finalizeAsyncRun() +{ +} + +} // namespace ClangBackEnd + diff --git a/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h new file mode 100644 index 0000000000..023a34c8f3 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangcreateinitialdocumentpreamblejob.h @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangasyncjob.h" +#include "clangtranslationunit.h" + +namespace ClangBackEnd { + +class CreateInitialDocumentPreambleJob : public AsyncJob<void> +{ +public: + bool prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + TranslationUnit m_pinnedTranslationUnit; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp new file mode 100644 index 0000000000..8c433c4092 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangiasyncjob.h" + +#include "clangcompletecodejob.h" +#include "clangcreateinitialdocumentpreamblejob.h" +#include "clangrequestdocumentannotationsjob.h" +#include "clangupdatedocumentannotationsjob.h" + +Q_LOGGING_CATEGORY(jobsLog, "qtc.clangbackend.jobs"); + +namespace ClangBackEnd { + +IAsyncJob *IAsyncJob::create(JobRequest::Type type) +{ + switch (type) { + case JobRequest::Type::UpdateDocumentAnnotations: + return new UpdateDocumentAnnotationsJob(); + case JobRequest::Type::CreateInitialDocumentPreamble: + return new CreateInitialDocumentPreambleJob(); + case JobRequest::Type::CompleteCode: + return new CompleteCodeJob(); + case JobRequest::Type::RequestDocumentAnnotations: + return new RequestDocumentAnnotationsJob(); + } + + return nullptr; +} + +IAsyncJob::IAsyncJob() + : m_context(JobContext()) +{ +} + +IAsyncJob::~IAsyncJob() +{ +} + +JobContext IAsyncJob::context() const +{ + return m_context; +} + +void IAsyncJob::setContext(const JobContext &context) +{ + m_context = context; +} + +IAsyncJob::FinishedHandler IAsyncJob::finishedHandler() const +{ + return m_finishedHandler; +} + +void IAsyncJob::setFinishedHandler(const IAsyncJob::FinishedHandler &finishedHandler) +{ + m_finishedHandler = finishedHandler; +} + +bool IAsyncJob::isFinished() const +{ + return m_isFinished; +} + +void IAsyncJob::setIsFinished(bool isFinished) +{ + m_isFinished = isFinished; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangiasyncjob.h b/src/tools/clangbackend/ipcsource/clangiasyncjob.h new file mode 100644 index 0000000000..44f503f3cb --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangiasyncjob.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangjobcontext.h" + +#include <QFuture> +#include <QLoggingCategory> + +#include <functional> + +Q_DECLARE_LOGGING_CATEGORY(jobsLog); + +namespace ClangBackEnd { + +class IAsyncJob +{ +public: + static IAsyncJob *create(JobRequest::Type type); + +public: + IAsyncJob(); + virtual ~IAsyncJob(); + + JobContext context() const; + void setContext(const JobContext &context); + + using FinishedHandler = std::function<void(IAsyncJob *job)>; + FinishedHandler finishedHandler() const; + void setFinishedHandler(const FinishedHandler &finishedHandler); + + virtual bool prepareAsyncRun() = 0; + virtual QFuture<void> runAsync() = 0; + virtual void finalizeAsyncRun() = 0; + +public: // for tests + bool isFinished() const; + void setIsFinished(bool isFinished); + +private: + bool m_isFinished = false; + FinishedHandler m_finishedHandler; + JobContext m_context; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobcontext.cpp b/src/tools/clangbackend/ipcsource/clangjobcontext.cpp new file mode 100644 index 0000000000..9c9c520fce --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobcontext.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangiasyncjob.h" + +#include "translationunits.h" + +namespace ClangBackEnd { + +JobContext::JobContext(const JobRequest &jobRequest, + TranslationUnits *translationUnits, + UnsavedFiles *unsavedFiles, + ClangCodeModelClientInterface *clientInterface) + : jobRequest(jobRequest) + , translationUnits(translationUnits) + , unsavedFiles(unsavedFiles) + , client(clientInterface) +{ +} + +TranslationUnit JobContext::translationUnitForJobRequest() const +{ + return translationUnits->translationUnit(jobRequest.filePath, jobRequest.projectPartId); +} + +bool JobContext::isOutdated() const +{ + return !isDocumentOpen() || documentRevisionChanged(); +} + +bool JobContext::isDocumentOpen() const +{ + const bool hasTranslationUnit + = translationUnits->hasTranslationUnit(jobRequest.filePath, jobRequest.projectPartId); + + if (!hasTranslationUnit) + qCDebug(jobsLog) << "Document already closed for results of" << jobRequest; + + return hasTranslationUnit; +} + +bool JobContext::documentRevisionChanged() const +{ + const TranslationUnit &translationUnit + = translationUnits->translationUnit(jobRequest.filePath, jobRequest.projectPartId); + const bool revisionChanged = translationUnit.documentRevision() != jobRequest.documentRevision; + + if (revisionChanged) + qCDebug(jobsLog) << "Document revision changed for results of" << jobRequest; + + return revisionChanged; +} + +} // ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobcontext.h b/src/tools/clangbackend/ipcsource/clangjobcontext.h new file mode 100644 index 0000000000..6e124c609e --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobcontext.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangjobrequest.h" + +namespace ClangBackEnd { + +class ClangCodeModelClientInterface; +class TranslationUnit; +class TranslationUnits; +class UnsavedFiles; + +class JobContext +{ +public: + JobContext() = default; + JobContext(const JobRequest &jobRequest, + TranslationUnits *translationUnits, + UnsavedFiles *unsavedFiles, + ClangCodeModelClientInterface *client); + + TranslationUnit translationUnitForJobRequest() const; + + bool isOutdated() const; + bool isDocumentOpen() const; + bool documentRevisionChanged() const; + +public: + JobRequest jobRequest; + TranslationUnits *translationUnits = nullptr; + UnsavedFiles *unsavedFiles = nullptr; + ClangCodeModelClientInterface *client = nullptr; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.cpp b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp new file mode 100644 index 0000000000..59d5e5ba9e --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangiasyncjob.h" +#include "clangjobqueue.h" +#include "clangtranslationunit.h" +#include "translationunits.h" +#include "projects.h" +#include "unsavedfiles.h" + +#include <utils/algorithm.h> + +namespace ClangBackEnd { + +JobQueue::JobQueue(TranslationUnits &translationUnits, + UnsavedFiles &unsavedFiles, + ProjectParts &projectParts, + ClangCodeModelClientInterface &client) + : m_translationUnits(translationUnits) + , m_unsavedFiles(unsavedFiles) + , m_projectParts(projectParts) + , m_client(client) +{ +} + +void JobQueue::add(const JobRequest &job) +{ + qCDebug(jobsLog) << "Adding" << job; + + m_queue.append(job); +} + +int JobQueue::size() const +{ + return m_queue.size(); +} + +JobRequests JobQueue::processQueue() +{ + removeOutDatedRequests(); + prioritizeRequests(); + const JobRequests jobsToRun = takeJobRequestsToRunNow(); + + return jobsToRun; +} + +void JobQueue::removeOutDatedRequests() +{ + JobRequests cleanedRequests; + + foreach (const JobRequest &jobRequest, m_queue) { + try { + if (!isJobRequestOutDated(jobRequest)) + cleanedRequests.append(jobRequest); + } catch (const std::exception &exception) { + qWarning() << "Error in Jobs::removeOutDatedRequests for" + << jobRequest << ":" << exception.what(); + } + } + + m_queue = cleanedRequests; +} + +bool JobQueue::isJobRequestOutDated(const JobRequest &jobRequest) const +{ + const JobRequest::Requirements requirements = jobRequest.requirements; + const UnsavedFiles unsavedFiles = m_translationUnits.unsavedFiles(); + + if (requirements.testFlag(JobRequest::CurrentUnsavedFiles)) { + if (jobRequest.unsavedFilesChangeTimePoint != unsavedFiles.lastChangeTimePoint()) { + qCDebug(jobsLog) << "Removing due to outdated unsaved files:" << jobRequest; + return true; + } + } + + bool projectCheckedAndItExists = false; + + if (requirements.testFlag(JobRequest::DocumentValid)) { + if (!m_translationUnits.hasTranslationUnit(jobRequest.filePath, jobRequest.projectPartId)) { + qCDebug(jobsLog) << "Removing due to already closed document:" << jobRequest; + return true; + } + + if (!m_projectParts.hasProjectPart(jobRequest.projectPartId)) { + qCDebug(jobsLog) << "Removing due to already closed project:" << jobRequest; + return true; + } + projectCheckedAndItExists = true; + + const TranslationUnit translationUnit + = m_translationUnits.translationUnit(jobRequest.filePath, jobRequest.projectPartId); + if (!translationUnit.isIntact()) { + qCDebug(jobsLog) << "Removing due to not intact translation unit:" << jobRequest; + return true; + } + + if (requirements.testFlag(JobRequest::CurrentDocumentRevision)) { + if (translationUnit.documentRevision() != jobRequest.documentRevision) { + qCDebug(jobsLog) << "Removing due to changed document revision:" << jobRequest; + return true; + } + } + } + + if (requirements.testFlag(JobRequest::CurrentProject)) { + if (!projectCheckedAndItExists && !m_projectParts.hasProjectPart(jobRequest.projectPartId)) { + qCDebug(jobsLog) << "Removing due to already closed project:" << jobRequest; + return true; + } + + const ProjectPart &project = m_projectParts.project(jobRequest.projectPartId); + if (project.lastChangeTimePoint() != jobRequest.projectChangeTimePoint) { + qCDebug(jobsLog) << "Removing due to outdated project:" << jobRequest; + return true; + } + } + + return false; +} + +static int priority(const TranslationUnit &translationUnit) +{ + int thePriority = 0; + + if (translationUnit.isUsedByCurrentEditor()) + thePriority += 1000; + + if (translationUnit.isVisibleInEditor()) + thePriority += 100; + + return thePriority; +} + +void JobQueue::prioritizeRequests() +{ + const auto lessThan = [this] (const JobRequest &r1, const JobRequest &r2) { + // TODO: Getting the TU is O(n) currently, so this might become expensive for large n. + const TranslationUnit &t1 = m_translationUnits.translationUnit(r1.filePath, r1.projectPartId); + const TranslationUnit &t2 = m_translationUnits.translationUnit(r2.filePath, r2.projectPartId); + + return priority(t1) > priority(t2); + }; + + std::stable_sort(m_queue.begin(), m_queue.end(), lessThan); +} + +JobRequests JobQueue::takeJobRequestsToRunNow() +{ + JobRequests jobsToRun; + QSet<DocumentId> documentsScheduledForThisRun; + + QMutableVectorIterator<JobRequest> i(m_queue); + while (i.hasNext()) { + const JobRequest &jobRequest = i.next(); + + try { + const TranslationUnit &translationUnit + = m_translationUnits.translationUnit(jobRequest.filePath, + jobRequest.projectPartId); + const DocumentId documentId = DocumentId(jobRequest.filePath, jobRequest.projectPartId); + + if (!translationUnit.isUsedByCurrentEditor() && !translationUnit.isVisibleInEditor()) + continue; + + if (documentsScheduledForThisRun.contains(documentId)) + continue; + + if (isJobRunningForDocument(documentId)) + continue; + + documentsScheduledForThisRun.insert(documentId); + jobsToRun += jobRequest; + i.remove(); + } catch (const std::exception &exception) { + qWarning() << "Error in Jobs::takeJobRequestsToRunNow for" + << jobRequest << ":" << exception.what(); + } + } + + return jobsToRun; +} + +bool JobQueue::isJobRunningForDocument(const JobQueue::DocumentId &documentId) +{ + if (m_isJobRunningHandler) + return m_isJobRunningHandler(documentId.first, documentId.second); + + return false; +} + +void JobQueue::setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler) +{ + m_isJobRunningHandler = isJobRunningHandler; +} + +JobRequests JobQueue::queue() const +{ + return m_queue; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobqueue.h b/src/tools/clangbackend/ipcsource/clangjobqueue.h new file mode 100644 index 0000000000..f29be968d5 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobqueue.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangjobrequest.h" + +#include <functional> + +namespace ClangBackEnd { + +class ClangCodeModelClientInterface; +class ProjectParts; +class TranslationUnits; +class UnsavedFiles; + +class JobQueue +{ +public: + JobQueue(TranslationUnits &translationUnits, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + + void add(const JobRequest &job); + + JobRequests processQueue(); + + using IsJobRunningHandler = std::function<bool(const Utf8String &, const Utf8String &)>; + void setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler); + +public: // for tests + JobRequests queue() const; + int size() const; + void prioritizeRequests(); + +private: + using DocumentId = QPair<Utf8String, Utf8String>; + bool isJobRunningForDocument(const DocumentId &documentId); + JobRequests takeJobRequestsToRunNow(); + void removeOutDatedRequests(); + bool isJobRequestOutDated(const JobRequest &jobRequest) const; + +private: + TranslationUnits &m_translationUnits; + UnsavedFiles &m_unsavedFiles; + ProjectParts &m_projectParts; + ClangCodeModelClientInterface &m_client; + + IsJobRunningHandler m_isJobRunningHandler; + + JobRequests m_queue; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.cpp b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp new file mode 100644 index 0000000000..4c3d692d08 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangjobrequest.h" + +namespace ClangBackEnd { + +#define RETURN_TEXT_FOR_CASE(enumValue) case JobRequest::Type::enumValue: return #enumValue +static const char *JobRequestTypeToText(JobRequest::Type type) +{ + switch (type) { + RETURN_TEXT_FOR_CASE(UpdateDocumentAnnotations); + RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble); + RETURN_TEXT_FOR_CASE(CompleteCode); + RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations); + } + + return "UnhandledJobRequestType"; +} +#undef RETURN_TEXT_FOR_CASE + +QDebug operator<<(QDebug debug, JobRequest::Type type) +{ + debug << JobRequestTypeToText(type); + + return debug; +} + +QDebug operator<<(QDebug debug, const JobRequest &jobRequest) +{ + debug.nospace() << "Job<" + << jobRequest.id + << "," + << JobRequestTypeToText(jobRequest.type) + << "," + << jobRequest.filePath + << ">"; + + return debug; +} + +JobRequest::JobRequest() +{ + static quint64 idCounter = 0; + id = ++idCounter; +} + +JobRequest::Requirements JobRequest::requirementsForType(Type type) +{ + switch (type) { + case JobRequest::Type::UpdateDocumentAnnotations: + return JobRequest::Requirements(JobRequest::All); + case JobRequest::Type::RequestDocumentAnnotations: + return JobRequest::Requirements(JobRequest::DocumentValid + |JobRequest::CurrentDocumentRevision); + case JobRequest::Type::CompleteCode: + case JobRequest::Type::CreateInitialDocumentPreamble: + return JobRequest::Requirements(JobRequest::DocumentValid); + } + + return JobRequest::Requirements(JobRequest::DocumentValid); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobrequest.h b/src/tools/clangbackend/ipcsource/clangjobrequest.h new file mode 100644 index 0000000000..ca75e4ec67 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobrequest.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <utf8string.h> + +#include <QFlags> +#include <QDebug> +#include <QVector> + +#include <chrono> + +namespace ClangBackEnd { + +using time_point = std::chrono::steady_clock::time_point; + +class JobRequest +{ +public: + enum class Type { + UpdateDocumentAnnotations, + CreateInitialDocumentPreamble, + CompleteCode, + RequestDocumentAnnotations, + }; + + enum Requirement { + None = 1 << 0, + + DocumentValid = 1 << 1, + CurrentDocumentRevision = 1 << 3, // Only effective if DocumentValid is also set + CurrentUnsavedFiles = 1 << 2, + CurrentProject = 1 << 4, + + All = DocumentValid | CurrentUnsavedFiles | CurrentDocumentRevision | CurrentProject + }; + Q_DECLARE_FLAGS(Requirements, Requirement) + +public: + static Requirements requirementsForType(Type type); + + JobRequest(); + +public: + quint64 id = 0; + Type type; + Requirements requirements; + + // General + Utf8String filePath; + Utf8String projectPartId; + time_point unsavedFilesChangeTimePoint; + time_point projectChangeTimePoint; + uint documentRevision = 0; + + // For code completion + quint32 line = 0; + quint32 column = 0; + quint64 ticketNumber = 0; +}; + +using JobRequests = QVector<JobRequest>; + +QDebug operator<<(QDebug debug, const JobRequest &jobRequest); + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobs.cpp b/src/tools/clangbackend/ipcsource/clangjobs.cpp new file mode 100644 index 0000000000..6871b03a40 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobs.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangjobs.h" + +#include "clangiasyncjob.h" + +#include <QDebug> +#include <QFutureSynchronizer> +#include <QLoggingCategory> + +#include <utils/algorithm.h> +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +Jobs::Jobs(TranslationUnits &translationUnits, + UnsavedFiles &unsavedFiles, + ProjectParts &projectParts, + ClangCodeModelClientInterface &client) + : m_translationUnits(translationUnits) + , m_unsavedFiles(unsavedFiles) + , m_projectParts(projectParts) + , m_client(client) + , m_queue(translationUnits, unsavedFiles, projectParts, client) +{ + m_queue.setIsJobRunningHandler([this](const Utf8String &filePath, + const Utf8String &projectPartId) { + return isJobRunning(filePath, projectPartId); + }); +} + +Jobs::~Jobs() +{ + QFutureSynchronizer<void> waitForFinishedJobs; + foreach (const RunningJob &runningJob, m_running.values()) + waitForFinishedJobs.addFuture(runningJob.future); +} + +void Jobs::add(const JobRequest &job) +{ + m_queue.add(job); +} + +JobRequests Jobs::process() +{ + const JobRequests jobsToRun = m_queue.processQueue(); + const JobRequests jobsStarted = runJobs(jobsToRun); + + QTC_CHECK(jobsToRun.size() == jobsStarted.size()); + + return jobsStarted; +} + +JobRequests Jobs::runJobs(const JobRequests &jobsRequests) +{ + JobRequests jobsStarted; + + foreach (const JobRequest &jobRequest, jobsRequests) { + if (runJob(jobRequest)) + jobsStarted += jobRequest; + } + + return jobsStarted; +} + +bool Jobs::runJob(const JobRequest &jobRequest) +{ + if (IAsyncJob *asyncJob = IAsyncJob::create(jobRequest.type)) { + JobContext context(jobRequest, &m_translationUnits, &m_unsavedFiles, &m_client); + asyncJob->setContext(context); + + if (asyncJob->prepareAsyncRun()) { + qCDebug(jobsLog) << "Running" << jobRequest; + + asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); }); + const QFuture<void> future = asyncJob->runAsync(); + + m_running.insert(asyncJob, RunningJob{jobRequest, future}); + return true; + } else { + qCDebug(jobsLog) << "Preparation failed for " << jobRequest; + delete asyncJob; + } + } + + return false; +} + +void Jobs::onJobFinished(IAsyncJob *asyncJob) +{ + qCDebug(jobsLog) << "Finishing" << asyncJob->context().jobRequest; + + m_running.remove(asyncJob); + delete asyncJob; + + process(); +} + +int Jobs::runningJobs() const +{ + return m_running.size(); +} + +JobRequests Jobs::queue() const +{ + return m_queue.queue(); +} + +bool Jobs::isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const +{ + const auto hasJobRequest = [filePath, projectPartId](const RunningJob &runningJob) { + const JobRequest &jobRequest = runningJob.jobRequest; + return filePath == jobRequest.filePath + && projectPartId == jobRequest.projectPartId; + }; + + return Utils::anyOf(m_running.values(), hasJobRequest); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangjobs.h b/src/tools/clangbackend/ipcsource/clangjobs.h new file mode 100644 index 0000000000..30e2fef7d8 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangjobs.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangjobqueue.h" + +#include <clangbackendipc/clangcodemodelclientinterface.h> + +#include <QFuture> + +namespace ClangBackEnd { + +class ClangCodeModelClientInterface; +class IAsyncJob; +class ProjectParts; +class TranslationUnits; +class UnsavedFiles; + +class Jobs +{ +public: + struct RunningJob { + JobRequest jobRequest; + QFuture<void> future; + }; + using RunningJobs = QHash<IAsyncJob *, RunningJob>; + +public: + Jobs(TranslationUnits &translationUnits, + UnsavedFiles &unsavedFiles, + ProjectParts &projects, + ClangCodeModelClientInterface &client); + ~Jobs(); + + void add(const JobRequest &job); + + JobRequests process(); + +public /*for tests*/: + int runningJobs() const; + JobRequests queue() const; + bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; + +private: + JobRequests runJobs(const JobRequests &jobRequest); + bool runJob(const JobRequest &jobRequest); + void onJobFinished(IAsyncJob *asyncJob); + +private: + TranslationUnits &m_translationUnits; + UnsavedFiles &m_unsavedFiles; + ProjectParts &m_projectParts; + ClangCodeModelClientInterface &m_client; + + JobQueue m_queue; + RunningJobs m_running; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp new file mode 100644 index 0000000000..ce9106d5cb --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangrequestdocumentannotationsjob.h" + +#include <clangbackendipc/clangbackendipcdebugutils.h> +#include <clangbackendipc/documentannotationschangedmessage.h> +#include <clangbackendipc/clangcodemodelclientinterface.h> + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +static RequestDocumentAnnotationsJob::AsyncResult runAsyncHelper( + const TranslationUnitCore &translationUnitCore) +{ + TIME_SCOPE_DURATION("RequestDocumentAnnotationsJobRunner"); + + RequestDocumentAnnotationsJob::AsyncResult asyncResult; + + try { + translationUnitCore.extractDocumentAnnotations(asyncResult.diagnostics, + asyncResult.highlightingMarks, + asyncResult.skippedSourceRanges); + } catch (const std::exception &exception) { + qWarning() << "Error in RequestDocumentAnnotationsJobRunner:" << exception.what(); + } + + return asyncResult; +} + +bool RequestDocumentAnnotationsJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations, return false); + + try { + m_pinnedTranslationUnit = context().translationUnitForJobRequest(); + m_pinnedFileContainer = m_pinnedTranslationUnit.fileContainer(); + + const TranslationUnitCore translationUnitCore = m_pinnedTranslationUnit.translationUnitCore(); + setRunner([translationUnitCore]() { + return runAsyncHelper(translationUnitCore); + }); + + } catch (const std::exception &exception) { + qWarning() << "Error in RequestDocumentAnnotationsJob::prepareAsyncRun:" << exception.what(); + return false; + } + + return true; +} + +void RequestDocumentAnnotationsJob::finalizeAsyncRun() +{ + if (context().isDocumentOpen()) { + const AsyncResult result = asyncResult(); + sendAnnotations(result); + } +} + +void RequestDocumentAnnotationsJob::sendAnnotations( + const RequestDocumentAnnotationsJob::AsyncResult &result) +{ + const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, + result.diagnostics, + result.highlightingMarks, + result.skippedSourceRanges); + + context().client->documentAnnotationsChanged(message); +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h new file mode 100644 index 0000000000..f44c50b190 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangrequestdocumentannotationsjob.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangasyncjob.h" +#include "clangtranslationunit.h" + +#include <clangbackendipc/diagnosticcontainer.h> +#include <clangbackendipc/highlightingmarkcontainer.h> +#include <clangbackendipc/sourcerangecontainer.h> + +namespace ClangBackEnd { + +struct RequestDocumentAnnotationsJobResult +{ + QVector<ClangBackEnd::DiagnosticContainer> diagnostics; + QVector<HighlightingMarkContainer> highlightingMarks; + QVector<SourceRangeContainer> skippedSourceRanges; +}; + +class RequestDocumentAnnotationsJob : public AsyncJob<RequestDocumentAnnotationsJobResult> +{ +public: + using AsyncResult = RequestDocumentAnnotationsJobResult; + + bool prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + void sendAnnotations(const AsyncResult &result); + +private: + TranslationUnit m_pinnedTranslationUnit; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp index f271401b36..8907a285bd 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.cpp @@ -25,19 +25,10 @@ #include "clangtranslationunit.h" -#include "cursor.h" -#include "clangfilepath.h" #include "clangstring.h" #include "clangunsavedfilesshallowarguments.h" #include "codecompleter.h" -#include "commandlinearguments.h" -#include "diagnosticcontainer.h" -#include "diagnosticset.h" #include "projectpart.h" -#include "skippedsourceranges.h" -#include "sourcelocation.h" -#include "sourcerange.h" -#include "highlightingmarks.h" #include "translationunitfilenotexitexception.h" #include "translationunitisnullexception.h" #include "translationunitparseerrorexception.h" @@ -69,19 +60,22 @@ public: public: TranslationUnits &translationUnits; - time_point lastProjectPartChangeTimePoint; - QSet<Utf8String> dependedFilePaths; + + const Utf8String filePath; + const Utf8StringVector fileArguments; + ProjectPart projectPart; - Utf8StringVector fileArguments; - Utf8String filePath; + time_point lastProjectPartChangeTimePoint; + CXTranslationUnit translationUnit = nullptr; - CXErrorCode parseErrorCode = CXError_Success; - int reparseErrorCode = 0; CXIndex index = nullptr; + + QSet<Utf8String> dependedFilePaths; + uint documentRevision = 0; + time_point needsToBeReparsedChangeTimePoint; + bool hasParseOrReparseFailed = false; bool needsToBeReparsed = false; - bool hasNewDiagnostics = true; - bool hasNewHighlightingMarks = true; bool isUsedByCurrentEditor = false; bool isVisibleInEditor = false; }; @@ -91,10 +85,11 @@ TranslationUnitData::TranslationUnitData(const Utf8String &filePath, const Utf8StringVector &fileArguments, TranslationUnits &translationUnits) : translationUnits(translationUnits), - lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), - projectPart(projectPart), + filePath(filePath), fileArguments(fileArguments), - filePath(filePath) + projectPart(projectPart), + lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), + needsToBeReparsedChangeTimePoint(lastProjectPartChangeTimePoint) { dependedFilePaths.insert(filePath); } @@ -119,29 +114,20 @@ TranslationUnit::TranslationUnit(const Utf8String &filePath, checkIfFileExists(); } -bool TranslationUnit::isNull() const -{ - return !d; -} - -void TranslationUnit::setIsUsedByCurrentEditor(bool isUsedByCurrentEditor) -{ - d->isUsedByCurrentEditor = isUsedByCurrentEditor; -} +TranslationUnit::~TranslationUnit() = default; +TranslationUnit::TranslationUnit(const TranslationUnit &) = default; +TranslationUnit &TranslationUnit::operator=(const TranslationUnit &) = default; -bool TranslationUnit::isUsedByCurrentEditor() const +TranslationUnit::TranslationUnit(TranslationUnit &&other) + : d(std::move(other.d)) { - return d->isUsedByCurrentEditor; } -void TranslationUnit::setIsVisibleInEditor(bool isVisibleInEditor) +TranslationUnit &TranslationUnit::operator=(TranslationUnit &&other) { - d->isVisibleInEditor = isVisibleInEditor; -} + d = std::move(other.d); -bool TranslationUnit::isVisibleInEditor() const -{ - return d->isVisibleInEditor; + return *this; } void TranslationUnit::reset() @@ -149,54 +135,16 @@ void TranslationUnit::reset() d.reset(); } -void TranslationUnit::parse() const -{ - checkIfNull(); - - const TranslationUnitUpdateInput updateInput = createUpdateInput(); - TranslationUnitUpdateResult result = translationUnitCore().parse(updateInput); - - incorporateUpdaterResult(result); -} - -void TranslationUnit::reparse() const -{ - parse(); // TODO: Remove - - const TranslationUnitUpdateInput updateInput = createUpdateInput(); - TranslationUnitUpdateResult result = translationUnitCore().reparse(updateInput); - - incorporateUpdaterResult(result); -} - -bool TranslationUnit::parseWasSuccessful() const -{ - return d->parseErrorCode == CXError_Success; -} - -bool TranslationUnit::reparseWasSuccessful() const -{ - return d->reparseErrorCode == 0; -} - -CXIndex &TranslationUnit::index() const -{ - checkIfNull(); - - return d->index; -} - -CXTranslationUnit &TranslationUnit::cxTranslationUnit() const +bool TranslationUnit::isNull() const { - checkIfNull(); - checkIfFileExists(); - - return d->translationUnit; + return !d; } -UnsavedFile TranslationUnit::unsavedFile() const +bool TranslationUnit::isIntact() const { - return unsavedFiles().unsavedFile(filePath()); + return !isNull() + && fileExists() + && !d->hasParseOrReparseFailed; } Utf8String TranslationUnit::filePath() const @@ -213,13 +161,6 @@ Utf8StringVector TranslationUnit::fileArguments() const return d->fileArguments; } -Utf8String TranslationUnit::projectPartId() const -{ - checkIfNull(); - - return d->projectPart.projectPartId(); -} - FileContainer TranslationUnit::fileContainer() const { checkIfNull(); @@ -231,137 +172,111 @@ FileContainer TranslationUnit::fileContainer() const d->documentRevision); } -const ProjectPart &TranslationUnit::projectPart() const +Utf8String TranslationUnit::projectPartId() const { checkIfNull(); - return d->projectPart; + return d->projectPart.projectPartId(); } -void TranslationUnit::setDocumentRevision(uint revision) +const ProjectPart &TranslationUnit::projectPart() const { - d->documentRevision = revision; -} + checkIfNull(); -uint TranslationUnit::documentRevision() const -{ - return d->documentRevision; + return d->projectPart; } -const time_point &TranslationUnit::lastProjectPartChangeTimePoint() const +const time_point TranslationUnit::lastProjectPartChangeTimePoint() const { - return d->lastProjectPartChangeTimePoint; -} + checkIfNull(); -bool TranslationUnit::isNeedingReparse() const -{ - return d->needsToBeReparsed; + return d->lastProjectPartChangeTimePoint; } -bool TranslationUnit::hasNewDiagnostics() const +bool TranslationUnit::isProjectPartOutdated() const { - return d->hasNewDiagnostics; -} + checkIfNull(); -bool TranslationUnit::hasNewHighlightingMarks() const -{ - return d->hasNewHighlightingMarks; + return d->projectPart.lastChangeTimePoint() >= d->lastProjectPartChangeTimePoint; } -DiagnosticSet TranslationUnit::diagnostics() const +uint TranslationUnit::documentRevision() const { - d->hasNewDiagnostics = false; + checkIfNull(); - return translationUnitCore().diagnostics(); + return d->documentRevision; } -QVector<ClangBackEnd::DiagnosticContainer> TranslationUnit::mainFileDiagnostics() const +void TranslationUnit::setDocumentRevision(uint revision) { - d->hasNewDiagnostics = false; + checkIfNull(); - return translationUnitCore().mainFileDiagnostics(); + d->documentRevision = revision; } -const QSet<Utf8String> &TranslationUnit::dependedFilePaths() const +bool TranslationUnit::isUsedByCurrentEditor() const { - cxTranslationUnit(); + checkIfNull(); - return d->dependedFilePaths; + return d->isUsedByCurrentEditor; } -void TranslationUnit::setDirtyIfProjectPartIsOutdated() +void TranslationUnit::setIsUsedByCurrentEditor(bool isUsedByCurrentEditor) { - if (projectPartIsOutdated()) - setDirty(); -} + checkIfNull(); -void TranslationUnit::setDirtyIfDependencyIsMet(const Utf8String &filePath) -{ - if (d->dependedFilePaths.contains(filePath) && isMainFileAndExistsOrIsOtherFile(filePath)) - setDirty(); + d->isUsedByCurrentEditor = isUsedByCurrentEditor; } -HighlightingMarks TranslationUnit::highlightingMarks() const +bool TranslationUnit::isVisibleInEditor() const { - d->hasNewHighlightingMarks = false; + checkIfNull(); - return translationUnitCore().highlightingMarks(); + return d->isVisibleInEditor; } -void TranslationUnit::checkIfNull() const +void TranslationUnit::setIsVisibleInEditor(bool isVisibleInEditor) { - if (isNull()) - throw TranslationUnitIsNullException(); -} + checkIfNull(); -void TranslationUnit::checkIfFileExists() const -{ - if (!fileExists()) - throw TranslationUnitFileNotExitsException(d->filePath); + d->isVisibleInEditor = isVisibleInEditor; } -bool TranslationUnit::projectPartIsOutdated() const +time_point TranslationUnit::isNeededReparseChangeTimePoint() const { - return d->projectPart.lastChangeTimePoint() >= d->lastProjectPartChangeTimePoint; -} + checkIfNull(); -void TranslationUnit::setDirty() -{ - d->needsToBeReparsed = true; - d->hasNewDiagnostics = true; - d->hasNewHighlightingMarks = true; + return d->needsToBeReparsedChangeTimePoint; } -bool TranslationUnit::isMainFileAndExistsOrIsOtherFile(const Utf8String &filePath) const +bool TranslationUnit::isNeedingReparse() const { - if (filePath == d->filePath) - return QFileInfo::exists(d->filePath); + checkIfNull(); - return true; + return d->needsToBeReparsed; } -void TranslationUnit::checkParseErrorCode() const +void TranslationUnit::setDirtyIfProjectPartIsOutdated() { - if (!parseWasSuccessful()) { - throw TranslationUnitParseErrorException(d->filePath, - d->projectPart.projectPartId(), - d->parseErrorCode); - } + if (isProjectPartOutdated()) + setDirty(); } -bool TranslationUnit::fileExists() const +void TranslationUnit::setDirtyIfDependencyIsMet(const Utf8String &filePath) { - return QFileInfo::exists(d->filePath.toString()); + if (d->dependedFilePaths.contains(filePath) && isMainFileAndExistsOrIsOtherFile(filePath)) + setDirty(); } TranslationUnitUpdateInput TranslationUnit::createUpdateInput() const { TranslationUnitUpdateInput updateInput; + updateInput.parseNeeded = isProjectPartOutdated(); updateInput.reparseNeeded = isNeedingReparse(); - updateInput.parseNeeded = projectPartIsOutdated(); + updateInput.needsToBeReparsedChangeTimePoint = d->needsToBeReparsedChangeTimePoint; updateInput.filePath = filePath(); updateInput.fileArguments = fileArguments(); - updateInput.unsavedFiles = unsavedFiles(); + updateInput.unsavedFiles = d->translationUnits.unsavedFiles(); updateInput.projectId = projectPart().projectPartId(); updateInput.projectArguments = projectPart().arguments(); @@ -371,66 +286,102 @@ TranslationUnitUpdateInput TranslationUnit::createUpdateInput() const TranslationUnitUpdater TranslationUnit::createUpdater() const { const TranslationUnitUpdateInput updateInput = createUpdateInput(); - TranslationUnitUpdater updater(index(), d->translationUnit, updateInput); + TranslationUnitUpdater updater(d->index, d->translationUnit, updateInput); return updater; } +void TranslationUnit::setHasParseOrReparseFailed(bool hasFailed) +{ + d->hasParseOrReparseFailed = hasFailed; +} + void TranslationUnit::incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const { + d->hasParseOrReparseFailed = result.hasParseOrReparseFailed; + if (d->hasParseOrReparseFailed) { + d->needsToBeReparsed = false; + return; + } + if (result.parseTimePointIsSet) d->lastProjectPartChangeTimePoint = result.parseTimePoint; - d->dependedFilePaths = result.dependedOnFilePaths; + if (result.parseTimePointIsSet || result.reparsed) + d->dependedFilePaths = result.dependedOnFilePaths; + d->translationUnits.addWatchedFiles(d->dependedFilePaths); - if (result.reparsed) + if (result.reparsed + && result.needsToBeReparsedChangeTimePoint == d->needsToBeReparsedChangeTimePoint) { d->needsToBeReparsed = false; + } } -bool TranslationUnit::isIntact() const +TranslationUnitCore TranslationUnit::translationUnitCore() const { - return !isNull() - && fileExists() - && parseWasSuccessful() - && reparseWasSuccessful(); + checkIfNull(); + + return TranslationUnitCore(d->filePath, d->index, d->translationUnit); } -CommandLineArguments TranslationUnit::commandLineArguments() const +void TranslationUnit::parse() const { - return createUpdater().commandLineArguments(); + checkIfNull(); + + const TranslationUnitUpdateInput updateInput = createUpdateInput(); + TranslationUnitUpdateResult result = translationUnitCore().parse(updateInput); + + incorporateUpdaterResult(result); } -TranslationUnitCore TranslationUnit::translationUnitCore() const +void TranslationUnit::reparse() const { - return TranslationUnitCore(d->filePath, d->index, d->translationUnit); + checkIfNull(); + + const TranslationUnitUpdateInput updateInput = createUpdateInput(); + TranslationUnitUpdateResult result = translationUnitCore().reparse(updateInput); + + incorporateUpdaterResult(result); } -uint TranslationUnit::unsavedFilesCount() const +const QSet<Utf8String> TranslationUnit::dependedFilePaths() const { - return unsavedFiles().count(); + checkIfNull(); + checkIfFileExists(); + + return d->dependedFilePaths; } -UnsavedFiles TranslationUnit::unsavedFiles() const +void TranslationUnit::setDirty() { - return d->translationUnits.unsavedFiles(); + d->needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); + d->needsToBeReparsed = true; } -TranslationUnit::~TranslationUnit() = default; +void TranslationUnit::checkIfNull() const +{ + if (isNull()) + throw TranslationUnitIsNullException(); +} -TranslationUnit::TranslationUnit(const TranslationUnit &) = default; -TranslationUnit &TranslationUnit::operator=(const TranslationUnit &) = default; +void TranslationUnit::checkIfFileExists() const +{ + if (!fileExists()) + throw TranslationUnitFileNotExitsException(d->filePath); +} -TranslationUnit::TranslationUnit(TranslationUnit &&other) - : d(std::move(other.d)) +bool TranslationUnit::fileExists() const { + return QFileInfo::exists(d->filePath.toString()); } -TranslationUnit &TranslationUnit::operator=(TranslationUnit &&other) +bool TranslationUnit::isMainFileAndExistsOrIsOtherFile(const Utf8String &filePath) const { - d = std::move(other.d); + if (filePath == d->filePath) + return QFileInfo::exists(d->filePath); - return *this; + return true; } bool operator==(const TranslationUnit &first, const TranslationUnit &second) diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunit.h b/src/tools/clangbackend/ipcsource/clangtranslationunit.h index 36e03fd4dd..e3ec05fa87 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunit.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunit.h @@ -33,11 +33,11 @@ #include <clang-c/Index.h> +#include <QSet> #include <QtGlobal> #include <chrono> #include <memory> -#include <QSet> class Utf8String; @@ -46,20 +46,9 @@ namespace ClangBackEnd { class TranslationUnitCore; class TranslationUnitData; class TranslationUnitUpdateResult; -class CodeCompleter; -class UnsavedFile; -class UnsavedFiles; class ProjectPart; -class DiagnosticContainer; -class DiagnosticSet; class FileContainer; -class HighlightingMarks; class TranslationUnits; -class CommandLineArguments; -class Cursor; -class SourceLocation; -class SourceRange; -class SkippedSourceRanges; using time_point = std::chrono::steady_clock::time_point; @@ -85,76 +74,54 @@ public: TranslationUnit(TranslationUnit &&cxTranslationUnit); TranslationUnit &operator=(TranslationUnit &&cxTranslationUnit); - bool isNull() const; - - void setIsUsedByCurrentEditor(bool isUsedByCurrentEditor); - bool isUsedByCurrentEditor() const; - - void setIsVisibleInEditor(bool isVisibleInEditor); - bool isVisibleInEditor() const; - void reset(); - void parse() const; - void reparse() const; + bool isNull() const; bool isIntact() const; - CXIndex &index() const; - - CXTranslationUnit &cxTranslationUnit() const; - - UnsavedFile unsavedFile() const; - UnsavedFiles unsavedFiles() const; - uint unsavedFilesCount() const; - Utf8String filePath() const; Utf8StringVector fileArguments() const; - Utf8String projectPartId() const; FileContainer fileContainer() const; + + Utf8String projectPartId() const; const ProjectPart &projectPart() const; + const time_point lastProjectPartChangeTimePoint() const; + bool isProjectPartOutdated() const; - void setDocumentRevision(uint revision); uint documentRevision() const; + void setDocumentRevision(uint revision); - const time_point &lastProjectPartChangeTimePoint() const; - - bool isNeedingReparse() const; - - // TODO: Remove the following two - bool hasNewDiagnostics() const; - bool hasNewHighlightingMarks() const; - - // TODO: Remove the following two - DiagnosticSet diagnostics() const; - QVector<DiagnosticContainer> mainFileDiagnostics() const; + bool isUsedByCurrentEditor() const; + void setIsUsedByCurrentEditor(bool isUsedByCurrentEditor); - const QSet<Utf8String> &dependedFilePaths() const; + bool isVisibleInEditor() const; + void setIsVisibleInEditor(bool isVisibleInEditor); + bool isNeedingReparse() const; void setDirtyIfProjectPartIsOutdated(); void setDirtyIfDependencyIsMet(const Utf8String &filePath); - CommandLineArguments commandLineArguments() const; - - // TODO: Remove - HighlightingMarks highlightingMarks() const; + TranslationUnitUpdateInput createUpdateInput() const; + void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const; TranslationUnitCore translationUnitCore() const; - bool projectPartIsOutdated() const; +public: // for tests + void parse() const; + void reparse() const; + const QSet<Utf8String> dependedFilePaths() const; + TranslationUnitUpdater createUpdater() const; + void setHasParseOrReparseFailed(bool hasFailed); + time_point isNeededReparseChangeTimePoint() const; private: void setDirty(); + void checkIfNull() const; void checkIfFileExists() const; - bool isMainFileAndExistsOrIsOtherFile(const Utf8String &filePath) const; - void checkParseErrorCode() const; - bool parseWasSuccessful() const; - bool reparseWasSuccessful() const; - bool fileExists() const; - TranslationUnitUpdateInput createUpdateInput() const; - TranslationUnitUpdater createUpdater() const; - void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const; + bool fileExists() const; + bool isMainFileAndExistsOrIsOtherFile(const Utf8String &filePath) const; private: mutable std::shared_ptr<TranslationUnitData> d; diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp index 9a09170c43..c907b6705d 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.cpp @@ -28,8 +28,6 @@ #include "clangfilepath.h" #include "clangstring.h" #include "clangunsavedfilesshallowarguments.h" -#include "translationunitparseerrorexception.h" -#include "translationunitreparseerrorexception.h" #include <QLoggingCategory> @@ -105,11 +103,15 @@ void TranslationUnitUpdater::createTranslationUnitIfNeeded() defaultParseOptions(), &m_cxTranslationUnit); - checkParseErrorCode(); - updateIncludeFilePaths(); - updateLastProjectPartChangeTimePoint(); + if (parseWasSuccessful()) { + updateIncludeFilePaths(); + updateLastProjectPartChangeTimePoint(); + } else { + qWarning() << "Parsing" << m_in.filePath << "failed:" << m_parseErrorCode; + m_out.hasParseOrReparseFailed = true; + } } } @@ -129,11 +131,16 @@ void TranslationUnitUpdater::reparse() unsaved.data(), clang_defaultReparseOptions(m_cxTranslationUnit)); - checkReparseErrorCode(); - updateIncludeFilePaths(); + if (reparseWasSuccessful()) { + updateIncludeFilePaths(); - m_out.reparsed = true; + m_out.reparsed = true; + m_out.needsToBeReparsedChangeTimePoint = m_in.needsToBeReparsedChangeTimePoint; + } else { + qWarning() << "Reparsing" << m_in.filePath << "failed:" << m_reparseErrorCode; + m_out.hasParseOrReparseFailed = true; + } } void TranslationUnitUpdater::updateIncludeFilePaths() @@ -146,24 +153,6 @@ void TranslationUnitUpdater::updateIncludeFilePaths() const_cast<TranslationUnitUpdater *>(this)); } -void TranslationUnitUpdater::checkParseErrorCode() const -{ - if (!parseWasSuccessful()) { - throw TranslationUnitParseErrorException(m_in.filePath, - m_in.projectId, - m_parseErrorCode); - } -} - -void TranslationUnitUpdater::checkReparseErrorCode() const -{ - if (!reparseWasSuccessful()) { - throw TranslationUnitReparseErrorException(m_in.filePath, - m_in.projectId, - m_reparseErrorCode); - } -} - uint TranslationUnitUpdater::defaultParseOptions() { return CXTranslationUnit_CacheCompletionResults diff --git a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h index a7a9d88f23..cb7a06b7f6 100644 --- a/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h +++ b/src/tools/clangbackend/ipcsource/clangtranslationunitupdater.h @@ -41,9 +41,10 @@ using time_point = std::chrono::steady_clock::time_point; class TranslationUnitUpdateInput { public: - bool reparseNeeded = false; bool parseNeeded = false; + bool reparseNeeded = false; + time_point needsToBeReparsedChangeTimePoint; Utf8String filePath; Utf8StringVector fileArguments; @@ -55,9 +56,12 @@ public: class TranslationUnitUpdateResult { public: + bool hasParseOrReparseFailed = false; + bool parseTimePointIsSet = false; time_point parseTimePoint; + time_point needsToBeReparsedChangeTimePoint; bool reparsed = false; QSet<Utf8String> dependedOnFilePaths; @@ -100,9 +104,6 @@ private: bool parseWasSuccessful() const; bool reparseWasSuccessful() const; - void checkReparseErrorCode() const; - void checkParseErrorCode() const; - private: CXIndex &m_cxIndex; CXTranslationUnit &m_cxTranslationUnit; diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp new file mode 100644 index 0000000000..2e61c50b92 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "clangupdatedocumentannotationsjob.h" + +#include <clangbackendipc/clangbackendipcdebugutils.h> +#include <clangbackendipc/clangcodemodelclientinterface.h> +#include <clangbackendipc/documentannotationschangedmessage.h> + +#include <utils/qtcassert.h> + +namespace ClangBackEnd { + +static UpdateDocumentAnnotationsJob::AsyncResult runAsyncHelper( + const TranslationUnitCore &translationUnitCore, + const TranslationUnitUpdateInput &translationUnitUpdatInput) +{ + TIME_SCOPE_DURATION("UpdateDocumentAnnotationsJobRunner"); + + UpdateDocumentAnnotationsJob::AsyncResult asyncResult; + + try { + // Update + asyncResult.updateResult = translationUnitCore.update(translationUnitUpdatInput); + + // Collect + translationUnitCore.extractDocumentAnnotations(asyncResult.diagnostics, + asyncResult.highlightingMarks, + asyncResult.skippedSourceRanges); + + } catch (const std::exception &exception) { + qWarning() << "Error in UpdateDocumentAnnotationsJobRunner:" << exception.what(); + } + + return asyncResult; +} + +bool UpdateDocumentAnnotationsJob::prepareAsyncRun() +{ + const JobRequest jobRequest = context().jobRequest; + QTC_ASSERT(jobRequest.type == JobRequest::Type::UpdateDocumentAnnotations, return false); + + try { + m_pinnedTranslationUnit = context().translationUnitForJobRequest(); + m_pinnedFileContainer = m_pinnedTranslationUnit.fileContainer(); + + const TranslationUnitCore translationUnitCore = m_pinnedTranslationUnit.translationUnitCore(); + const TranslationUnitUpdateInput updateInput = m_pinnedTranslationUnit.createUpdateInput(); + setRunner([translationUnitCore, updateInput]() { + return runAsyncHelper(translationUnitCore, updateInput); + }); + + } catch (const std::exception &exception) { + qWarning() << "Error in UpdateDocumentAnnotationsJob::prepareAsyncRun:" << exception.what(); + return false; + } + + return true; +} + +void UpdateDocumentAnnotationsJob::finalizeAsyncRun() +{ + if (!context().isOutdated()) { + const AsyncResult result = asyncResult(); + + incorporateUpdaterResult(result); + sendAnnotations(result); + } +} + +void UpdateDocumentAnnotationsJob::incorporateUpdaterResult(const AsyncResult &result) +{ + m_pinnedTranslationUnit.incorporateUpdaterResult(result.updateResult); +} + +void UpdateDocumentAnnotationsJob::sendAnnotations(const AsyncResult &result) +{ + const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, + result.diagnostics, + result.highlightingMarks, + result.skippedSourceRanges); + + context().client->documentAnnotationsChanged(message); +} + +} // namespace ClangBackEnd + diff --git a/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h new file mode 100644 index 0000000000..643df07571 --- /dev/null +++ b/src/tools/clangbackend/ipcsource/clangupdatedocumentannotationsjob.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangasyncjob.h" +#include "clangtranslationunit.h" +#include "clangtranslationunitupdater.h" + +#include <clangbackendipc/diagnosticcontainer.h> +#include <clangbackendipc/highlightingmarkcontainer.h> +#include <clangbackendipc/sourcerangecontainer.h> + +namespace ClangBackEnd { + +struct UpdateDocumentAnnotationsJobResult +{ + TranslationUnitUpdateResult updateResult; + + QVector<ClangBackEnd::DiagnosticContainer> diagnostics; + QVector<HighlightingMarkContainer> highlightingMarks; + QVector<SourceRangeContainer> skippedSourceRanges; +}; + +class UpdateDocumentAnnotationsJob : public AsyncJob<UpdateDocumentAnnotationsJobResult> +{ +public: + using AsyncResult = UpdateDocumentAnnotationsJobResult; + + bool prepareAsyncRun() override; + void finalizeAsyncRun() override; + +private: + void incorporateUpdaterResult(const AsyncResult &result); + void sendAnnotations(const AsyncResult &result); + +private: + TranslationUnit m_pinnedTranslationUnit; + FileContainer m_pinnedFileContainer; +}; + +} // namespace ClangBackEnd diff --git a/src/tools/clangbackend/ipcsource/translationunits.cpp b/src/tools/clangbackend/ipcsource/translationunits.cpp index 2e90e0bf81..5fde27b8b9 100644 --- a/src/tools/clangbackend/ipcsource/translationunits.cpp +++ b/src/tools/clangbackend/ipcsource/translationunits.cpp @@ -128,14 +128,10 @@ const TranslationUnit &TranslationUnits::translationUnit(const FileContainer &fi return translationUnit(fileContainer.filePath(), fileContainer.projectPartId()); } -bool TranslationUnits::hasTranslationUnit(const Utf8String &filePath) const +bool TranslationUnits::hasTranslationUnit(const Utf8String &filePath, + const Utf8String &projectPartId) const { - return std::any_of(translationUnits_.cbegin(), - translationUnits_.cend(), - [&filePath] (const TranslationUnit &translationUnit) - { - return translationUnit.filePath() == filePath; - }); + return hasTranslationUnit(FileContainer(filePath, projectPartId)); } const std::vector<TranslationUnit> &TranslationUnits::translationUnits() const @@ -171,75 +167,6 @@ void TranslationUnits::setTranslationUnitsDirtyIfProjectPartChanged() translationUnit.setDirtyIfProjectPartIsOutdated(); } -DocumentAnnotationsSendState TranslationUnits::sendDocumentAnnotations() -{ - auto documentAnnotationsSendState = sendDocumentAnnotationsForCurrentEditor(); - if (documentAnnotationsSendState == DocumentAnnotationsSendState::NoDocumentAnnotationsSent) - documentAnnotationsSendState = sendDocumentAnnotationsForVisibleEditors(); - - return documentAnnotationsSendState; -} - -template<class Predicate> -DocumentAnnotationsSendState TranslationUnits::sendDocumentAnnotations(Predicate predicate) -{ - auto foundTranslationUnit = std::find_if(translationUnits_.begin(), - translationUnits_.end(), - predicate); - - if (foundTranslationUnit != translationUnits().end()) { - sendDocumentAnnotations(*foundTranslationUnit); - return DocumentAnnotationsSendState::MaybeThereAreDocumentAnnotations; - } - - return DocumentAnnotationsSendState::NoDocumentAnnotationsSent; -} - -namespace { - -bool translationUnitHasNewDocumentAnnotations(const TranslationUnit &translationUnit) -{ - return translationUnit.isIntact() - && (translationUnit.hasNewDiagnostics() - || translationUnit.hasNewHighlightingMarks()); -} - -} - -DocumentAnnotationsSendState TranslationUnits::sendDocumentAnnotationsForCurrentEditor() -{ - auto hasDocumentAnnotationsForCurrentEditor = [] (const TranslationUnit &translationUnit) { - return translationUnit.isUsedByCurrentEditor() - && translationUnitHasNewDocumentAnnotations(translationUnit); - }; - - return sendDocumentAnnotations(hasDocumentAnnotationsForCurrentEditor); -} - -DocumentAnnotationsSendState TranslationUnits::sendDocumentAnnotationsForVisibleEditors() -{ - auto hasDocumentAnnotationsForVisibleEditor = [] (const TranslationUnit &translationUnit) { - return translationUnit.isVisibleInEditor() - && translationUnitHasNewDocumentAnnotations(translationUnit); - }; - - return sendDocumentAnnotations(hasDocumentAnnotationsForVisibleEditor); -} - -DocumentAnnotationsSendState TranslationUnits::sendDocumentAnnotationsForAll() -{ - auto hasDocumentAnnotations = [] (const TranslationUnit &translationUnit) { - return translationUnitHasNewDocumentAnnotations(translationUnit); - }; - - return sendDocumentAnnotations(hasDocumentAnnotations); -} - -void TranslationUnits::setSendDocumentAnnotationsCallback(SendDocumentAnnotationsCallback &&callback) -{ - sendDocumentAnnotationsCallback = std::move(callback); -} - QVector<FileContainer> TranslationUnits::newerFileContainers(const QVector<FileContainer> &fileContainers) const { QVector<FileContainer> newerContainers; @@ -367,18 +294,6 @@ void TranslationUnits::checkIfTranslationUnitsForFilePathsDoesExists(const QVect } } -void TranslationUnits::sendDocumentAnnotations(const TranslationUnit &translationUnit) -{ - if (sendDocumentAnnotationsCallback) { - DocumentAnnotationsChangedMessage message(translationUnit.fileContainer(), - translationUnit.mainFileDiagnostics(), - translationUnit.highlightingMarks().toHighlightingMarksContainers(), - translationUnit.translationUnitCore().skippedSourceRanges().toSourceRangeContainers()); - - sendDocumentAnnotationsCallback(std::move(message)); - } -} - void TranslationUnits::removeTranslationUnits(const QVector<FileContainer> &fileContainers) { QVector<FileContainer> processedFileContainers = fileContainers; diff --git a/src/tools/clangbackend/ipcsource/translationunits.h b/src/tools/clangbackend/ipcsource/translationunits.h index 4081efd7ab..5607ebb665 100644 --- a/src/tools/clangbackend/ipcsource/translationunits.h +++ b/src/tools/clangbackend/ipcsource/translationunits.h @@ -32,28 +32,16 @@ #include <QVector> -#include <functional> #include <vector> namespace ClangBackEnd { class ProjectParts; class UnsavedFiles; -class DocumentAnnotationsChangedMessage; - -enum class DocumentAnnotationsSendState -{ - NoDocumentAnnotationsSent, - MaybeThereAreDocumentAnnotations, -}; class TranslationUnits { public: - using SendDocumentAnnotationsCallback - = std::function<void (const DocumentAnnotationsChangedMessage &)>; - -public: TranslationUnits(ProjectParts &projectParts, UnsavedFiles &unsavedFiles); std::vector<TranslationUnit> create(const QVector<FileContainer> &fileContainers); @@ -65,7 +53,8 @@ public: const TranslationUnit &translationUnit(const Utf8String &filePath, const Utf8String &projectPartId) const; const TranslationUnit &translationUnit(const FileContainer &fileContainer) const; - bool hasTranslationUnit(const Utf8String &filePath) const; + bool hasTranslationUnit(const Utf8String &filePath, const Utf8String &projectPartId) const; + bool hasTranslationUnitWithFilePath(const Utf8String &filePath) const; const std::vector<TranslationUnit> &translationUnits() const; @@ -77,13 +66,6 @@ public: void updateTranslationUnitsWithChangedDependencies(const QVector<FileContainer> &fileContainers); void setTranslationUnitsDirtyIfProjectPartChanged(); - DocumentAnnotationsSendState sendDocumentAnnotationsForCurrentEditor(); - DocumentAnnotationsSendState sendDocumentAnnotationsForVisibleEditors(); - DocumentAnnotationsSendState sendDocumentAnnotationsForAll(); - DocumentAnnotationsSendState sendDocumentAnnotations(); - - void setSendDocumentAnnotationsCallback(SendDocumentAnnotationsCallback &&callback); - QVector<FileContainer> newerFileContainers(const QVector<FileContainer> &fileContainers) const; const ClangFileSystemWatcher *clangFileSystemWatcher() const; @@ -95,7 +77,6 @@ private: std::vector<TranslationUnit> findAllTranslationUnitWithFilePath(const Utf8String &filePath); std::vector<TranslationUnit>::const_iterator findTranslationUnit(const Utf8String &filePath, const Utf8String &projectPartId) const; bool hasTranslationUnit(const FileContainer &fileContainer) const; - bool hasTranslationUnitWithFilePath(const Utf8String &filePath) const; void checkIfProjectPartExists(const Utf8String &projectFileName) const; void checkIfProjectPartsExists(const QVector<FileContainer> &fileContainers) const; void checkIfTranslationUnitsDoesNotExists(const QVector<FileContainer> &fileContainers) const; @@ -103,13 +84,8 @@ private: void removeTranslationUnits(const QVector<FileContainer> &fileContainers); - template<class Predicate> - DocumentAnnotationsSendState sendDocumentAnnotations(Predicate predicate); - void sendDocumentAnnotations(const TranslationUnit &translationUnit); - private: ClangFileSystemWatcher fileSystemWatcher; - SendDocumentAnnotationsCallback sendDocumentAnnotationsCallback; std::vector<TranslationUnit> translationUnits_; ProjectParts &projectParts; UnsavedFiles &unsavedFiles_; |