Merge remote-tracking branch 'origin/master' into 4.2

Change-Id: I96904f9c65b6c25bb4e04ca34e2d1acb27b8dd58
This commit is contained in:
Eike Ziller
2016-10-12 10:15:53 +02:00
131 changed files with 4220 additions and 505 deletions

View File

@@ -824,6 +824,9 @@
For example, to turn the first character in the line edit to upper For example, to turn the first character in the line edit to upper
case. case.
\li \c isPassword is a boolean value that specifies that the line edit
contains a password, which will be masked.
\endlist \endlist
\section2 Path Chooser \section2 Path Chooser

View File

@@ -63,7 +63,8 @@ Module {
"QT_CREATOR", "QT_CREATOR",
'IDE_LIBRARY_BASENAME="' + libDirName + '"', 'IDE_LIBRARY_BASENAME="' + libDirName + '"',
"QT_NO_CAST_TO_ASCII", "QT_NO_CAST_TO_ASCII",
"QT_RESTRICTED_CAST_FROM_ASCII" "QT_RESTRICTED_CAST_FROM_ASCII",
"QT_DISABLE_DEPRECATED_BEFORE=0x050600",
].concat(testsEnabled ? ["WITH_TESTS"] : []) ].concat(testsEnabled ? ["WITH_TESTS"] : [])
Rule { Rule {

View File

@@ -179,7 +179,11 @@ exists($$IDE_LIBRARY_PATH): LIBS *= -L$$IDE_LIBRARY_PATH # library path from ou
DEFINES += IDE_LIBRARY_BASENAME=\\\"$$IDE_LIBRARY_BASENAME\\\" DEFINES += IDE_LIBRARY_BASENAME=\\\"$$IDE_LIBRARY_BASENAME\\\"
} }
DEFINES += QT_CREATOR QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII DEFINES += \
QT_CREATOR \
QT_NO_CAST_TO_ASCII \
QT_RESTRICTED_CAST_FROM_ASCII \
QT_DISABLE_DEPRECATED_BEFORE=0x050600
!macx:DEFINES += QT_USE_FAST_OPERATOR_PLUS QT_USE_FAST_CONCATENATION !macx:DEFINES += QT_USE_FAST_OPERATOR_PLUS QT_USE_FAST_CONCATENATION
unix { unix {

View File

@@ -37,6 +37,7 @@ QDebug operator<<(QDebug debug, const DocumentAnnotationsChangedMessage &message
debug.nospace() << "DocumentAnnotationsChangedMessage(" debug.nospace() << "DocumentAnnotationsChangedMessage("
<< message.fileContainer() << message.fileContainer()
<< ", " << message.diagnostics().size() << ", " << message.diagnostics().size()
<< ", " << !message.firstHeaderErrorDiagnostic().text().isEmpty()
<< ", " << message.highlightingMarks().size() << ", " << message.highlightingMarks().size()
<< ", " << message.skippedPreprocessorRanges().size() << ", " << message.skippedPreprocessorRanges().size()
<< ")"; << ")";
@@ -49,6 +50,7 @@ void PrintTo(const DocumentAnnotationsChangedMessage &message, ::std::ostream* o
*os << "DocumentAnnotationsChangedMessage("; *os << "DocumentAnnotationsChangedMessage(";
PrintTo(message.fileContainer(), os); PrintTo(message.fileContainer(), os);
*os << "," << message.diagnostics().size(); *os << "," << message.diagnostics().size();
*os << "," << !message.firstHeaderErrorDiagnostic().text().isEmpty();
*os << "," << message.highlightingMarks().size(); *os << "," << message.highlightingMarks().size();
*os << "," << message.skippedPreprocessorRanges().size(); *os << "," << message.skippedPreprocessorRanges().size();
*os << ")"; *os << ")";

View File

@@ -41,10 +41,12 @@ public:
DocumentAnnotationsChangedMessage() = default; DocumentAnnotationsChangedMessage() = default;
DocumentAnnotationsChangedMessage(const FileContainer &fileContainer, DocumentAnnotationsChangedMessage(const FileContainer &fileContainer,
const QVector<DiagnosticContainer> &diagnostics, const QVector<DiagnosticContainer> &diagnostics,
const DiagnosticContainer &firstHeaderErrorDiagnostic_,
const QVector<HighlightingMarkContainer> &highlightingMarks, const QVector<HighlightingMarkContainer> &highlightingMarks,
const QVector<SourceRangeContainer> &skippedPreprocessorRanges) const QVector<SourceRangeContainer> &skippedPreprocessorRanges)
: fileContainer_(fileContainer), : fileContainer_(fileContainer),
diagnostics_(diagnostics), diagnostics_(diagnostics),
firstHeaderErrorDiagnostic_(firstHeaderErrorDiagnostic_),
highlightingMarks_(highlightingMarks), highlightingMarks_(highlightingMarks),
skippedPreprocessorRanges_(skippedPreprocessorRanges) skippedPreprocessorRanges_(skippedPreprocessorRanges)
{ {
@@ -60,6 +62,11 @@ public:
return diagnostics_; return diagnostics_;
} }
const DiagnosticContainer &firstHeaderErrorDiagnostic() const
{
return firstHeaderErrorDiagnostic_;
}
const QVector<HighlightingMarkContainer> &highlightingMarks() const const QVector<HighlightingMarkContainer> &highlightingMarks() const
{ {
return highlightingMarks_; return highlightingMarks_;
@@ -74,6 +81,7 @@ public:
{ {
out << message.fileContainer_; out << message.fileContainer_;
out << message.diagnostics_; out << message.diagnostics_;
out << message.firstHeaderErrorDiagnostic_;
out << message.highlightingMarks_; out << message.highlightingMarks_;
out << message.skippedPreprocessorRanges_; out << message.skippedPreprocessorRanges_;
@@ -84,6 +92,7 @@ public:
{ {
in >> message.fileContainer_; in >> message.fileContainer_;
in >> message.diagnostics_; in >> message.diagnostics_;
in >> message.firstHeaderErrorDiagnostic_;
in >> message.highlightingMarks_; in >> message.highlightingMarks_;
in >> message.skippedPreprocessorRanges_; in >> message.skippedPreprocessorRanges_;
@@ -95,6 +104,7 @@ public:
{ {
return first.fileContainer_ == second.fileContainer_ return first.fileContainer_ == second.fileContainer_
&& first.diagnostics_ == second.diagnostics_ && first.diagnostics_ == second.diagnostics_
&& first.firstHeaderErrorDiagnostic_ == second.firstHeaderErrorDiagnostic_
&& first.highlightingMarks_ == second.highlightingMarks_ && first.highlightingMarks_ == second.highlightingMarks_
&& first.skippedPreprocessorRanges_ == second.skippedPreprocessorRanges_; && first.skippedPreprocessorRanges_ == second.skippedPreprocessorRanges_;
} }
@@ -102,6 +112,7 @@ public:
private: private:
FileContainer fileContainer_; FileContainer fileContainer_;
QVector<DiagnosticContainer> diagnostics_; QVector<DiagnosticContainer> diagnostics_;
DiagnosticContainer firstHeaderErrorDiagnostic_;
QVector<HighlightingMarkContainer> highlightingMarks_; QVector<HighlightingMarkContainer> highlightingMarks_;
QVector<SourceRangeContainer> skippedPreprocessorRanges_; QVector<SourceRangeContainer> skippedPreprocessorRanges_;
}; };

View File

@@ -32,6 +32,7 @@
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <cpptools/abstracteditorsupport.h> #include <cpptools/abstracteditorsupport.h>
#include <cpptools/baseeditordocumentprocessor.h> #include <cpptools/baseeditordocumentprocessor.h>
@@ -68,6 +69,7 @@
#include <cplusplus/Icons.h> #include <cplusplus/Icons.h>
#include <QDir>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QLoggingCategory> #include <QLoggingCategory>
#include <QProcess> #include <QProcess>
@@ -180,7 +182,9 @@ void IpcReceiver::documentAnnotationsChanged(const DocumentAnnotationsChangedMes
const QString documentProjectPartId = CppTools::CppToolsBridge::projectPartIdForFile(filePath); const QString documentProjectPartId = CppTools::CppToolsBridge::projectPartIdForFile(filePath);
if (projectPartId == documentProjectPartId) { if (projectPartId == documentProjectPartId) {
const quint32 documentRevision = message.fileContainer().documentRevision(); const quint32 documentRevision = message.fileContainer().documentRevision();
processor->updateCodeWarnings(message.diagnostics(), documentRevision); processor->updateCodeWarnings(message.diagnostics(),
message.firstHeaderErrorDiagnostic(),
documentRevision);
processor->updateHighlighting(message.highlightingMarks(), processor->updateHighlighting(message.highlightingMarks(),
message.skippedPreprocessorRanges(), message.skippedPreprocessorRanges(),
documentRevision); documentRevision);
@@ -304,10 +308,16 @@ public:
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &) override {} void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &) override {}
}; };
enum { backEndStartTimeOutInMs = 10000 };
IpcCommunicator::IpcCommunicator() IpcCommunicator::IpcCommunicator()
: m_connection(&m_ipcReceiver) : m_connection(&m_ipcReceiver)
, m_ipcSender(new DummyIpcSender) , m_ipcSender(new DummyIpcSender)
{ {
m_backendStartTimeOut.setSingleShot(true);
connect(&m_backendStartTimeOut, &QTimer::timeout,
this, &IpcCommunicator::logStartTimeOut);
m_ipcReceiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); }); m_ipcReceiver.setAliveHandler([this]() { m_connection.resetProcessAliveTimer(); });
connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose, connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose,
@@ -326,8 +336,11 @@ IpcCommunicator::~IpcCommunicator()
void IpcCommunicator::initializeBackend() void IpcCommunicator::initializeBackend()
{ {
const QString clangBackEndProcessPath = backendProcessPath(); const QString clangBackEndProcessPath = backendProcessPath();
if (!QFileInfo(clangBackEndProcessPath).exists()) {
logExecutableDoesNotExist();
return;
}
qCDebug(log) << "Starting" << clangBackEndProcessPath; qCDebug(log) << "Starting" << clangBackEndProcessPath;
QTC_ASSERT(QFileInfo(clangBackEndProcessPath).exists(), return);
m_connection.setProcessAliveTimerInterval(30 * 1000); m_connection.setProcessAliveTimerInterval(30 * 1000);
m_connection.setProcessPath(clangBackEndProcessPath); m_connection.setProcessPath(clangBackEndProcessPath);
@@ -338,6 +351,7 @@ void IpcCommunicator::initializeBackend()
this, &IpcCommunicator::setupDummySender); this, &IpcCommunicator::setupDummySender);
m_connection.startProcessAndConnectToServerAsynchronously(); m_connection.startProcessAndConnectToServerAsynchronously();
m_backendStartTimeOut.start(backEndStartTimeOutInMs);
} }
static QStringList projectPartOptions(const CppTools::ProjectPart::Ptr &projectPart) static QStringList projectPartOptions(const CppTools::ProjectPart::Ptr &projectPart)
@@ -622,11 +636,11 @@ void IpcCommunicator::updateUnsavedFile(Core::IDocument *document)
void IpcCommunicator::onConnectedToBackend() void IpcCommunicator::onConnectedToBackend()
{ {
m_backendStartTimeOut.stop();
++m_connectedCount; ++m_connectedCount;
if (m_connectedCount > 1) { if (m_connectedCount > 1)
qWarning("Clang back end finished unexpectedly, restarted."); logRestartedDueToUnexpectedFinish();
qCDebug(log) << "Backend restarted, re-initializing with project data and unsaved files.";
}
m_ipcReceiver.deleteAndClearWaitingAssistProcessors(); m_ipcReceiver.deleteAndClearWaitingAssistProcessors();
m_ipcSender.reset(new IpcSender(m_connection)); m_ipcSender.reset(new IpcSender(m_connection));
@@ -645,6 +659,42 @@ void IpcCommunicator::setupDummySender()
m_ipcSender.reset(new DummyIpcSender); m_ipcSender.reset(new DummyIpcSender);
} }
void IpcCommunicator::logExecutableDoesNotExist()
{
const QString msg
= tr("Clang Code Model: Error: "
"The clangbackend executable \"%1\" does not exist.")
.arg(QDir::toNativeSeparators(backendProcessPath()));
logError(msg);
}
void IpcCommunicator::logStartTimeOut()
{
const QString msg
= tr("Clang Code Model: Error: "
"The clangbackend executable \"%1\" could not be started (timeout after %2ms).")
.arg(QDir::toNativeSeparators(backendProcessPath()))
.arg(backEndStartTimeOutInMs);
logError(msg);
}
void IpcCommunicator::logRestartedDueToUnexpectedFinish()
{
const QString msg
= tr("Clang Code Model: Error: "
"The clangbackend process has finished unexpectedly and was restarted.");
logError(msg);
}
void IpcCommunicator::logError(const QString &text)
{
Core::MessageManager::write(text, Core::MessageManager::Flash);
qWarning("%s", qPrintable(text));
}
void IpcCommunicator::initializeBackendWithCurrentData() void IpcCommunicator::initializeBackendWithCurrentData()
{ {
registerFallbackProjectPart(); registerFallbackProjectPart();

View File

@@ -171,12 +171,18 @@ private:
void onDisconnectedFromBackend(); void onDisconnectedFromBackend();
void onEditorAboutToClose(Core::IEditor *editor); void onEditorAboutToClose(Core::IEditor *editor);
void logExecutableDoesNotExist();
void logRestartedDueToUnexpectedFinish();
void logStartTimeOut();
void logError(const QString &text);
void updateTranslationUnitVisiblity(const Utf8String &currentEditorFilePath, void updateTranslationUnitVisiblity(const Utf8String &currentEditorFilePath,
const Utf8StringVector &visibleEditorsFilePaths); const Utf8StringVector &visibleEditorsFilePaths);
private: private:
IpcReceiver m_ipcReceiver; IpcReceiver m_ipcReceiver;
ClangBackEnd::ClangCodeModelConnectionClient m_connection; ClangBackEnd::ClangCodeModelConnectionClient m_connection;
QTimer m_backendStartTimeOut;
QScopedPointer<IpcSenderInterface> m_ipcSender; QScopedPointer<IpcSenderInterface> m_ipcSender;
int m_connectedCount = 0; int m_connectedCount = 0;
}; };

View File

@@ -41,6 +41,7 @@
#include <utils/proxyaction.h> #include <utils/proxyaction.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QFileInfo>
#include <QTextBlock> #include <QTextBlock>
namespace { namespace {

View File

@@ -129,9 +129,10 @@ enum IndentType { IndentDiagnostic, DoNotIndentDiagnostic };
QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnostic, QWidget *createDiagnosticLabel(const ClangBackEnd::DiagnosticContainer &diagnostic,
const QString &mainFilePath, const QString &mainFilePath,
IndentType indentType = DoNotIndentDiagnostic) IndentType indentType = DoNotIndentDiagnostic,
bool enableClickableFixits = true)
{ {
const bool hasFixit = !diagnostic.fixIts().isEmpty(); const bool hasFixit = enableClickableFixits ? !diagnostic.fixIts().isEmpty() : false;
const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped(); const QString diagnosticText = diagnostic.text().toString().toHtmlEscaped();
const QString text = clickableLocation(mainFilePath, diagnostic.location()) const QString text = clickableLocation(mainFilePath, diagnostic.location())
+ QStringLiteral(": ") + QStringLiteral(": ")
@@ -159,25 +160,35 @@ class MainDiagnosticWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic) MainDiagnosticWidget(const ClangBackEnd::DiagnosticContainer &diagnostic,
const ClangCodeModel::Internal::DisplayHints &displayHints)
{ {
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
auto *mainLayout = createLayout<QVBoxLayout>(); auto *mainLayout = createLayout<QVBoxLayout>();
// Set up header row: category + responsible option
const QString category = diagnostic.category();
const QString responsibleOption = diagnostic.enableOption();
const ClangBackEnd::SourceLocationContainer location = diagnostic.location(); const ClangBackEnd::SourceLocationContainer location = diagnostic.location();
auto *headerLayout = createLayout<QHBoxLayout>(); // Set up header row: category + responsible option
headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1); if (displayHints.showMainDiagnosticHeader) {
const QString category = diagnostic.category();
const QString responsibleOption = diagnostic.enableOption();
auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray")); auto *headerLayout = createLayout<QHBoxLayout>();
headerLayout->addWidget(responsibleOptionLabel, 0); headerLayout->addWidget(new QLabel(wrapInBoldTags(category)), 1);
mainLayout->addLayout(headerLayout);
auto *responsibleOptionLabel = new QLabel(wrapInColor(responsibleOption, "gray"));
headerLayout->addWidget(responsibleOptionLabel, 0);
mainLayout->addLayout(headerLayout);
}
// Set up main row: diagnostic text // Set up main row: diagnostic text
mainLayout->addWidget(createDiagnosticLabel(diagnostic, location.filePath())); const Utf8String mainFilePath = displayHints.showFileNameInMainDiagnostic
? Utf8String()
: location.filePath();
mainLayout->addWidget(createDiagnosticLabel(diagnostic,
mainFilePath,
DoNotIndentDiagnostic,
displayHints.enableClickableFixits));
setLayout(mainLayout); setLayout(mainLayout);
} }
@@ -186,26 +197,35 @@ public:
void addChildrenToLayout(const QString &mainFilePath, void addChildrenToLayout(const QString &mainFilePath,
const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator first, const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator first,
const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator last, const QVector<ClangBackEnd::DiagnosticContainer>::const_iterator last,
bool enableClickableFixits,
QLayout &boxLayout) QLayout &boxLayout)
{ {
for (auto it = first; it != last; ++it) for (auto it = first; it != last; ++it) {
boxLayout.addWidget(createDiagnosticLabel(*it, mainFilePath, IndentDiagnostic)); boxLayout.addWidget(createDiagnosticLabel(*it,
mainFilePath,
IndentDiagnostic,
enableClickableFixits));
}
} }
void setupChildDiagnostics(const QString &mainFilePath, void setupChildDiagnostics(const QString &mainFilePath,
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics, const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
bool enableClickableFixits,
QLayout &boxLayout) QLayout &boxLayout)
{ {
if (diagnostics.size() <= 10) { if (diagnostics.size() <= 10) {
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(), boxLayout); addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.end(),
enableClickableFixits, boxLayout);
} else { } else {
addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7, boxLayout); addChildrenToLayout(mainFilePath, diagnostics.begin(), diagnostics.begin() + 7,
enableClickableFixits, boxLayout);
auto ellipsisLabel = new QLabel(QStringLiteral("...")); auto ellipsisLabel = new QLabel(QStringLiteral("..."));
ellipsisLabel->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0); ellipsisLabel->setContentsMargins(childIndentationOnTheLeftInPixel, 0, 0, 0);
boxLayout.addWidget(ellipsisLabel); boxLayout.addWidget(ellipsisLabel);
addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(), boxLayout); addChildrenToLayout(mainFilePath, diagnostics.end() - 3, diagnostics.end(),
enableClickableFixits, boxLayout);
} }
} }
@@ -214,13 +234,18 @@ void setupChildDiagnostics(const QString &mainFilePath,
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target) void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic,
QLayout *target,
const DisplayHints &displayHints)
{ {
// Set up header and text row for main diagnostic // Set up header and text row for main diagnostic
target->addWidget(new MainDiagnosticWidget(diagnostic)); target->addWidget(new MainDiagnosticWidget(diagnostic, displayHints));
// Set up child rows for notes // Set up child rows for notes
setupChildDiagnostics(diagnostic.location().filePath(), diagnostic.children(), *target); setupChildDiagnostics(diagnostic.location().filePath(),
diagnostic.children(),
displayHints.enableClickableFixits,
*target);
} }
} // namespace Internal } // namespace Internal

View File

@@ -34,7 +34,15 @@ QT_END_NAMESPACE
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic, QLayout *target); struct DisplayHints {
bool showMainDiagnosticHeader = true;
bool showFileNameInMainDiagnostic = false;
bool enableClickableFixits = true;
};
void addToolTipToLayout(const ClangBackEnd::DiagnosticContainer &diagnostic,
QLayout *target,
const DisplayHints &displayHints = DisplayHints());
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -57,6 +57,8 @@
#include <utils/runextensions.h> #include <utils/runextensions.h>
#include <QTextBlock> #include <QTextBlock>
#include <QVBoxLayout>
#include <QWidget>
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
@@ -72,6 +74,11 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
, m_semanticHighlighter(document) , m_semanticHighlighter(document)
, m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false) , m_builtinProcessor(document, /*enableSemanticHighlighter=*/ false)
{ {
m_updateTranslationUnitTimer.setSingleShot(true);
m_updateTranslationUnitTimer.setInterval(350);
connect(&m_updateTranslationUnitTimer, &QTimer::timeout,
this, &ClangEditorDocumentProcessor::updateTranslationUnitIfProjectPartExists);
// Forwarding the semantic info from the builtin processor enables us to provide all // Forwarding the semantic info from the builtin processor enables us to provide all
// editor (widget) related features that are not yet implemented by the clang plugin. // editor (widget) related features that are not yet implemented by the clang plugin.
connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated, connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated,
@@ -82,6 +89,8 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor() ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
{ {
m_updateTranslationUnitTimer.stop();
m_parserWatcher.cancel(); m_parserWatcher.cancel();
m_parserWatcher.waitForFinished(); m_parserWatcher.waitForFinished();
@@ -93,7 +102,7 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
void ClangEditorDocumentProcessor::run() void ClangEditorDocumentProcessor::run()
{ {
updateTranslationUnitIfProjectPartExists(); m_updateTranslationUnitTimer.start();
// Run clang parser // Run clang parser
disconnect(&m_parserWatcher, &QFutureWatcher<void>::finished, disconnect(&m_parserWatcher, &QFutureWatcher<void>::finished,
@@ -160,14 +169,21 @@ void ClangEditorDocumentProcessor::clearProjectPart()
m_projectPart.clear(); m_projectPart.clear();
} }
void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics, void ClangEditorDocumentProcessor::updateCodeWarnings(
uint documentRevision) const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
uint documentRevision)
{ {
if (documentRevision == revision()) { if (documentRevision == revision()) {
m_diagnosticManager.processNewDiagnostics(diagnostics); m_diagnosticManager.processNewDiagnostics(diagnostics);
const auto codeWarnings = m_diagnosticManager.takeExtraSelections(); const auto codeWarnings = m_diagnosticManager.takeExtraSelections();
const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers(); const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers();
emit codeWarningsUpdated(revision(), codeWarnings, fixitAvailableMarkers); const auto creator = creatorForHeaderErrorDiagnosticWidget(firstHeaderErrorDiagnostic);
emit codeWarningsUpdated(revision(),
codeWarnings,
creator,
fixitAvailableMarkers);
} }
} }
namespace { namespace {
@@ -251,6 +267,11 @@ void ClangEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint line,
addToolTipToLayout(diagnostic, target); addToolTipToLayout(diagnostic, target);
} }
void ClangEditorDocumentProcessor::editorDocumentTimerRestarted()
{
m_updateTranslationUnitTimer.stop(); // Wait for the next call to run().
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
{ {
return fileContainerWithArguments(m_projectPart.data()); return fileContainerWithArguments(m_projectPart.data());
@@ -321,6 +342,38 @@ void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &pro
m_ipcCommunicator.requestDocumentAnnotations(fileContainer); m_ipcCommunicator.requestDocumentAnnotations(fileContainer);
} }
static Internal::DisplayHints displayHintsForInfoBar()
{
Internal::DisplayHints displayHints;
displayHints.showMainDiagnosticHeader = false;
displayHints.showFileNameInMainDiagnostic = true;
displayHints.enableClickableFixits = false; // Tool chain headers might be changed, so disable.
return displayHints;
}
CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator
ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic)
{
if (firstHeaderErrorDiagnostic.text().isEmpty())
return CppTools::BaseEditorDocumentProcessor::HeaderErrorDiagnosticWidgetCreator();
return [firstHeaderErrorDiagnostic]() {
auto vbox = new QVBoxLayout;
vbox->setMargin(0);
vbox->setContentsMargins(10, 0, 0, 2);
vbox->setSpacing(2);
addToolTipToLayout(firstHeaderErrorDiagnostic, vbox, displayHintsForInfoBar());
auto widget = new QWidget;
widget->setLayout(vbox);
return widget;
};
}
static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart) static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart)
{ {
if (projectPart) if (projectPart)

View File

@@ -32,6 +32,7 @@
#include <cpptools/semantichighlighter.h> #include <cpptools/semantichighlighter.h>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QTimer>
namespace ClangBackEnd { namespace ClangBackEnd {
class DiagnosticContainer; class DiagnosticContainer;
@@ -67,6 +68,7 @@ public:
void clearProjectPart(); void clearProjectPart();
void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics, void updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
uint documentRevision); uint documentRevision);
void updateHighlighting(const QVector<ClangBackEnd::HighlightingMarkContainer> &highlightingMarks, void updateHighlighting(const QVector<ClangBackEnd::HighlightingMarkContainer> &highlightingMarks,
const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges, const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges,
@@ -78,6 +80,8 @@ public:
bool hasDiagnosticsAt(uint line, uint column) const override; bool hasDiagnosticsAt(uint line, uint column) const override;
void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *target) const override; void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *target) const override;
void editorDocumentTimerRestarted() override;
ClangBackEnd::FileContainer fileContainerWithArguments() const; ClangBackEnd::FileContainer fileContainerWithArguments() const;
void clearDiagnosticsWithFixIts(); void clearDiagnosticsWithFixIts();
@@ -93,6 +97,8 @@ private:
void registerTranslationUnitForEditor(CppTools::ProjectPart *projectPart); void registerTranslationUnitForEditor(CppTools::ProjectPart *projectPart);
void updateTranslationUnitIfProjectPartExists(); void updateTranslationUnitIfProjectPartExists();
void requestDocumentAnnotations(const QString &projectpartId); void requestDocumentAnnotations(const QString &projectpartId);
HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic);
ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const; ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const;
ClangBackEnd::FileContainer fileContainerWithDocumentContent(const QString &projectpartId) const; ClangBackEnd::FileContainer fileContainerWithDocumentContent(const QString &projectpartId) const;
@@ -102,6 +108,7 @@ private:
QSharedPointer<ClangEditorDocumentParser> m_parser; QSharedPointer<ClangEditorDocumentParser> m_parser;
CppTools::ProjectPart::Ptr m_projectPart; CppTools::ProjectPart::Ptr m_projectPart;
QFutureWatcher<void> m_parserWatcher; QFutureWatcher<void> m_parserWatcher;
QTimer m_updateTranslationUnitTimer;
unsigned m_parserRevision; unsigned m_parserRevision;
CppTools::SemanticHighlighter m_semanticHighlighter; CppTools::SemanticHighlighter m_semanticHighlighter;

View File

@@ -60,6 +60,8 @@ TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type)
return TextEditor::C_PREPROCESSOR; return TextEditor::C_PREPROCESSOR;
case HighlightingType::Declaration: case HighlightingType::Declaration:
return TextEditor::C_DECLARATION; return TextEditor::C_DECLARATION;
case HighlightingType::OutputArgument:
return TextEditor::C_OUTPUT_ARGUMENT;
default: default:
return TextEditor::C_TEXT; // never called return TextEditor::C_TEXT; // never called
} }

View File

@@ -84,7 +84,7 @@ void ClangTextMark::setIcon(ClangBackEnd::DiagnosticSeverity severity)
bool ClangTextMark::addToolTipContent(QLayout *target) bool ClangTextMark::addToolTipContent(QLayout *target)
{ {
Internal::addToolTipToLayout(m_diagnostic, target); Internal::addToolTipToLayout(m_diagnostic, target, Internal::DisplayHints());
return true; return true;
} }

View File

@@ -36,12 +36,16 @@
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/projectpartbuilder.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kit.h> #include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h> #include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h> #include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
@@ -247,6 +251,44 @@ void BuildDirManager::generateProjectTree(CMakeProjectNode *root)
m_files.clear(); // Some of the FileNodes in files() were deleted! m_files.clear(); // Some of the FileNodes in files() were deleted!
} }
QSet<Core::Id> BuildDirManager::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
{
QSet<Core::Id> languages;
ToolChain *tc = ToolChainKitInformation::toolChain(kit(), ToolChain::Language::Cxx);
const Utils::FileName sysroot = SysRootKitInformation::sysRoot(kit());
QHash<QString, QStringList> targetDataCache;
foreach (const CMakeBuildTarget &cbt, buildTargets()) {
if (cbt.targetType == UtilityType)
continue;
// CMake shuffles the include paths that it reports via the CodeBlocks generator
// So remove the toolchain include paths, so that at least those end up in the correct
// place.
QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache);
QSet<QString> tcIncludes;
foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot))
tcIncludes.insert(hp.path());
QStringList includePaths;
foreach (const QString &i, cbt.includeFiles) {
if (!tcIncludes.contains(i))
includePaths.append(i);
}
includePaths += buildDirectory().toString();
ppBuilder.setIncludePaths(includePaths);
ppBuilder.setCFlags(cxxflags);
ppBuilder.setCxxFlags(cxxflags);
ppBuilder.setDefines(cbt.defines);
ppBuilder.setDisplayName(cbt.title);
const QSet<Core::Id> partLanguages
= QSet<Core::Id>::fromList(ppBuilder.createProjectPartsForFiles(cbt.files));
languages.unite(partLanguages);
}
return languages;
}
void BuildDirManager::parse() void BuildDirManager::parse()
{ {
checkConfiguration(); checkConfiguration();
@@ -545,6 +587,102 @@ void BuildDirManager::processCMakeError()
}); });
} }
QStringList BuildDirManager::getCXXFlagsFor(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
// check cache:
auto it = cache.constFind(buildTarget.title);
if (it != cache.constEnd())
return *it;
if (extractCXXFlagsFromMake(buildTarget, cache))
return cache.value(buildTarget.title);
if (extractCXXFlagsFromNinja(buildTarget, cache))
return cache.value(buildTarget.title);
cache.insert(buildTarget.title, QStringList());
return QStringList();
}
bool BuildDirManager::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
int startIndex = makeCommand.indexOf('\"');
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
if (startIndex != -1 && endIndex != -1) {
startIndex += 1;
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
int slashIndex = makefile.lastIndexOf('/');
makefile.truncate(slashIndex);
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
QFile file(makefile);
if (file.exists()) {
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream stream(&file);
while (!stream.atEnd()) {
QString line = stream.readLine().trimmed();
if (line.startsWith("CXX_FLAGS =")) {
// Skip past =
cache.insert(buildTarget.title,
line.mid(11).trimmed().split(' ', QString::SkipEmptyParts));
return true;
}
}
}
}
return false;
}
bool BuildDirManager::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
Q_UNUSED(buildTarget)
if (!cache.isEmpty()) // We fill the cache in one go!
return false;
// Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were
// found
// Get "all" target's working directory
QByteArray ninjaFile;
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
buildNinjaFile += "/build.ninja";
QFile buildNinja(buildNinjaFile);
if (buildNinja.exists()) {
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
ninjaFile = buildNinja.readAll();
buildNinja.close();
}
if (ninjaFile.isEmpty())
return false;
QTextStream stream(ninjaFile);
bool cxxFound = false;
const QString targetSignature = "# Object build statements for ";
QString currentTarget;
while (!stream.atEnd()) {
// 1. Look for a block that refers to the current target
// 2. Look for a build rule which invokes CXX_COMPILER
// 3. Return the FLAGS definition
QString line = stream.readLine().trimmed();
if (line.startsWith('#')) {
if (line.startsWith(targetSignature)) {
int pos = line.lastIndexOf(' ');
currentTarget = line.mid(pos + 1);
}
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
cxxFound = line.indexOf("CXX_COMPILER") != -1;
} else if (cxxFound && line.startsWith("FLAGS =")) {
// Skip past =
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
}
}
return !cache.isEmpty();
}
void BuildDirManager::checkConfiguration() void BuildDirManager::checkConfiguration()
{ {
if (m_tempDir) // always throw away changes in the tmpdir! if (m_tempDir) // always throw away changes in the tmpdir!
@@ -656,6 +794,7 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
} }
QSet<QByteArray> advancedSet; QSet<QByteArray> advancedSet;
QMap<QByteArray, QByteArray> valuesMap;
QByteArray documentation; QByteArray documentation;
while (!cache.atEnd()) { while (!cache.atEnd()) {
const QByteArray line = trimCMakeCacheLine(cache.readLine()); const QByteArray line = trimCMakeCacheLine(cache.readLine());
@@ -679,6 +818,8 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
if (key.endsWith("-ADVANCED") && value == "1") { if (key.endsWith("-ADVANCED") && value == "1") {
advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */)); advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */));
} else if (key.endsWith("-STRINGS") && fromByteArray(type) == CMakeConfigItem::INTERNAL) {
valuesMap[key.left(key.count() - 8) /* "-STRINGS" */] = value;
} else { } else {
CMakeConfigItem::Type t = fromByteArray(type); CMakeConfigItem::Type t = fromByteArray(type);
result << CMakeConfigItem(key, t, documentation, value); result << CMakeConfigItem(key, t, documentation, value);
@@ -689,6 +830,13 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
for (int i = 0; i < result.count(); ++i) { for (int i = 0; i < result.count(); ++i) {
CMakeConfigItem &item = result[i]; CMakeConfigItem &item = result[i];
item.isAdvanced = advancedSet.contains(item.key); item.isAdvanced = advancedSet.contains(item.key);
if (valuesMap.contains(item.key)) {
item.values = CMakeConfigItem::cmakeSplitValue(QString::fromUtf8(valuesMap[item.key]));
} else if (item.key == "CMAKE_BUILD_TYPE") {
// WA for known options
item.values << "" << "Debug" << "Release" << "MinSizeRel" << "RelWithDebInfo";
}
} }
Utils::sort(result, CMakeConfigItem::sortOperator()); Utils::sort(result, CMakeConfigItem::sortOperator());

View File

@@ -45,6 +45,7 @@ QT_FORWARD_DECLARE_CLASS(QTemporaryDir);
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher); QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher);
namespace Core { class IDocument; } namespace Core { class IDocument; }
namespace CppTools { class ProjectPartBuilder; }
namespace ProjectExplorer { namespace ProjectExplorer {
class FileNode; class FileNode;
@@ -80,6 +81,7 @@ public:
bool persistCMakeState(); bool persistCMakeState();
void generateProjectTree(CMakeProjectNode *root); void generateProjectTree(CMakeProjectNode *root);
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
QList<CMakeBuildTarget> buildTargets() const; QList<CMakeBuildTarget> buildTargets() const;
CMakeConfig parsedConfiguration() const; CMakeConfig parsedConfiguration() const;
@@ -117,6 +119,10 @@ private:
void processCMakeOutput(); void processCMakeOutput();
void processCMakeError(); void processCMakeError();
QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
bool m_hasData = false; bool m_hasData = false;
CMakeBuildConfiguration *m_buildConfiguration = nullptr; CMakeBuildConfiguration *m_buildConfiguration = nullptr;

View File

@@ -211,6 +211,11 @@ void CMakeBuildConfiguration::generateProjectTree(CMakeProjectNode *root) const
return m_buildDirManager->generateProjectTree(root); return m_buildDirManager->generateProjectTree(root);
} }
QSet<Core::Id> CMakeBuildConfiguration::updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder)
{
return m_buildDirManager->updateCodeModel(ppBuilder);
}
FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath, FileName CMakeBuildConfiguration::shadowBuildDirectory(const FileName &projectFilePath,
const Kit *k, const Kit *k,
const QString &bcName, const QString &bcName,
@@ -240,6 +245,7 @@ QList<ConfigModel::DataItem> CMakeBuildConfiguration::completeCMakeConfiguration
j.key = QString::fromUtf8(i.key); j.key = QString::fromUtf8(i.key);
j.value = QString::fromUtf8(i.value); j.value = QString::fromUtf8(i.value);
j.description = QString::fromUtf8(i.documentation); j.description = QString::fromUtf8(i.documentation);
j.values = i.values;
j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL; j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL;
switch (i.type) { switch (i.type) {
@@ -275,6 +281,7 @@ void CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QList<ConfigMod
ni.value = i.value.toUtf8(); ni.value = i.value.toUtf8();
ni.documentation = i.description.toUtf8(); ni.documentation = i.description.toUtf8();
ni.isAdvanced = i.isAdvanced; ni.isAdvanced = i.isAdvanced;
ni.values = i.values;
switch (i.type) { switch (i.type) {
case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN:
ni.type = CMakeConfigItem::BOOL; ni.type = CMakeConfigItem::BOOL;

View File

@@ -32,6 +32,7 @@
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/abi.h> #include <projectexplorer/abi.h>
namespace CppTools { class ProjectPartBuilder; }
namespace ProjectExplorer { class ToolChain; } namespace ProjectExplorer { class ToolChain; }
namespace CMakeProjectManager { namespace CMakeProjectManager {
@@ -82,6 +83,7 @@ public:
QList<CMakeBuildTarget> buildTargets() const; QList<CMakeBuildTarget> buildTargets() const;
void generateProjectTree(CMakeProjectNode *root) const; void generateProjectTree(CMakeProjectNode *root) const;
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
static Utils::FileName static Utils::FileName
shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k, shadowBuildDirectory(const Utils::FileName &projectFilePath, const ProjectExplorer::Kit *k,

View File

@@ -26,6 +26,7 @@
#include "cmakebuildsettingswidget.h" #include "cmakebuildsettingswidget.h"
#include "configmodel.h" #include "configmodel.h"
#include "configmodelitemdelegate.h"
#include "cmakeproject.h" #include "cmakeproject.h"
#include "cmakebuildconfiguration.h" #include "cmakebuildconfiguration.h"
@@ -140,6 +141,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_configView->setSelectionBehavior(QAbstractItemView::SelectItems); m_configView->setSelectionBehavior(QAbstractItemView::SelectItems);
m_configView->setFrameShape(QFrame::NoFrame); m_configView->setFrameShape(QFrame::NoFrame);
m_configView->hideColumn(2); // Hide isAdvanced column m_configView->hideColumn(2); // Hide isAdvanced column
m_configView->setItemDelegate(new ConfigModelItemDelegate(m_configView));
QFrame *findWrapper = Core::ItemViewFind::createSearchableWrapper(m_configView, Core::ItemViewFind::LightColored); QFrame *findWrapper = Core::ItemViewFind::createSearchableWrapper(m_configView, Core::ItemViewFind::LightColored);
findWrapper->setFrameStyle(QFrame::StyledPanel); findWrapper->setFrameStyle(QFrame::StyledPanel);

View File

@@ -42,7 +42,7 @@ CMakeConfigItem::CMakeConfigItem() = default;
CMakeConfigItem::CMakeConfigItem(const CMakeConfigItem &other) : CMakeConfigItem::CMakeConfigItem(const CMakeConfigItem &other) :
key(other.key), type(other.type), isAdvanced(other.isAdvanced), key(other.key), type(other.type), isAdvanced(other.isAdvanced),
value(other.value), documentation(other.documentation) value(other.value), documentation(other.documentation), values(other.values)
{ } { }
CMakeConfigItem::CMakeConfigItem(const QByteArray &k, Type t, CMakeConfigItem::CMakeConfigItem(const QByteArray &k, Type t,

View File

@@ -61,6 +61,7 @@ public:
bool isAdvanced = false; bool isAdvanced = false;
QByteArray value; // converted to string as needed QByteArray value; // converted to string as needed
QByteArray documentation; QByteArray documentation;
QStringList values;
}; };
using CMakeConfig = QList<CMakeConfigItem>; using CMakeConfig = QList<CMakeConfigItem>;

View File

@@ -94,102 +94,6 @@ CMakeProject::~CMakeProject()
qDeleteAll(m_extraCompilers); qDeleteAll(m_extraCompilers);
} }
QStringList CMakeProject::getCXXFlagsFor(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
// check cache:
auto it = cache.constFind(buildTarget.title);
if (it != cache.constEnd())
return *it;
if (extractCXXFlagsFromMake(buildTarget, cache))
return cache.value(buildTarget.title);
if (extractCXXFlagsFromNinja(buildTarget, cache))
return cache.value(buildTarget.title);
cache.insert(buildTarget.title, QStringList());
return QStringList();
}
bool CMakeProject::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
int startIndex = makeCommand.indexOf('\"');
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
if (startIndex != -1 && endIndex != -1) {
startIndex += 1;
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
int slashIndex = makefile.lastIndexOf('/');
makefile.truncate(slashIndex);
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
QFile file(makefile);
if (file.exists()) {
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream stream(&file);
while (!stream.atEnd()) {
QString line = stream.readLine().trimmed();
if (line.startsWith("CXX_FLAGS =")) {
// Skip past =
cache.insert(buildTarget.title,
line.mid(11).trimmed().split(' ', QString::SkipEmptyParts));
return true;
}
}
}
}
return false;
}
bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
QHash<QString, QStringList> &cache)
{
Q_UNUSED(buildTarget)
if (!cache.isEmpty()) // We fill the cache in one go!
return false;
// Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS) from there if no suitable flags.make were
// found
// Get "all" target's working directory
QByteArray ninjaFile;
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
buildNinjaFile += "/build.ninja";
QFile buildNinja(buildNinjaFile);
if (buildNinja.exists()) {
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
ninjaFile = buildNinja.readAll();
buildNinja.close();
}
if (ninjaFile.isEmpty())
return false;
QTextStream stream(ninjaFile);
bool cxxFound = false;
const QString targetSignature = "# Object build statements for ";
QString currentTarget;
while (!stream.atEnd()) {
// 1. Look for a block that refers to the current target
// 2. Look for a build rule which invokes CXX_COMPILER
// 3. Return the FLAGS definition
QString line = stream.readLine().trimmed();
if (line.startsWith('#')) {
if (line.startsWith(targetSignature)) {
int pos = line.lastIndexOf(' ');
currentTarget = line.mid(pos + 1);
}
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
cxxFound = line.indexOf("CXX_COMPILER") != -1;
} else if (cxxFound && line.startsWith("FLAGS =")) {
// Skip past =
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
}
}
return !cache.isEmpty();
}
void CMakeProject::updateProjectData() void CMakeProject::updateProjectData()
{ {
auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender()); auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender());
@@ -225,39 +129,11 @@ void CMakeProject::updateProjectData()
activeQtVersion = CppTools::ProjectPart::Qt5; activeQtVersion = CppTools::ProjectPart::Qt5;
} }
const FileName sysroot = SysRootKitInformation::sysRoot(k);
ppBuilder.setQtVersion(activeQtVersion); ppBuilder.setQtVersion(activeQtVersion);
QHash<QString, QStringList> targetDataCache; const QSet<Core::Id> languages = cmakeBc->updateCodeModel(ppBuilder);
foreach (const CMakeBuildTarget &cbt, buildTargets()) { for (const auto &lid : languages)
if (cbt.targetType == UtilityType) setProjectLanguage(lid, true);
continue;
// CMake shuffles the include paths that it reports via the CodeBlocks generator
// So remove the toolchain include paths, so that at least those end up in the correct
// place.
QStringList cxxflags = getCXXFlagsFor(cbt, targetDataCache);
QSet<QString> tcIncludes;
foreach (const HeaderPath &hp, tc->systemHeaderPaths(cxxflags, sysroot)) {
tcIncludes.insert(hp.path());
}
QStringList includePaths;
foreach (const QString &i, cbt.includeFiles) {
if (!tcIncludes.contains(i))
includePaths.append(i);
}
includePaths += projectDirectory().toString();
ppBuilder.setIncludePaths(includePaths);
ppBuilder.setCFlags(cxxflags);
ppBuilder.setCxxFlags(cxxflags);
ppBuilder.setDefines(cbt.defines);
ppBuilder.setDisplayName(cbt.title);
const QList<Core::Id> languages = ppBuilder.createProjectPartsForFiles(cbt.files);
foreach (Core::Id language, languages)
setProjectLanguage(language, true);
}
m_codeModelFuture.cancel(); m_codeModelFuture.cancel();
pinfo.finish(); pinfo.finish();

View File

@@ -120,9 +120,6 @@ private:
QStringList filesGeneratedFrom(const QString &sourceFile) const final; QStringList filesGeneratedFrom(const QString &sourceFile) const final;
void updateTargetRunConfigurations(ProjectExplorer::Target *t); void updateTargetRunConfigurations(ProjectExplorer::Target *t);
void updateApplicationAndDeploymentTargets(); void updateApplicationAndDeploymentTargets();
QStringList getCXXFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
bool extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
bool extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache);
ProjectExplorer::Target *m_connectedTarget = nullptr; ProjectExplorer::Target *m_connectedTarget = nullptr;

View File

@@ -28,7 +28,8 @@ HEADERS = builddirmanager.h \
cmakebuildsettingswidget.h \ cmakebuildsettingswidget.h \
cmakeindenter.h \ cmakeindenter.h \
cmakeautocompleter.h \ cmakeautocompleter.h \
configmodel.h configmodel.h \
configmodelitemdelegate.h
SOURCES = builddirmanager.cpp \ SOURCES = builddirmanager.cpp \
cmakebuildstep.cpp \ cmakebuildstep.cpp \
@@ -54,6 +55,7 @@ SOURCES = builddirmanager.cpp \
cmakebuildsettingswidget.cpp \ cmakebuildsettingswidget.cpp \
cmakeindenter.cpp \ cmakeindenter.cpp \
cmakeautocompleter.cpp \ cmakeautocompleter.cpp \
configmodel.cpp configmodel.cpp \
configmodelitemdelegate.cpp
RESOURCES += cmakeproject.qrc RESOURCES += cmakeproject.qrc

View File

@@ -71,6 +71,8 @@ QtcPlugin {
"cmakeautocompleter.h", "cmakeautocompleter.h",
"cmakeautocompleter.cpp", "cmakeautocompleter.cpp",
"configmodel.cpp", "configmodel.cpp",
"configmodel.h" "configmodel.h",
"configmodelitemdelegate.cpp",
"configmodelitemdelegate.h"
] ]
} }

View File

@@ -88,6 +88,15 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const
const InternalDataItem &item = m_configuration[index.row()]; const InternalDataItem &item = m_configuration[index.row()];
if (index.column() < 2) {
switch (role) {
case ItemTypeRole:
return item.type;
case ItemValuesRole:
return item.values;
}
}
switch (index.column()) { switch (index.column()) {
case 0: case 0:
switch (role) { switch (role) {
@@ -97,8 +106,6 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const
return item.key; return item.key;
case Qt::ToolTipRole: case Qt::ToolTipRole:
return item.description; return item.description;
case Qt::UserRole:
return item.type;
case Qt::FontRole: { case Qt::FontRole: {
QFont font; QFont font;
font.setItalic(item.isCMakeChanged); font.setItalic(item.isCMakeChanged);
@@ -126,8 +133,6 @@ QVariant ConfigModel::data(const QModelIndex &index, int role) const
} }
case Qt::ToolTipRole: case Qt::ToolTipRole:
return item.description; return item.description;
case Qt::UserRole:
return item.type;
default: default:
return QVariant(); return QVariant();
} }
@@ -209,13 +214,15 @@ QVariant ConfigModel::headerData(int section, Qt::Orientation orientation, int r
void ConfigModel::appendConfiguration(const QString &key, void ConfigModel::appendConfiguration(const QString &key,
const QString &value, const QString &value,
const ConfigModel::DataItem::Type type, const ConfigModel::DataItem::Type type,
const QString &description) const QString &description,
const QStringList &values)
{ {
DataItem item; DataItem item;
item.key = key; item.key = key;
item.type = type; item.type = type;
item.value = value; item.value = value;
item.description = description; item.description = description;
item.values = values;
InternalDataItem internalItem(item); InternalDataItem internalItem(item);
internalItem.isUserNew = true; internalItem.isUserNew = true;

View File

@@ -34,6 +34,11 @@ class ConfigModel : public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
enum Roles {
ItemTypeRole = Qt::UserRole,
ItemValuesRole
};
class DataItem { class DataItem {
public: public:
enum Type { BOOLEAN, FILE, DIRECTORY, STRING, UNKNOWN}; enum Type { BOOLEAN, FILE, DIRECTORY, STRING, UNKNOWN};
@@ -43,6 +48,7 @@ public:
bool isAdvanced = false; bool isAdvanced = false;
QString value; QString value;
QString description; QString description;
QStringList values;
}; };
explicit ConfigModel(QObject *parent = nullptr); explicit ConfigModel(QObject *parent = nullptr);
@@ -58,7 +64,8 @@ public:
void appendConfiguration(const QString &key, void appendConfiguration(const QString &key,
const QString &value = QString(), const QString &value = QString(),
const DataItem::Type type = DataItem::UNKNOWN, const DataItem::Type type = DataItem::UNKNOWN,
const QString &description = QString()); const QString &description = QString(),
const QStringList &values = QStringList());
void setConfiguration(const QList<DataItem> &config); void setConfiguration(const QList<DataItem> &config);
void flush(); void flush();
void resetAllChanges(); void resetAllChanges();

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2016 Alexander Drozdov.
** Contact: adrozdoff@gmail.com
**
** This file is part of CMakeProjectManager2 plugin.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
****************************************************************************/
#include "configmodelitemdelegate.h"
#include "configmodel.h"
#include <QComboBox>
namespace CMakeProjectManager {
ConfigModelItemDelegate::ConfigModelItemDelegate(QObject* parent)
: QStyledItemDelegate(parent)
{ }
ConfigModelItemDelegate::~ConfigModelItemDelegate()
{ }
QWidget* ConfigModelItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
// ComboBox ony in column 2
if (index.column() != 1)
return QStyledItemDelegate::createEditor(parent, option, index);
auto model = index.model();
auto values = model->data(index, ConfigModel::ItemValuesRole).toStringList();
if (values.isEmpty())
return QStyledItemDelegate::createEditor(parent, option, index);
// Create the combobox and populate it
auto cb = new QComboBox(parent);
cb->addItems(values);
cb->setEditable(true);
return cb;
}
void ConfigModelItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (QComboBox* cb = qobject_cast<QComboBox*>(editor)) {
// get the index of the text in the combobox that matches the current value of the itenm
QString currentText = index.data(Qt::EditRole).toString();
int cbIndex = cb->findText(currentText);
// if it is valid, adjust the combobox
if (cbIndex >= 0)
cb->setCurrentIndex(cbIndex);
else
cb->setEditText(currentText);
} else {
QStyledItemDelegate::setEditorData(editor, index);
}
}
void ConfigModelItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
{
if (QComboBox* cb = qobject_cast<QComboBox*>(editor))
// save the current text of the combo box as the current value of the item
model->setData(index, cb->currentText(), Qt::EditRole);
else
QStyledItemDelegate::setModelData(editor, model, index);
}
} // namespace CMakeProjectManager

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** Copyright (C) 2016 Alexander Drozdov.
** Contact: adrozdoff@gmail.com
**
** This file is part of CMakeProjectManager2 plugin.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
****************************************************************************/
#pragma once
#include <QStyledItemDelegate>
namespace CMakeProjectManager {
class ConfigModelItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ConfigModelItemDelegate(QObject* parent=0);
~ConfigModelItemDelegate();
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setEditorData(QWidget* editor, const QModelIndex& index) const override;
void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const override;
};
} // namespace CMakeProjectManager

View File

@@ -32,6 +32,7 @@
#include <QFrame> #include <QFrame>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel> #include <QLabel>
#include <QToolButton> #include <QToolButton>
@@ -67,6 +68,20 @@ void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBac
m_cancelButtonCallBack = callBack; m_cancelButtonCallBack = callBack;
} }
void InfoBarEntry::setSuppressionButtonInfo(InfoBarEntry::CallBack callback)
{
m_suppressionButtonCallBack = callback;
}
void InfoBarEntry::setShowDefaultCancelButton(bool yesno)
{
m_showDefaultCancelButton = yesno;
}
void InfoBarEntry::setDetailsWidgetCreator(const InfoBarEntry::DetailsWidgetCreator &creator)
{
m_detailsWidgetCreator = creator;
}
void InfoBar::addInfo(const InfoBarEntry &info) void InfoBar::addInfo(const InfoBarEntry &info)
{ {
@@ -124,10 +139,13 @@ void InfoBar::clear()
void InfoBar::globallySuppressInfo(Id id) void InfoBar::globallySuppressInfo(Id id)
{ {
globallySuppressed.insert(id); globallySuppressed.insert(id);
QStringList list; writeGloballySuppressedToSettings();
foreach (Id i, globallySuppressed) }
list << QLatin1String(i.name());
ICore::settings()->setValue(QLatin1String(C_SUPPRESSED_WARNINGS), list); void InfoBar::globallyUnsuppressInfo(Id id)
{
globallySuppressed.remove(id);
writeGloballySuppressedToSettings();
} }
void InfoBar::initializeGloballySuppressed() void InfoBar::initializeGloballySuppressed()
@@ -148,12 +166,17 @@ bool InfoBar::anyGloballySuppressed()
return !globallySuppressed.isEmpty(); return !globallySuppressed.isEmpty();
} }
void InfoBar::writeGloballySuppressedToSettings()
{
QStringList list;
foreach (Id i, globallySuppressed)
list << QLatin1String(i.name());
ICore::settings()->setValue(QLatin1String(C_SUPPRESSED_WARNINGS), list);
}
InfoBarDisplay::InfoBarDisplay(QObject *parent) InfoBarDisplay::InfoBarDisplay(QObject *parent)
: QObject(parent) : QObject(parent)
, m_infoBar(0)
, m_boxLayout(0)
, m_boxIndex(0)
{ {
} }
@@ -209,13 +232,43 @@ void InfoBarDisplay::update()
infoWidget->setLineWidth(1); infoWidget->setLineWidth(1);
infoWidget->setAutoFillBackground(true); infoWidget->setAutoFillBackground(true);
QHBoxLayout *hbox = new QHBoxLayout(infoWidget); QHBoxLayout *hbox = new QHBoxLayout;
hbox->setMargin(2); hbox->setMargin(2);
auto *vbox = new QVBoxLayout(infoWidget);
vbox->setMargin(0);
vbox->addLayout(hbox);
QLabel *infoWidgetLabel = new QLabel(info.infoText); QLabel *infoWidgetLabel = new QLabel(info.infoText);
infoWidgetLabel->setWordWrap(true); infoWidgetLabel->setWordWrap(true);
hbox->addWidget(infoWidgetLabel); hbox->addWidget(infoWidgetLabel);
if (info.m_detailsWidgetCreator) {
if (m_isShowingDetailsWidget) {
QWidget *detailsWidget = info.m_detailsWidgetCreator();
vbox->addWidget(detailsWidget);
}
auto *showDetailsButton = new QToolButton;
showDetailsButton->setCheckable(true);
showDetailsButton->setChecked(m_isShowingDetailsWidget);
showDetailsButton->setText(tr("&Show Details"));
connect(showDetailsButton, &QToolButton::clicked, [this, vbox, info] (bool) {
QWidget *detailsWidget = vbox->count() == 2 ? vbox->itemAt(1)->widget() : nullptr;
if (!detailsWidget) {
detailsWidget = info.m_detailsWidgetCreator();
vbox->addWidget(detailsWidget);
}
m_isShowingDetailsWidget = !m_isShowingDetailsWidget;
detailsWidget->setVisible(m_isShowingDetailsWidget);
});
hbox->addWidget(showDetailsButton);
} else {
m_isShowingDetailsWidget = false;
}
if (!info.buttonText.isEmpty()) { if (!info.buttonText.isEmpty()) {
QToolButton *infoWidgetButton = new QToolButton; QToolButton *infoWidgetButton = new QToolButton;
infoWidgetButton->setText(info.buttonText); infoWidgetButton->setText(info.buttonText);
@@ -229,7 +282,9 @@ void InfoBarDisplay::update()
if (info.globalSuppression == InfoBarEntry::GlobalSuppressionEnabled) { if (info.globalSuppression == InfoBarEntry::GlobalSuppressionEnabled) {
infoWidgetSuppressButton = new QToolButton; infoWidgetSuppressButton = new QToolButton;
infoWidgetSuppressButton->setText(tr("Do Not Show Again")); infoWidgetSuppressButton->setText(tr("Do Not Show Again"));
connect(infoWidgetSuppressButton, &QAbstractButton::clicked, this, [this, id] { connect(infoWidgetSuppressButton, &QAbstractButton::clicked, this, [this, info, id] {
if (info.m_suppressionButtonCallBack)
info.m_suppressionButtonCallBack();
m_infoBar->removeInfo(id); m_infoBar->removeInfo(id);
InfoBar::globallySuppressInfo(id); InfoBar::globallySuppressInfo(id);
}); });
@@ -245,12 +300,17 @@ void InfoBarDisplay::update()
}); });
if (info.cancelButtonText.isEmpty()) { if (info.cancelButtonText.isEmpty()) {
infoWidgetCloseButton->setAutoRaise(true); if (info.m_showDefaultCancelButton) {
infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon()); infoWidgetCloseButton->setAutoRaise(true);
infoWidgetCloseButton->setToolTip(tr("Close")); infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon());
infoWidgetCloseButton->setToolTip(tr("Close"));
}
if (infoWidgetSuppressButton) if (infoWidgetSuppressButton)
hbox->addWidget(infoWidgetSuppressButton); hbox->addWidget(infoWidgetSuppressButton);
hbox->addWidget(infoWidgetCloseButton);
if (info.m_showDefaultCancelButton)
hbox->addWidget(infoWidgetCloseButton);
} else { } else {
infoWidgetCloseButton->setText(info.cancelButtonText); infoWidgetCloseButton->setText(info.cancelButtonText);
hbox->addWidget(infoWidgetCloseButton); hbox->addWidget(infoWidgetCloseButton);

View File

@@ -54,10 +54,15 @@ public:
InfoBarEntry(Id _id, const QString &_infoText, GlobalSuppressionMode _globalSuppression = GlobalSuppressionDisabled); InfoBarEntry(Id _id, const QString &_infoText, GlobalSuppressionMode _globalSuppression = GlobalSuppressionDisabled);
InfoBarEntry(const InfoBarEntry &other) { *this = other; } InfoBarEntry(const InfoBarEntry &other) { *this = other; }
typedef std::function<void()> CallBack; using CallBack = std::function<void()>;
void setCustomButtonInfo(const QString &_buttonText, CallBack callBack); void setCustomButtonInfo(const QString &_buttonText, CallBack callBack);
void setCancelButtonInfo(CallBack callBack); void setCancelButtonInfo(CallBack callBack);
void setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack); void setCancelButtonInfo(const QString &_cancelButtonText, CallBack callBack);
void setSuppressionButtonInfo(CallBack callback);
void setShowDefaultCancelButton(bool yesno);
using DetailsWidgetCreator = std::function<QWidget*()>;
void setDetailsWidgetCreator(const DetailsWidgetCreator &creator);
private: private:
Id id; Id id;
@@ -66,7 +71,10 @@ private:
CallBack m_buttonCallBack; CallBack m_buttonCallBack;
QString cancelButtonText; QString cancelButtonText;
CallBack m_cancelButtonCallBack; CallBack m_cancelButtonCallBack;
CallBack m_suppressionButtonCallBack;
GlobalSuppressionMode globalSuppression; GlobalSuppressionMode globalSuppression;
DetailsWidgetCreator m_detailsWidgetCreator;
bool m_showDefaultCancelButton = true;
friend class InfoBar; friend class InfoBar;
friend class InfoBarDisplay; friend class InfoBarDisplay;
}; };
@@ -84,6 +92,7 @@ public:
void enableInfo(Id id); void enableInfo(Id id);
void clear(); void clear();
static void globallySuppressInfo(Id id); static void globallySuppressInfo(Id id);
static void globallyUnsuppressInfo(Id id);
static void initializeGloballySuppressed(); static void initializeGloballySuppressed();
static void clearGloballySuppressed(); static void clearGloballySuppressed();
static bool anyGloballySuppressed(); static bool anyGloballySuppressed();
@@ -91,6 +100,9 @@ public:
signals: signals:
void changed(); void changed();
private:
static void writeGloballySuppressedToSettings();
private: private:
QList<InfoBarEntry> m_infoBarEntries; QList<InfoBarEntry> m_infoBarEntries;
QSet<Id> m_suppressed; QSet<Id> m_suppressed;
@@ -113,9 +125,10 @@ private:
void widgetDestroyed(); void widgetDestroyed();
QList<QWidget *> m_infoWidgets; QList<QWidget *> m_infoWidgets;
InfoBar *m_infoBar; InfoBar *m_infoBar = nullptr;
QBoxLayout *m_boxLayout; QBoxLayout *m_boxLayout = nullptr;
int m_boxIndex; int m_boxIndex = 0;
bool m_isShowingDetailsWidget = false;
}; };
} // namespace Core } // namespace Core

View File

@@ -44,6 +44,7 @@
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/infobar.h>
#include <cpptools/cppchecksymbols.h> #include <cpptools/cppchecksymbols.h>
#include <cpptools/cppcodeformatter.h> #include <cpptools/cppcodeformatter.h>
@@ -77,6 +78,7 @@
#include <cplusplus/FastPreprocessor.h> #include <cplusplus/FastPreprocessor.h>
#include <cplusplus/MatchingText.h> #include <cplusplus/MatchingText.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QApplication> #include <QApplication>
#include <QAction> #include <QAction>
@@ -126,9 +128,13 @@ public:
QSharedPointer<FunctionDeclDefLink> m_declDefLink; QSharedPointer<FunctionDeclDefLink> m_declDefLink;
QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor; QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
QToolButton *m_preprocessorButton;
QToolButton *m_preprocessorButton = nullptr;
QToolButton *m_headerErrorsIndicatorButton = nullptr;
CppSelectionChanger m_cppSelectionChanger; CppSelectionChanger m_cppSelectionChanger;
CppEditorWidget::HeaderErrorDiagnosticWidgetCreator m_headerErrorDiagnosticWidgetCreator;
}; };
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
@@ -139,7 +145,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
, m_useSelectionsUpdater(q) , m_useSelectionsUpdater(q)
, m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
, m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q)) , m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
, m_preprocessorButton(0)
, m_cppSelectionChanger() , m_cppSelectionChanger()
{ {
} }
@@ -224,7 +229,15 @@ void CppEditorWidget::finalizeInitialization()
connect(cmd, &Command::keySequenceChanged, this, &CppEditorWidget::updatePreprocessorButtonTooltip); connect(cmd, &Command::keySequenceChanged, this, &CppEditorWidget::updatePreprocessorButtonTooltip);
updatePreprocessorButtonTooltip(); updatePreprocessorButtonTooltip();
connect(d->m_preprocessorButton, &QAbstractButton::clicked, this, &CppEditorWidget::showPreProcessorWidget); connect(d->m_preprocessorButton, &QAbstractButton::clicked, this, &CppEditorWidget::showPreProcessorWidget);
d->m_headerErrorsIndicatorButton = new QToolButton(this);
d->m_headerErrorsIndicatorButton->setIcon(Utils::Icons::WARNING_TOOLBAR.pixmap());
connect(d->m_headerErrorsIndicatorButton, &QAbstractButton::clicked,
this, &CppEditorWidget::showHeaderErrorInfoBar);
d->m_headerErrorsIndicatorButton->setEnabled(false);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton); insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_headerErrorsIndicatorButton);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget()); insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
} }
@@ -287,6 +300,7 @@ void CppEditorWidget::onCppDocumentUpdated()
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections, const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers) const TextEditor::RefactorMarkers &refactorMarkers)
{ {
if (revision != documentRevision()) if (revision != documentRevision())
@@ -294,6 +308,9 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections); setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers); setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
d->m_headerErrorDiagnosticWidgetCreator = creator;
updateHeaderErrorWidgets();
} }
void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
@@ -304,6 +321,24 @@ void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
setIfdefedOutBlocks(ifdefedOutBlocks); setIfdefedOutBlocks(ifdefedOutBlocks);
} }
void CppEditorWidget::updateHeaderErrorWidgets()
{
const Id id(Constants::ERRORS_IN_HEADER_FILES);
InfoBar *infoBar = textDocument()->infoBar();
infoBar->removeInfo(id);
if (d->m_headerErrorDiagnosticWidgetCreator) {
if (infoBar->canInfoBeAdded(id)) {
addHeaderErrorInfoBarEntryAndHideIndicator();
} else {
d->m_headerErrorsIndicatorButton->setEnabled(true);
}
} else {
d->m_headerErrorsIndicatorButton->setEnabled(false);
}
}
void CppEditorWidget::findUsages() void CppEditorWidget::findUsages()
{ {
if (!d->m_modelManager) if (!d->m_modelManager)
@@ -399,6 +434,25 @@ void CppEditorWidget::renameSymbolUnderCursorBuiltin()
renameUsages(); // Rename non-local symbol or macro renameUsages(); // Rename non-local symbol or macro
} }
void CppEditorWidget::addHeaderErrorInfoBarEntryAndHideIndicator() const
{
InfoBarEntry info(Constants::ERRORS_IN_HEADER_FILES,
tr("<b>Warning</b>: The code model could not parse an included file, "
"which might lead to slow or incorrect code completion and "
"highlighting, for example."),
InfoBarEntry::GlobalSuppressionEnabled);
info.setDetailsWidgetCreator(d->m_headerErrorDiagnosticWidgetCreator);
info.setShowDefaultCancelButton(false);
info.setSuppressionButtonInfo([this](){
d->m_headerErrorsIndicatorButton->setEnabled(true);
});
InfoBar *infoBar = textDocument()->infoBar();
infoBar->addInfo(info);
d->m_headerErrorsIndicatorButton->setEnabled(false);
}
namespace { namespace {
QList<ProjectPart::Ptr> fetchProjectParts(CppTools::CppModelManager *modelManager, QList<ProjectPart::Ptr> fetchProjectParts(CppTools::CppModelManager *modelManager,
@@ -970,5 +1024,14 @@ void CppEditorWidget::showPreProcessorWidget()
} }
} }
void CppEditorWidget::showHeaderErrorInfoBar()
{
const Id id(Constants::ERRORS_IN_HEADER_FILES);
QTC_CHECK(!textDocument()->infoBar()->canInfoBeAdded(id));
InfoBar::globallyUnsuppressInfo(id);
addHeaderErrorInfoBarEntryAndHideIndicator();
}
} // namespace Internal } // namespace Internal
} // namespace CppEditor } // namespace CppEditor

View File

@@ -31,6 +31,10 @@
#include <QScopedPointer> #include <QScopedPointer>
namespace Core {
class InfoBarEntry;
}
namespace CppTools { namespace CppTools {
class CppEditorOutline; class CppEditorOutline;
class RefactoringEngineInterface; class RefactoringEngineInterface;
@@ -87,6 +91,7 @@ public:
void switchDeclarationDefinition(bool inNextSplit); void switchDeclarationDefinition(bool inNextSplit);
void showPreProcessorWidget(); void showPreProcessorWidget();
void showHeaderErrorInfoBar();
void findUsages(); void findUsages();
void renameSymbolUnderCursor(); void renameSymbolUnderCursor();
@@ -108,6 +113,9 @@ protected:
void slotCodeStyleSettingsChanged(const QVariant &) override; void slotCodeStyleSettingsChanged(const QVariant &) override;
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
private: private:
void updateFunctionDeclDefLink(); void updateFunctionDeclDefLink();
void updateFunctionDeclDefLinkNow(); void updateFunctionDeclDefLinkNow();
@@ -118,10 +126,12 @@ private:
void onCodeWarningsUpdated(unsigned revision, void onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections, const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers); const TextEditor::RefactorMarkers &refactorMarkers);
void onIfdefedOutBlocksUpdated(unsigned revision, void onIfdefedOutBlocksUpdated(unsigned revision,
const QList<TextEditor::BlockRange> ifdefedOutBlocks); const QList<TextEditor::BlockRange> ifdefedOutBlocks);
void updateHeaderErrorWidgets();
void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo, void updateSemanticInfo(const CppTools::SemanticInfo &semanticInfo,
bool updateUseSelectionSynchronously = false); bool updateUseSelectionSynchronously = false);
void updatePreprocessorButtonTooltip(); void updatePreprocessorButtonTooltip();
@@ -140,6 +150,8 @@ private:
void renameSymbolUnderCursorClang(); void renameSymbolUnderCursorClang();
void renameSymbolUnderCursorBuiltin(); void renameSymbolUnderCursorBuiltin();
void addHeaderErrorInfoBarEntryAndHideIndicator() const;
CppTools::ProjectPart *projectPart() const; CppTools::ProjectPart *projectPart() const;
private: private:

View File

@@ -36,6 +36,7 @@ const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarat
const char RENAME_SYMBOL_UNDER_CURSOR[] = "CppEditor.RenameSymbolUnderCursor"; const char RENAME_SYMBOL_UNDER_CURSOR[] = "CppEditor.RenameSymbolUnderCursor";
const char FIND_USAGES[] = "CppEditor.FindUsages"; const char FIND_USAGES[] = "CppEditor.FindUsages";
const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog"; const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog";
const char ERRORS_IN_HEADER_FILES[] = "CppEditor.ErrorsInHeaderFiles";
const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup"; const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup";
const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel"; const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel";
const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel"; const char INSPECT_CPP_CODEMODEL[] = "CppEditor.InspectCppCodeModel";

View File

@@ -230,12 +230,14 @@ void CppEditorDocument::scheduleProcessDocument()
{ {
m_processorRevision = document()->revision(); m_processorRevision = document()->revision();
m_processorTimer.start(); m_processorTimer.start();
processor()->editorDocumentTimerRestarted();
} }
void CppEditorDocument::processDocument() void CppEditorDocument::processDocument()
{ {
if (processor()->isParserRunning() || m_processorRevision != contentsRevision()) { if (processor()->isParserRunning() || m_processorRevision != contentsRevision()) {
m_processorTimer.start(); m_processorTimer.start();
processor()->editorDocumentTimerRestarted();
return; return;
} }

View File

@@ -59,9 +59,13 @@ public:
const QByteArray &defines); const QByteArray &defines);
void scheduleProcessDocument(); void scheduleProcessDocument();
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
signals: signals:
void codeWarningsUpdated(unsigned contentsRevision, void codeWarningsUpdated(unsigned contentsRevision,
const QList<QTextEdit::ExtraSelection> selections, const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers); const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned contentsRevision, void ifdefedOutBlocksUpdated(unsigned contentsRevision,

View File

@@ -67,6 +67,10 @@ void BaseEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint, uint, QLayo
{ {
} }
void BaseEditorDocumentProcessor::editorDocumentTimerRestarted()
{
}
void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future, void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
BaseEditorDocumentParser::Ptr parser, BaseEditorDocumentParser::Ptr parser,
const WorkingCopy workingCopy) const WorkingCopy workingCopy)

View File

@@ -37,6 +37,8 @@
#include <QTextEdit> #include <QTextEdit>
#include <functional>
namespace TextEditor { namespace TextEditor {
class TextDocument; class TextDocument;
class QuickFixOperations; class QuickFixOperations;
@@ -67,10 +69,17 @@ public:
virtual bool hasDiagnosticsAt(uint line, uint column) const; virtual bool hasDiagnosticsAt(uint line, uint column) const;
virtual void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *layout) const; virtual void addDiagnosticToolTipToLayout(uint line, uint column, QLayout *layout) const;
virtual void editorDocumentTimerRestarted();
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
signals: signals:
// Signal interface to implement // Signal interface to implement
void codeWarningsUpdated(unsigned revision, void codeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections, const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers); const TextEditor::RefactorMarkers &refactorMarkers);
void ifdefedOutBlocksUpdated(unsigned revision, void ifdefedOutBlocksUpdated(unsigned revision,

View File

@@ -310,7 +310,10 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument()); m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument());
m_codeWarningsUpdated = true; m_codeWarningsUpdated = true;
emit codeWarningsUpdated(revision(), m_codeWarnings, TextEditor::RefactorMarkers()); emit codeWarningsUpdated(revision(),
m_codeWarnings,
HeaderErrorDiagnosticWidgetCreator(),
TextEditor::RefactorMarkers());
} }
SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bool force) const

View File

@@ -622,25 +622,25 @@ void HelpPlugin::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocat
if (HelpViewer::launchWithExternalApp(url)) if (HelpViewer::launchWithExternalApp(url))
return; return;
QString address = url.toString();
if (!HelpManager::findFile(url).isValid()) { if (!HelpManager::findFile(url).isValid()) {
const QString address = url.toString();
if (address.startsWith("qthelp://org.qt-project.") if (address.startsWith("qthelp://org.qt-project.")
|| address.startsWith("qthelp://com.nokia.") || address.startsWith("qthelp://com.nokia.")
|| address.startsWith("qthelp://com.trolltech.")) { || address.startsWith("qthelp://com.trolltech.")) {
// local help not installed, resort to external web help // local help not installed, resort to external web help
QString urlPrefix = "http://doc.qt.io/"; QString urlPrefix = "http://doc.qt.io/";
if (url.authority() == "org.qt-project.qtcreator") if (url.authority() == "org.qt-project.qtcreator")
urlPrefix.append(QString::fromLatin1("qtcreator")); urlPrefix.append(QString::fromLatin1("qtcreator"));
else else
urlPrefix.append("qt-5"); urlPrefix.append("qt-5");
address = urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/'))); QDesktopServices::openUrl(QUrl(urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/')))));
return;
} }
} }
const QUrl newUrl(address);
HelpViewer *viewer = viewerForHelpViewerLocation(location); HelpViewer *viewer = viewerForHelpViewerLocation(location);
QTC_ASSERT(viewer, return); QTC_ASSERT(viewer, return);
viewer->setSource(newUrl); viewer->setSource(url);
ICore::raiseWindow(viewer); ICore::raiseWindow(viewer);
} }

View File

@@ -169,7 +169,7 @@ static QPoint flipPoint(const NSPoint &p)
NSURL *resolvedURL = data.resolvedUrl.toNSURL(); NSURL *resolvedURL = data.resolvedUrl.toNSURL();
NSString *mimeType = data.mimeType.toNSString(); NSString *mimeType = data.mimeType.toNSString();
NSData *nsdata = QtMac::toNSData(data.data); // Qt 5.3 has this in QByteArray NSData *nsdata = data.data.toNSData();
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:resolvedURL NSURLResponse *response = [[NSURLResponse alloc] initWithURL:resolvedURL
MIMEType:mimeType MIMEType:mimeType
expectedContentLength:data.data.length() expectedContentLength:data.data.length()

View File

@@ -93,7 +93,8 @@ RemoteHelpFilter::RemoteHelpFilter()
m_remoteUrls.append("https://www.bing.com/search?q=%1"); m_remoteUrls.append("https://www.bing.com/search?q=%1");
m_remoteUrls.append("https://www.google.com/search?q=%1"); m_remoteUrls.append("https://www.google.com/search?q=%1");
m_remoteUrls.append("https://search.yahoo.com/search?p=%1"); m_remoteUrls.append("https://search.yahoo.com/search?p=%1");
m_remoteUrls.append("https://www.cplusplus.com/reference/stl/%1"); m_remoteUrls.append("https://stackoverflow.com/search?q=%1");
m_remoteUrls.append("http://en.cppreference.com/mwiki/index.php?title=Special%3ASearch&search=%1");
m_remoteUrls.append("https://en.wikipedia.org/w/index.php?search=%1"); m_remoteUrls.append("https://en.wikipedia.org/w/index.php?search=%1");
} }

View File

@@ -404,6 +404,7 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage)
QVariantMap tmp = data.toMap(); QVariantMap tmp = data.toMap();
m_isPassword = tmp.value("isPassword", false).toBool();
m_defaultText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trText")).toString()); m_defaultText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trText")).toString());
m_disabledText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trDisabledText")).toString()); m_disabledText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trDisabledText")).toString());
m_placeholderText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trPlaceholder")).toString()); m_placeholderText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trPlaceholder")).toString());
@@ -439,6 +440,8 @@ QWidget *LineEditField::createWidget(const QString &displayName, JsonFieldPage *
if (!m_historyId.isEmpty()) if (!m_historyId.isEmpty())
w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem); w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem);
w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal);
return w; return w;
} }

View File

@@ -105,6 +105,7 @@ private:
bool m_isModified; bool m_isModified;
bool m_isValidating; bool m_isValidating;
bool m_restoreLastHistoryItem; bool m_restoreLastHistoryItem;
bool m_isPassword;
QString m_placeholderText; QString m_placeholderText;
QString m_defaultText; QString m_defaultText;
QString m_disabledText; QString m_disabledText;

View File

@@ -93,6 +93,7 @@ void QtProjectParameters::writeProFile(QTextStream &str) const
case ConsoleApp: case ConsoleApp:
// Mac: Command line apps should not be bundles // Mac: Command line apps should not be bundles
str << "CONFIG += console\nCONFIG -= app_bundle\n\n"; str << "CONFIG += console\nCONFIG -= app_bundle\n\n";
// fallthrough
case GuiApp: case GuiApp:
str << "TEMPLATE = app\n"; str << "TEMPLATE = app\n";
break; break;

View File

@@ -102,6 +102,7 @@ const char *nameForStyle(TextStyle style)
case C_WARNING_CONTEXT: return "WarningContext"; case C_WARNING_CONTEXT: return "WarningContext";
case C_DECLARATION: return "Declaration"; case C_DECLARATION: return "Declaration";
case C_OUTPUT_ARGUMENT: return "C_OUTPUT_ARGUMENT";
case C_LAST_STYLE_SENTINEL: return "LastStyleSentinel"; case C_LAST_STYLE_SENTINEL: return "LastStyleSentinel";
} }

View File

@@ -100,6 +100,7 @@ enum TextStyle : quint8 {
C_ERROR_CONTEXT, C_ERROR_CONTEXT,
C_DECLARATION, C_DECLARATION,
C_OUTPUT_ARGUMENT,
C_LAST_STYLE_SENTINEL C_LAST_STYLE_SENTINEL
}; };

View File

@@ -313,6 +313,10 @@ TextEditorSettings::TextEditorSettings(QObject *parent)
tr("Declaration"), tr("Declaration"),
tr("Declaration of a function, variable, and so on."), tr("Declaration of a function, variable, and so on."),
FormatDescription::ShowFontUnderlineAndRelativeControls); FormatDescription::ShowFontUnderlineAndRelativeControls);
formatDescr.emplace_back(C_OUTPUT_ARGUMENT,
tr("Output Argument"),
tr("Writable arguments of a function call."),
FormatDescription::ShowFontUnderlineAndRelativeControls);
d->m_fontSettingsPage = new FontSettingsPage(formatDescr, d->m_fontSettingsPage = new FontSettingsPage(formatDescr,
Constants::TEXT_EDITOR_FONT_SETTINGS, Constants::TEXT_EDITOR_FONT_SETTINGS,

View File

@@ -711,10 +711,11 @@ public:
return m_tool->createRunControl(runConfiguration, mode); return m_tool->createRunControl(runConfiguration, mode);
} }
IRunConfigurationAspect *createRunConfigurationAspect(ProjectExplorer::RunConfiguration *rc) override // Do not create an aspect, let the Callgrind tool create one and use that, too.
{ // IRunConfigurationAspect *createRunConfigurationAspect(ProjectExplorer::RunConfiguration *rc) override
return createValgrindRunConfigurationAspect(rc); // {
} // return createValgrindRunConfigurationAspect(rc);
// }
public: public:
MemcheckTool *m_tool; MemcheckTool *m_tool;

View File

@@ -64,6 +64,11 @@ public:
return future; return future;
} }
void preventFinalization() override
{
m_futureWatcher.disconnect();
}
private: private:
Runner m_runner; Runner m_runner;
QFutureWatcher<Result> m_futureWatcher; QFutureWatcher<Result> m_futureWatcher;

View File

@@ -0,0 +1,37 @@
/****************************************************************************
**
** 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
namespace ClangBackEnd {
enum class PreferredTranslationUnit
{
RecentlyParsed,
PreviouslyParsed,
LastUninitialized,
};
} // namespace ClangBackEnd

View File

@@ -29,6 +29,7 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/highlightingmarksiterator.h \ $$PWD/highlightingmarksiterator.h \
$$PWD/utf8positionfromlinecolumn.h \ $$PWD/utf8positionfromlinecolumn.h \
$$PWD/clangasyncjob.h \ $$PWD/clangasyncjob.h \
$$PWD/clangbackend_global.h \
$$PWD/clangcompletecodejob.h \ $$PWD/clangcompletecodejob.h \
$$PWD/clangcreateinitialdocumentpreamblejob.h \ $$PWD/clangcreateinitialdocumentpreamblejob.h \
$$PWD/clangfilepath.h \ $$PWD/clangfilepath.h \
@@ -41,7 +42,14 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/clangtranslationunit.h \ $$PWD/clangtranslationunit.h \
$$PWD/clangunsavedfilesshallowarguments.h \ $$PWD/clangunsavedfilesshallowarguments.h \
$$PWD/clangupdatedocumentannotationsjob.h \ $$PWD/clangupdatedocumentannotationsjob.h \
$$PWD/clangexceptions.h $$PWD/clangexceptions.h \
$$PWD/clangdocumentprocessor.h \
$$PWD/clangdocumentprocessors.h \
$$PWD/clangtranslationunits.h \
$$PWD/clangclock.h \
$$PWD/clangsupportivetranslationunitinitializer.h \
$$PWD/clangparsesupportivetranslationunitjob.h \
$$PWD/clangreparsesupportivetranslationunitjob.h \
SOURCES += $$PWD/clangcodemodelserver.cpp \ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/codecompleter.cpp \ $$PWD/codecompleter.cpp \
@@ -80,4 +88,10 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/clangtranslationunit.cpp \ $$PWD/clangtranslationunit.cpp \
$$PWD/clangunsavedfilesshallowarguments.cpp \ $$PWD/clangunsavedfilesshallowarguments.cpp \
$$PWD/clangupdatedocumentannotationsjob.cpp \ $$PWD/clangupdatedocumentannotationsjob.cpp \
$$PWD/clangexceptions.cpp $$PWD/clangexceptions.cpp \
$$PWD/clangdocumentprocessor.cpp \
$$PWD/clangdocumentprocessors.cpp \
$$PWD/clangtranslationunits.cpp \
$$PWD/clangsupportivetranslationunitinitializer.cpp \
$$PWD/clangparsesupportivetranslationunitjob.cpp \
$$PWD/clangreparsesupportivetranslationunitjob.cpp \

View File

@@ -0,0 +1,36 @@
/****************************************************************************
**
** 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 <chrono>
namespace ClangBackEnd {
using Clock = std::chrono::steady_clock;
using Duration = std::chrono::steady_clock::duration;
using TimePoint = std::chrono::steady_clock::time_point;
} // namespace ClangBackEnd

View File

@@ -27,6 +27,7 @@
#include "clangdocuments.h" #include "clangdocuments.h"
#include "clangfilesystemwatcher.h" #include "clangfilesystemwatcher.h"
#include "clangtranslationunits.h"
#include "codecompleter.h" #include "codecompleter.h"
#include "diagnosticset.h" #include "diagnosticset.h"
#include "highlightingmarks.h" #include "highlightingmarks.h"
@@ -126,10 +127,16 @@ void ClangCodeModelServer::updateTranslationUnitsForEditor(const UpdateTranslati
try { try {
const auto newerFileContainers = documents.newerFileContainers(message.fileContainers()); const auto newerFileContainers = documents.newerFileContainers(message.fileContainers());
if (newerFileContainers.size() > 0) { if (newerFileContainers.size() > 0) {
documents.update(newerFileContainers); const std::vector<Document> updateDocuments = documents.update(newerFileContainers);
unsavedFiles.createOrUpdate(newerFileContainers); unsavedFiles.createOrUpdate(newerFileContainers);
updateDocumentAnnotationsTimer.start(updateDocumentAnnotationsTimeOutInMs); // Start the jobs on the next event loop iteration since otherwise
// we might block the translation unit for a completion request
// that comes right after this message.
updateDocumentAnnotationsTimer.start(0);
QTimer::singleShot(0, [this, updateDocuments](){
startInitializingSupportiveTranslationUnits(updateDocuments);
});
} }
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::updateTranslationUnitsForEditor:" << exception.what(); qWarning() << "Error in ClangCodeModelServer::updateTranslationUnitsForEditor:" << exception.what();
@@ -141,6 +148,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn
TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor"); TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor");
try { try {
for (const auto &fileContainer : message.fileContainers()) {
const Document &document = documents.document(fileContainer);
documentProcessors().remove(document);
}
documents.remove(message.fileContainers()); documents.remove(message.fileContainers());
unsavedFiles.remove(message.fileContainers()); unsavedFiles.remove(message.fileContainers());
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
@@ -211,8 +222,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
jobRequest.column = message.column(); jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber(); jobRequest.ticketNumber = message.ticketNumber();
jobs().add(jobRequest); DocumentProcessor processor = documentProcessors().processor(document);
jobs().process(); processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what(); qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what();
} }
@@ -229,8 +241,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
const JobRequest jobRequest = createJobRequest(document, const JobRequest jobRequest = createJobRequest(document,
JobRequest::Type::RequestDocumentAnnotations); JobRequest::Type::RequestDocumentAnnotations);
jobs().add(jobRequest); DocumentProcessor processor = documentProcessors().processor(document);
jobs().process(); processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what(); qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what();
} }
@@ -260,9 +273,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen
updateDocumentAnnotationsTimer.start(0); updateDocumentAnnotationsTimer.start(0);
} }
const Jobs &ClangCodeModelServer::jobsForTestOnly() QList<Jobs::RunningJob> ClangCodeModelServer::runningJobsForTestsOnly()
{ {
return jobs(); return documentProcessors().runningJobs();
}
int ClangCodeModelServer::queueSizeForTestsOnly()
{
return documentProcessors().queueSize();
} }
bool ClangCodeModelServer::isTimerRunningForTestOnly() const bool ClangCodeModelServer::isTimerRunningForTestOnly() const
@@ -270,32 +288,55 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const
return updateDocumentAnnotationsTimer.isActive(); return updateDocumentAnnotationsTimer.isActive();
} }
void ClangCodeModelServer::addJobRequestsForDirtyAndVisibleDocuments()
{
for (const auto &document : documents.documents()) {
if (document.isNeedingReparse() && document.isVisibleInEditor())
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
}
}
void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments() void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments()
{ {
addJobRequestsForDirtyAndVisibleDocuments(); for (const auto &document : documents.documents()) {
jobs().process(); if (document.isNeedingReparse() && document.isVisibleInEditor()) {
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(createJobRequest(document,
JobRequest::Type::UpdateDocumentAnnotations,
PreferredTranslationUnit::PreviouslyParsed));
}
}
documentProcessors().process();
} }
void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents) void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents)
{ {
for (const auto &document : documents) { for (const auto &document : documents) {
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations)); DocumentProcessor processor = documentProcessors().create(document);
jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble)); const auto jobRequestCreator = [this](const Document &document,
} JobRequest::Type jobRequestType,
PreferredTranslationUnit preferredTranslationUnit) {
return createJobRequest(document, jobRequestType, preferredTranslationUnit);
};
processor.setJobRequestCreator(jobRequestCreator);
jobs().process(); processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
processor.addJob(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble));
processor.process();
}
} }
JobRequest ClangCodeModelServer::createJobRequest(const Document &document, void ClangCodeModelServer::startInitializingSupportiveTranslationUnits(
JobRequest::Type type) const const std::vector<Document> &documents)
{
for (const Document &document : documents) {
try {
DocumentProcessor processor = documentProcessors().processor(document);
if (!processor.hasSupportiveTranslationUnit())
processor.startInitializingSupportiveTranslationUnit();
} catch (const DocumentProcessorDoesNotExist &) {
// OK, document was already closed.
}
}
}
JobRequest ClangCodeModelServer::createJobRequest(
const Document &document,
JobRequest::Type type,
PreferredTranslationUnit preferredTranslationUnit) const
{ {
JobRequest jobRequest; JobRequest jobRequest;
jobRequest.type = type; jobRequest.type = type;
@@ -304,6 +345,7 @@ JobRequest ClangCodeModelServer::createJobRequest(const Document &document,
jobRequest.projectPartId = document.projectPartId(); jobRequest.projectPartId = document.projectPartId();
jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint(); jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint();
jobRequest.documentRevision = document.documentRevision(); jobRequest.documentRevision = document.documentRevision();
jobRequest.preferredTranslationUnit = preferredTranslationUnit;
const ProjectPart &projectPart = projects.project(document.projectPartId()); const ProjectPart &projectPart = projects.project(document.projectPartId());
jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint(); jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint();
@@ -315,16 +357,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i
updateDocumentAnnotationsTimeOutInMs = value; updateDocumentAnnotationsTimeOutInMs = value;
} }
Jobs &ClangCodeModelServer::jobs() DocumentProcessors &ClangCodeModelServer::documentProcessors()
{ {
if (!jobs_) { if (!documentProcessors_) {
// Jobs needs a reference to the client, but the client is not known at // DocumentProcessors needs a reference to the client, but the client
// construction time of ClangCodeModelServer, so construct Jobs in a // is not known at construction time of ClangCodeModelServer, so
// lazy manner. // construct DocumentProcessors in a lazy manner.
jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client())); documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client()));
} }
return *jobs_.data(); return *documentProcessors_.data();
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -31,8 +31,9 @@
#include "projects.h" #include "projects.h"
#include "clangdocument.h" #include "clangdocument.h"
#include "clangdocuments.h" #include "clangdocuments.h"
#include "clangdocumentprocessors.h"
#include "clangjobrequest.h"
#include "unsavedfiles.h" #include "unsavedfiles.h"
#include "clangjobs.h"
#include <utf8string.h> #include <utf8string.h>
@@ -58,27 +59,33 @@ public:
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
public /*for tests*/: public: // for tests
const Documents &documentsForTestOnly() const; const Documents &documentsForTestOnly() const;
const Jobs &jobsForTestOnly(); QList<Jobs::RunningJob> runningJobsForTestsOnly();
int queueSizeForTestsOnly();
bool isTimerRunningForTestOnly() const; bool isTimerRunningForTestOnly() const;
void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value); void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value);
DocumentProcessors &documentProcessors();
private: private:
Jobs &jobs();
void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath); void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath);
void addJobRequestsForDirtyAndVisibleDocuments(); void addJobRequestsForDirtyAndVisibleDocuments();
void processJobsForDirtyAndVisibleDocuments(); void processJobsForDirtyAndVisibleDocuments();
void processInitialJobsForDocuments(const std::vector<Document> &documents); void processInitialJobsForDocuments(const std::vector<Document> &documents);
void startInitializingSupportiveTranslationUnits(const std::vector<Document> &documents);
JobRequest createJobRequest(const Document &document, JobRequest::Type type) const; JobRequest createJobRequest(const Document &document,
JobRequest::Type type,
PreferredTranslationUnit preferredTranslationUnit
= PreferredTranslationUnit::RecentlyParsed) const;
private: private:
ProjectParts projects; ProjectParts projects;
UnsavedFiles unsavedFiles; UnsavedFiles unsavedFiles;
Documents documents; Documents documents;
QScopedPointer<Jobs> jobs_;
QScopedPointer<DocumentProcessors> documentProcessors_; // Delayed initialization
QTimer updateDocumentAnnotationsTimer; QTimer updateDocumentAnnotationsTimer;
int updateDocumentAnnotationsTimeOutInMs; int updateDocumentAnnotationsTimeOutInMs;

View File

@@ -50,29 +50,28 @@ static CompleteCodeJob::AsyncResult runAsyncHelper(const TranslationUnit &transl
return asyncResult; return asyncResult;
} }
bool CompleteCodeJob::prepareAsyncRun() IAsyncJob::AsyncPrepareResult CompleteCodeJob::prepareAsyncRun()
{ {
const JobRequest jobRequest = context().jobRequest; const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::CompleteCode, return false); QTC_ASSERT(jobRequest.type == JobRequest::Type::CompleteCode, return AsyncPrepareResult());
try { try {
m_pinnedDocument = context().documentForJobRequest(); m_pinnedDocument = context().documentForJobRequest();
const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const UnsavedFiles unsavedFiles = *context().unsavedFiles; const UnsavedFiles unsavedFiles = *context().unsavedFiles;
const quint32 line = jobRequest.line; const quint32 line = jobRequest.line;
const quint32 column = jobRequest.column; const quint32 column = jobRequest.column;
setRunner([translationUnit, unsavedFiles, line, column]() { setRunner([translationUnit, unsavedFiles, line, column]() {
return runAsyncHelper(translationUnit, unsavedFiles, line, column); return runAsyncHelper(translationUnit, unsavedFiles, line, column);
}); });
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in CompleteCodeJob::prepareAsyncRun:" << exception.what(); qWarning() << "Error in CompleteCodeJob::prepareAsyncRun:" << exception.what();
return false; return AsyncPrepareResult();
} }
return true;
} }
void CompleteCodeJob::finalizeAsyncRun() void CompleteCodeJob::finalizeAsyncRun()

View File

@@ -43,7 +43,7 @@ class CompleteCodeJob : public AsyncJob<CompleteCodeJobResult>
public: public:
using AsyncResult = CompleteCodeJobResult; using AsyncResult = CompleteCodeJobResult;
bool prepareAsyncRun() override; AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override; void finalizeAsyncRun() override;
private: private:

View File

@@ -39,28 +39,28 @@ static void runAsyncHelper(const TranslationUnit &translationUnit,
translationUnit.reparse(translationUnitUpdateInput); translationUnit.reparse(translationUnitUpdateInput);
} }
bool CreateInitialDocumentPreambleJob::prepareAsyncRun() IAsyncJob::AsyncPrepareResult CreateInitialDocumentPreambleJob::prepareAsyncRun()
{ {
const JobRequest jobRequest = context().jobRequest; const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::CreateInitialDocumentPreamble, return false); QTC_ASSERT(jobRequest.type == JobRequest::Type::CreateInitialDocumentPreamble, return AsyncPrepareResult());
try { try {
m_pinnedDocument = context().documentForJobRequest(); m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer(); m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput();
setRunner([translationUnit, updateInput]() { setRunner([translationUnit, updateInput]() {
return runAsyncHelper(translationUnit, updateInput); return runAsyncHelper(translationUnit, updateInput);
}); });
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in CreateInitialDocumentPreambleJob::prepareAsyncRun:" qWarning() << "Error in CreateInitialDocumentPreambleJob::prepareAsyncRun:"
<< exception.what(); << exception.what();
return false; return AsyncPrepareResult();
} }
return true;
} }
void CreateInitialDocumentPreambleJob::finalizeAsyncRun() void CreateInitialDocumentPreambleJob::finalizeAsyncRun()

View File

@@ -33,7 +33,7 @@ namespace ClangBackEnd {
class CreateInitialDocumentPreambleJob : public AsyncJob<void> class CreateInitialDocumentPreambleJob : public AsyncJob<void>
{ {
public: public:
bool prepareAsyncRun() override; AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override; void finalizeAsyncRun() override;
private: private:

View File

@@ -32,6 +32,7 @@
#include "projectpart.h" #include "projectpart.h"
#include "clangexceptions.h" #include "clangexceptions.h"
#include "clangtranslationunit.h" #include "clangtranslationunit.h"
#include "clangtranslationunits.h"
#include "clangtranslationunitupdater.h" #include "clangtranslationunitupdater.h"
#include "unsavedfiles.h" #include "unsavedfiles.h"
#include "unsavedfile.h" #include "unsavedfile.h"
@@ -62,15 +63,14 @@ public:
const Utf8StringVector fileArguments; const Utf8StringVector fileArguments;
ProjectPart projectPart; ProjectPart projectPart;
time_point lastProjectPartChangeTimePoint; TimePoint lastProjectPartChangeTimePoint;
CXTranslationUnit translationUnit = nullptr; TranslationUnits translationUnits;
CXIndex index = nullptr;
QSet<Utf8String> dependedFilePaths; QSet<Utf8String> dependedFilePaths;
uint documentRevision = 0; uint documentRevision = 0;
time_point needsToBeReparsedChangeTimePoint; TimePoint needsToBeReparsedChangeTimePoint;
bool hasParseOrReparseFailed = false; bool hasParseOrReparseFailed = false;
bool needsToBeReparsed = false; bool needsToBeReparsed = false;
bool isUsedByCurrentEditor = false; bool isUsedByCurrentEditor = false;
@@ -85,16 +85,16 @@ DocumentData::DocumentData(const Utf8String &filePath,
filePath(filePath), filePath(filePath),
fileArguments(fileArguments), fileArguments(fileArguments),
projectPart(projectPart), projectPart(projectPart),
lastProjectPartChangeTimePoint(std::chrono::steady_clock::now()), lastProjectPartChangeTimePoint(Clock::now()),
translationUnits(filePath),
needsToBeReparsedChangeTimePoint(lastProjectPartChangeTimePoint) needsToBeReparsedChangeTimePoint(lastProjectPartChangeTimePoint)
{ {
dependedFilePaths.insert(filePath); dependedFilePaths.insert(filePath);
translationUnits.createAndAppend();
} }
DocumentData::~DocumentData() DocumentData::~DocumentData()
{ {
clang_disposeTranslationUnit(translationUnit);
clang_disposeIndex(index);
} }
Document::Document(const Utf8String &filePath, Document::Document(const Utf8String &filePath,
@@ -183,7 +183,7 @@ const ProjectPart &Document::projectPart() const
return d->projectPart; return d->projectPart;
} }
const time_point Document::lastProjectPartChangeTimePoint() const const TimePoint Document::lastProjectPartChangeTimePoint() const
{ {
checkIfNull(); checkIfNull();
@@ -239,7 +239,7 @@ void Document::setIsVisibleInEditor(bool isVisibleInEditor)
d->isVisibleInEditor = isVisibleInEditor; d->isVisibleInEditor = isVisibleInEditor;
} }
time_point Document::isNeededReparseChangeTimePoint() const TimePoint Document::isNeededReparseChangeTimePoint() const
{ {
checkIfNull(); checkIfNull();
@@ -282,8 +282,13 @@ TranslationUnitUpdateInput Document::createUpdateInput() const
TranslationUnitUpdater Document::createUpdater() const TranslationUnitUpdater Document::createUpdater() const
{ {
TranslationUnit unit = translationUnit();
const TranslationUnitUpdateInput updateInput = createUpdateInput(); const TranslationUnitUpdateInput updateInput = createUpdateInput();
TranslationUnitUpdater updater(d->index, d->translationUnit, updateInput); TranslationUnitUpdater updater(unit.id(),
unit.cxIndex(),
unit.cxTranslationUnit(),
updateInput);
return updater; return updater;
} }
@@ -304,9 +309,13 @@ void Document::incorporateUpdaterResult(const TranslationUnitUpdateResult &resul
if (result.hasParsed()) if (result.hasParsed())
d->lastProjectPartChangeTimePoint = result.parseTimePoint; d->lastProjectPartChangeTimePoint = result.parseTimePoint;
if (result.hasParsed() || result.hasReparsed()) if (result.hasParsed() || result.hasReparsed()) {
d->dependedFilePaths = result.dependedOnFilePaths; d->dependedFilePaths = result.dependedOnFilePaths;
const TimePoint timePoint = qMax(result.parseTimePoint, result.reparseTimePoint);
d->translationUnits.updateParseTimePoint(result.translationUnitId, timePoint);
}
d->documents.addWatchedFiles(d->dependedFilePaths); d->documents.addWatchedFiles(d->dependedFilePaths);
if (result.hasReparsed() if (result.hasReparsed()
@@ -315,11 +324,16 @@ void Document::incorporateUpdaterResult(const TranslationUnitUpdateResult &resul
} }
} }
TranslationUnit Document::translationUnit() const TranslationUnit Document::translationUnit(PreferredTranslationUnit preferredTranslationUnit) const
{ {
checkIfNull(); checkIfNull();
return TranslationUnit(d->filePath, d->index, d->translationUnit); return d->translationUnits.get(preferredTranslationUnit);
}
TranslationUnits &Document::translationUnits() const
{
return d->translationUnits;
} }
void Document::parse() const void Document::parse() const
@@ -352,7 +366,7 @@ const QSet<Utf8String> Document::dependedFilePaths() const
void Document::setDirty() void Document::setDirty()
{ {
d->needsToBeReparsedChangeTimePoint = std::chrono::steady_clock::now(); d->needsToBeReparsedChangeTimePoint = Clock::now();
d->needsToBeReparsed = true; d->needsToBeReparsed = true;
} }

View File

@@ -27,6 +27,7 @@
#include "clangtranslationunitupdater.h" #include "clangtranslationunitupdater.h"
#include "clangbackend_global.h"
#include "clangtranslationunit.h" #include "clangtranslationunit.h"
#include <utf8stringvector.h> #include <utf8stringvector.h>
@@ -36,7 +37,6 @@
#include <QSet> #include <QSet>
#include <QtGlobal> #include <QtGlobal>
#include <chrono>
#include <memory> #include <memory>
class Utf8String; class Utf8String;
@@ -44,14 +44,13 @@ class Utf8String;
namespace ClangBackEnd { namespace ClangBackEnd {
class TranslationUnit; class TranslationUnit;
class TranslationUnits;
class DocumentData; class DocumentData;
class TranslationUnitUpdateResult; class TranslationUnitUpdateResult;
class ProjectPart; class ProjectPart;
class FileContainer; class FileContainer;
class Documents; class Documents;
using time_point = std::chrono::steady_clock::time_point;
class Document class Document
{ {
public: public:
@@ -85,7 +84,7 @@ public:
Utf8String projectPartId() const; Utf8String projectPartId() const;
const ProjectPart &projectPart() const; const ProjectPart &projectPart() const;
const time_point lastProjectPartChangeTimePoint() const; const TimePoint lastProjectPartChangeTimePoint() const;
bool isProjectPartOutdated() const; bool isProjectPartOutdated() const;
uint documentRevision() const; uint documentRevision() const;
@@ -104,7 +103,9 @@ public:
TranslationUnitUpdateInput createUpdateInput() const; TranslationUnitUpdateInput createUpdateInput() const;
void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const; void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const;
TranslationUnit translationUnit() const; TranslationUnit translationUnit(PreferredTranslationUnit preferredTranslationUnit
= PreferredTranslationUnit::RecentlyParsed) const;
TranslationUnits &translationUnits() const;
public: // for tests public: // for tests
void parse() const; void parse() const;
@@ -112,7 +113,7 @@ public: // for tests
const QSet<Utf8String> dependedFilePaths() const; const QSet<Utf8String> dependedFilePaths() const;
TranslationUnitUpdater createUpdater() const; TranslationUnitUpdater createUpdater() const;
void setHasParseOrReparseFailed(bool hasFailed); void setHasParseOrReparseFailed(bool hasFailed);
time_point isNeededReparseChangeTimePoint() const; TimePoint isNeededReparseChangeTimePoint() const;
private: private:
void setDirty(); void setDirty();

View File

@@ -0,0 +1,128 @@
/****************************************************************************
**
** 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 "clangdocumentprocessor.h"
#include "clangdocuments.h"
#include "clangjobs.h"
#include "clangsupportivetranslationunitinitializer.h"
#include "clangdocument.h"
#include "clangtranslationunits.h"
#include <utils/qtcassert.h>
namespace ClangBackEnd {
class DocumentProcessorData
{
public:
DocumentProcessorData(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: document(document)
, documents(documents)
, jobs(documents, unsavedFiles, projects, client)
, supportiveTranslationUnitInitializer(document, jobs)
{
const auto isDocumentClosedChecker = [this](const Utf8String &filePath,
const Utf8String &projectPartId) {
return !this->documents.hasDocument(filePath, projectPartId);
};
supportiveTranslationUnitInitializer.setIsDocumentClosedChecker(isDocumentClosedChecker);
}
public:
Document document;
Documents &documents;
Jobs jobs;
SupportiveTranslationUnitInitializer supportiveTranslationUnitInitializer;
JobRequestCreator jobRequestCreator;
};
DocumentProcessor::DocumentProcessor(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: d(std::make_shared<DocumentProcessorData>(document,
documents,
unsavedFiles,
projects,
client))
{
}
void DocumentProcessor::setJobRequestCreator(const JobRequestCreator &creator)
{
d->supportiveTranslationUnitInitializer.setJobRequestCreator(creator);
}
void DocumentProcessor::addJob(const JobRequest &jobRequest)
{
d->jobs.add(jobRequest);
}
JobRequests DocumentProcessor::process()
{
return d->jobs.process();
}
Document DocumentProcessor::document() const
{
return d->document;
}
bool DocumentProcessor::hasSupportiveTranslationUnit() const
{
return d->supportiveTranslationUnitInitializer.state()
!= SupportiveTranslationUnitInitializer::State::NotInitialized;
}
void DocumentProcessor::startInitializingSupportiveTranslationUnit()
{
d->supportiveTranslationUnitInitializer.startInitializing();
}
bool DocumentProcessor::isSupportiveTranslationUnitInitialized() const
{
return d->supportiveTranslationUnitInitializer.state()
== SupportiveTranslationUnitInitializer::State::Initialized;
}
QList<Jobs::RunningJob> DocumentProcessor::runningJobs() const
{
return d->jobs.runningJobs();
}
int DocumentProcessor::queueSize() const
{
return d->jobs.queue().size();
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** 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 "clangjobs.h"
#include <memory>
namespace ClangBackEnd {
class ClangCodeModelClientInterface;
class Document;
class Documents;
class DocumentProcessorData;
class JobRequest;
class ProjectParts;
class UnsavedFiles;
class DocumentProcessor
{
public:
DocumentProcessor(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client);
void setJobRequestCreator(const JobRequestCreator &creator);
void addJob(const JobRequest &jobRequest);
JobRequests process();
Document document() const;
bool hasSupportiveTranslationUnit() const;
void startInitializingSupportiveTranslationUnit();
public: // for tests
bool isSupportiveTranslationUnitInitialized() const;
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
std::shared_ptr<DocumentProcessorData> d;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,113 @@
/****************************************************************************
**
** 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 "clangdocumentprocessors.h"
#include "clangdocument.h"
#include "clangexceptions.h"
namespace ClangBackEnd {
DocumentProcessors::DocumentProcessors(Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: m_documents(documents)
, m_unsavedFiles(unsavedFiles)
, m_projects(projects)
, m_client(client)
{
}
static bool operator<(const DocumentId &lhs, const DocumentId &rhs)
{
return lhs.filePath < rhs.filePath
|| (lhs.filePath == rhs.filePath && lhs.projectPartId < lhs.projectPartId);
}
DocumentProcessor DocumentProcessors::create(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
if (m_processors.contains(id))
throw DocumentProcessorAlreadyExists(document.filePath(), document.projectPartId());
const DocumentProcessor element(document, m_documents, m_unsavedFiles, m_projects, m_client);
m_processors.insert(id, element);
return element;
}
DocumentProcessor DocumentProcessors::processor(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
const auto it = m_processors.find(id);
if (it == m_processors.end())
throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId());
return *it;
}
QList<DocumentProcessor> DocumentProcessors::processors() const
{
return m_processors.values();
}
void DocumentProcessors::remove(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
const int itemsRemoved = m_processors.remove(id);
if (itemsRemoved != 1)
throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId());
}
JobRequests DocumentProcessors::process()
{
JobRequests jobsStarted;
for (auto &processor : m_processors)
jobsStarted += processor.process();
return jobsStarted;
}
QList<Jobs::RunningJob> DocumentProcessors::runningJobs() const
{
QList<Jobs::RunningJob> jobs;
for (auto &processor : m_processors)
jobs += processor.runningJobs();
return jobs;
}
int DocumentProcessors::queueSize() const
{
int total = 0;
for (auto &processor : m_processors)
total += processor.queueSize();
return total;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,74 @@
/****************************************************************************
**
** 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 "clangdocumentprocessor.h"
#include "clangjobs.h"
#include <utf8string.h>
#include <QMap>
namespace ClangBackEnd {
class Document;
class DocumentProcessor;
class DocumentId {
public:
Utf8String filePath;
Utf8String projectPartId;
};
class DocumentProcessors
{
public:
DocumentProcessors(Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client);
DocumentProcessor create(const Document &document);
DocumentProcessor processor(const Document &document);
void remove(const Document &document);
JobRequests process();
public: // for tests
QList<DocumentProcessor> processors() const;
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
Documents &m_documents;
UnsavedFiles &m_unsavedFiles;
ProjectParts &m_projects;
ClangCodeModelClientInterface &m_client;
QMap<DocumentId, DocumentProcessor> m_processors;
};
} // namespace ClangBackEnd

View File

@@ -68,14 +68,20 @@ std::vector<Document> Documents::create(const QVector<FileContainer> &fileContai
return createdDocuments; return createdDocuments;
} }
void Documents::update(const QVector<FileContainer> &fileContainers) std::vector<Document> Documents::update(const QVector<FileContainer> &fileContainers)
{ {
checkIfDocumentsForFilePathsExist(fileContainers); checkIfDocumentsForFilePathsExist(fileContainers);
std::vector<Document> createdDocuments;
for (const FileContainer &fileContainer : fileContainers) { for (const FileContainer &fileContainer : fileContainers) {
updateDocument(fileContainer); const std::vector<Document> documents = updateDocument(fileContainer);
createdDocuments.insert(createdDocuments.end(), documents.begin(), documents.end());
updateDocumentsWithChangedDependency(fileContainer.filePath()); updateDocumentsWithChangedDependency(fileContainer.filePath());
} }
return createdDocuments;
} }
static bool removeFromFileContainer(QVector<FileContainer> &fileContainers, const Document &document) static bool removeFromFileContainer(QVector<FileContainer> &fileContainers, const Document &document)
@@ -205,12 +211,14 @@ Document Documents::createDocument(const FileContainer &fileContainer)
return documents_.back(); return documents_.back();
} }
void Documents::updateDocument(const FileContainer &fileContainer) std::vector<Document> Documents::updateDocument(const FileContainer &fileContainer)
{ {
const auto documents = findAllDocumentsWithFilePath(fileContainer.filePath()); const auto documents = findAllDocumentsWithFilePath(fileContainer.filePath());
for (auto document : documents) for (auto document : documents)
document.setDocumentRevision(fileContainer.documentRevision()); document.setDocumentRevision(fileContainer.documentRevision());
return documents;
} }
std::vector<Document>::iterator Documents::findDocument(const FileContainer &fileContainer) std::vector<Document>::iterator Documents::findDocument(const FileContainer &fileContainer)

View File

@@ -45,7 +45,7 @@ public:
Documents(ProjectParts &projectParts, UnsavedFiles &unsavedFiles); Documents(ProjectParts &projectParts, UnsavedFiles &unsavedFiles);
std::vector<Document> create(const QVector<FileContainer> &fileContainers); std::vector<Document> create(const QVector<FileContainer> &fileContainers);
void update(const QVector<FileContainer> &fileContainers); std::vector<Document> update(const QVector<FileContainer> &fileContainers);
void remove(const QVector<FileContainer> &fileContainers); void remove(const QVector<FileContainer> &fileContainers);
void setUsedByCurrentEditor(const Utf8String &filePath); void setUsedByCurrentEditor(const Utf8String &filePath);
@@ -72,7 +72,7 @@ public:
private: private:
Document createDocument(const FileContainer &fileContainer); Document createDocument(const FileContainer &fileContainer);
void updateDocument(const FileContainer &fileContainer); std::vector<Document> updateDocument(const FileContainer &fileContainer);
std::vector<Document>::iterator findDocument(const FileContainer &fileContainer); std::vector<Document>::iterator findDocument(const FileContainer &fileContainer);
std::vector<Document> findAllDocumentsWithFilePath(const Utf8String &filePath); std::vector<Document> findAllDocumentsWithFilePath(const Utf8String &filePath);
std::vector<Document>::const_iterator findDocument(const Utf8String &filePath, const Utf8String &projectPartId) const; std::vector<Document>::const_iterator findDocument(const Utf8String &filePath, const Utf8String &projectPartId) const;

View File

@@ -74,4 +74,31 @@ DocumentIsNullException::DocumentIsNullException()
m_info = Utf8String::fromUtf8("Tried to access a null Document!"); m_info = Utf8String::fromUtf8("Tried to access a null Document!");
} }
DocumentProcessorAlreadyExists::DocumentProcessorAlreadyExists(const Utf8String &filePath,
const Utf8String &projectPartId)
{
m_info = Utf8StringLiteral("Document processor for file '")
+ filePath
+ Utf8StringLiteral("' and project part id '")
+ projectPartId
+ Utf8StringLiteral("' already exists!");
}
DocumentProcessorDoesNotExist::DocumentProcessorDoesNotExist(const Utf8String &filePath,
const Utf8String &projectPartId)
{
m_info = Utf8StringLiteral("Document processor for file '")
+ filePath
+ Utf8StringLiteral("' and project part id '")
+ projectPartId
+ Utf8StringLiteral("' does not exist!");
}
TranslationUnitDoesNotExist::TranslationUnitDoesNotExist(const Utf8String &filePath)
{
m_info += Utf8StringLiteral("TranslationUnit for file '")
+ filePath
+ Utf8StringLiteral("' does not exist.");
}
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -74,4 +74,24 @@ public:
DocumentIsNullException(); DocumentIsNullException();
}; };
class DocumentProcessorAlreadyExists : public ClangBaseException
{
public:
DocumentProcessorAlreadyExists(const Utf8String &filePath,
const Utf8String &projectPartId);
};
class DocumentProcessorDoesNotExist : public ClangBaseException
{
public:
DocumentProcessorDoesNotExist(const Utf8String &filePath,
const Utf8String &projectPartId);
};
class TranslationUnitDoesNotExist : public ClangBaseException
{
public:
TranslationUnitDoesNotExist(const Utf8String &filePath);
};
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -27,6 +27,8 @@
#include "clangcompletecodejob.h" #include "clangcompletecodejob.h"
#include "clangcreateinitialdocumentpreamblejob.h" #include "clangcreateinitialdocumentpreamblejob.h"
#include "clangparsesupportivetranslationunitjob.h"
#include "clangreparsesupportivetranslationunitjob.h"
#include "clangrequestdocumentannotationsjob.h" #include "clangrequestdocumentannotationsjob.h"
#include "clangupdatedocumentannotationsjob.h" #include "clangupdatedocumentannotationsjob.h"
@@ -39,6 +41,10 @@ IAsyncJob *IAsyncJob::create(JobRequest::Type type)
switch (type) { switch (type) {
case JobRequest::Type::UpdateDocumentAnnotations: case JobRequest::Type::UpdateDocumentAnnotations:
return new UpdateDocumentAnnotationsJob(); return new UpdateDocumentAnnotationsJob();
case JobRequest::Type::ParseSupportiveTranslationUnit:
return new ParseSupportiveTranslationUnitJob();
case JobRequest::Type::ReparseSupportiveTranslationUnit:
return new ReparseSupportiveTranslationUnitJob();
case JobRequest::Type::CreateInitialDocumentPreamble: case JobRequest::Type::CreateInitialDocumentPreamble:
return new CreateInitialDocumentPreambleJob(); return new CreateInitialDocumentPreambleJob();
case JobRequest::Type::CompleteCode: case JobRequest::Type::CompleteCode:

View File

@@ -41,6 +41,11 @@ class IAsyncJob
public: public:
static IAsyncJob *create(JobRequest::Type type); static IAsyncJob *create(JobRequest::Type type);
struct AsyncPrepareResult {
operator bool() const { return !translationUnitId.isEmpty(); }
Utf8String translationUnitId;
};
public: public:
IAsyncJob(); IAsyncJob();
virtual ~IAsyncJob(); virtual ~IAsyncJob();
@@ -52,10 +57,12 @@ public:
FinishedHandler finishedHandler() const; FinishedHandler finishedHandler() const;
void setFinishedHandler(const FinishedHandler &finishedHandler); void setFinishedHandler(const FinishedHandler &finishedHandler);
virtual bool prepareAsyncRun() = 0; virtual AsyncPrepareResult prepareAsyncRun() = 0;
virtual QFuture<void> runAsync() = 0; virtual QFuture<void> runAsync() = 0;
virtual void finalizeAsyncRun() = 0; virtual void finalizeAsyncRun() = 0;
virtual void preventFinalization() = 0;
public: // for tests public: // for tests
bool isFinished() const; bool isFinished() const;
void setIsFinished(bool isFinished); void setIsFinished(bool isFinished);

View File

@@ -27,6 +27,7 @@
#include "clangjobqueue.h" #include "clangjobqueue.h"
#include "clangdocument.h" #include "clangdocument.h"
#include "clangdocuments.h" #include "clangdocuments.h"
#include "clangtranslationunits.h"
#include "projects.h" #include "projects.h"
#include "unsavedfiles.h" #include "unsavedfiles.h"
@@ -40,11 +41,22 @@ JobQueue::JobQueue(Documents &documents, ProjectParts &projectParts)
{ {
} }
void JobQueue::add(const JobRequest &job) bool JobQueue::add(const JobRequest &job)
{ {
qCDebug(jobsLog) << "Adding" << job; if (m_queue.contains(job)) {
qCDebug(jobsLog) << "Not adding duplicate request" << job;
return false;
}
if (isJobRunningForJobRequest(job)) {
qCDebug(jobsLog) << "Not adding duplicate request for already running job" << job;
return false;
}
qCDebug(jobsLog) << "Adding" << job;
m_queue.append(job); m_queue.append(job);
return true;
} }
int JobQueue::size() const int JobQueue::size() const
@@ -164,50 +176,65 @@ void JobQueue::prioritizeRequests()
JobRequests JobQueue::takeJobRequestsToRunNow() JobRequests JobQueue::takeJobRequestsToRunNow()
{ {
JobRequests jobsToRun; JobRequests jobsToRun;
QSet<DocumentId> documentsScheduledForThisRun; using TranslationUnitIds = QSet<Utf8String>;
TranslationUnitIds translationUnitsScheduledForThisRun;
QMutableVectorIterator<JobRequest> i(m_queue); QMutableVectorIterator<JobRequest> i(m_queue);
while (i.hasNext()) { while (i.hasNext()) {
const JobRequest &jobRequest = i.next(); const JobRequest &request = i.next();
try { try {
const Document &document const Document &document = m_documents.document(request.filePath,
= m_documents.document(jobRequest.filePath, request.projectPartId);
jobRequest.projectPartId);
const DocumentId documentId = DocumentId(jobRequest.filePath, jobRequest.projectPartId);
if (!document.isUsedByCurrentEditor() && !document.isVisibleInEditor()) if (!document.isUsedByCurrentEditor() && !document.isVisibleInEditor())
continue; continue;
if (documentsScheduledForThisRun.contains(documentId)) const Utf8String id = document.translationUnit(request.preferredTranslationUnit).id();
if (translationUnitsScheduledForThisRun.contains(id))
continue; continue;
if (isJobRunningForDocument(documentId)) if (isJobRunningForTranslationUnit(id))
continue; continue;
documentsScheduledForThisRun.insert(documentId); translationUnitsScheduledForThisRun.insert(id);
jobsToRun += jobRequest; jobsToRun += request;
i.remove(); i.remove();
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in Jobs::takeJobRequestsToRunNow for" qWarning() << "Error in Jobs::takeJobRequestsToRunNow for"
<< jobRequest << ":" << exception.what(); << request << ":" << exception.what();
} }
} }
return jobsToRun; return jobsToRun;
} }
bool JobQueue::isJobRunningForDocument(const JobQueue::DocumentId &documentId) bool JobQueue::isJobRunningForTranslationUnit(const Utf8String &translationUnitId)
{ {
if (m_isJobRunningHandler) if (m_isJobRunningForTranslationUnitHandler)
return m_isJobRunningHandler(documentId.first, documentId.second); return m_isJobRunningForTranslationUnitHandler(translationUnitId);
return false; return false;
} }
void JobQueue::setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler) bool JobQueue::isJobRunningForJobRequest(const JobRequest &jobRequest)
{ {
m_isJobRunningHandler = isJobRunningHandler; if (m_isJobRunningForJobRequestHandler)
return m_isJobRunningForJobRequestHandler(jobRequest);
return false;
}
void JobQueue::setIsJobRunningForTranslationUnitHandler(
const IsJobRunningForTranslationUnitHandler &isJobRunningHandler)
{
m_isJobRunningForTranslationUnitHandler = isJobRunningHandler;
}
void JobQueue::setIsJobRunningForJobRequestHandler(
const JobQueue::IsJobRunningForJobRequestHandler &isJobRunningHandler)
{
m_isJobRunningForJobRequestHandler = isJobRunningHandler;
} }
JobRequests JobQueue::queue() const JobRequests JobQueue::queue() const

View File

@@ -39,12 +39,17 @@ class JobQueue
public: public:
JobQueue(Documents &documents, ProjectParts &projects); JobQueue(Documents &documents, ProjectParts &projects);
void add(const JobRequest &job); bool add(const JobRequest &job);
JobRequests processQueue(); JobRequests processQueue();
using IsJobRunningHandler = std::function<bool(const Utf8String &, const Utf8String &)>; using IsJobRunningForTranslationUnitHandler = std::function<bool(const Utf8String &)>;
void setIsJobRunningHandler(const IsJobRunningHandler &isJobRunningHandler); void setIsJobRunningForTranslationUnitHandler(
const IsJobRunningForTranslationUnitHandler &isJobRunningHandler);
using IsJobRunningForJobRequestHandler = std::function<bool(const JobRequest &)>;
void setIsJobRunningForJobRequestHandler(
const IsJobRunningForJobRequestHandler &isJobRunningHandler);
public: // for tests public: // for tests
JobRequests queue() const; JobRequests queue() const;
@@ -52,8 +57,8 @@ public: // for tests
void prioritizeRequests(); void prioritizeRequests();
private: private:
using DocumentId = QPair<Utf8String, Utf8String>; bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId);
bool isJobRunningForDocument(const DocumentId &documentId); bool isJobRunningForJobRequest(const JobRequest &jobRequest);
JobRequests takeJobRequestsToRunNow(); JobRequests takeJobRequestsToRunNow();
void removeOutDatedRequests(); void removeOutDatedRequests();
bool isJobRequestOutDated(const JobRequest &jobRequest) const; bool isJobRequestOutDated(const JobRequest &jobRequest) const;
@@ -62,7 +67,8 @@ private:
Documents &m_documents; Documents &m_documents;
ProjectParts &m_projectParts; ProjectParts &m_projectParts;
IsJobRunningHandler m_isJobRunningHandler; IsJobRunningForTranslationUnitHandler m_isJobRunningForTranslationUnitHandler;
IsJobRunningForJobRequestHandler m_isJobRunningForJobRequestHandler;
JobRequests m_queue; JobRequests m_queue;
}; };

View File

@@ -25,6 +25,8 @@
#include "clangjobrequest.h" #include "clangjobrequest.h"
#include <QFileInfo>
namespace ClangBackEnd { namespace ClangBackEnd {
#define RETURN_TEXT_FOR_CASE(enumValue) case JobRequest::Type::enumValue: return #enumValue #define RETURN_TEXT_FOR_CASE(enumValue) case JobRequest::Type::enumValue: return #enumValue
@@ -32,6 +34,8 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
{ {
switch (type) { switch (type) {
RETURN_TEXT_FOR_CASE(UpdateDocumentAnnotations); RETURN_TEXT_FOR_CASE(UpdateDocumentAnnotations);
RETURN_TEXT_FOR_CASE(ParseSupportiveTranslationUnit);
RETURN_TEXT_FOR_CASE(ReparseSupportiveTranslationUnit);
RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble); RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble);
RETURN_TEXT_FOR_CASE(CompleteCode); RETURN_TEXT_FOR_CASE(CompleteCode);
RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations); RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations);
@@ -41,6 +45,19 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
} }
#undef RETURN_TEXT_FOR_CASE #undef RETURN_TEXT_FOR_CASE
#define RETURN_TEXT_FOR_CASE(enumValue) case PreferredTranslationUnit::enumValue: return #enumValue
const char *preferredTranslationUnitToText(PreferredTranslationUnit type)
{
switch (type) {
RETURN_TEXT_FOR_CASE(RecentlyParsed);
RETURN_TEXT_FOR_CASE(PreviouslyParsed);
RETURN_TEXT_FOR_CASE(LastUninitialized);
}
return "UnhandledPreferredTranslationUnitType";
}
#undef RETURN_TEXT_FOR_CASE
QDebug operator<<(QDebug debug, JobRequest::Type type) QDebug operator<<(QDebug debug, JobRequest::Type type)
{ {
debug << JobRequestTypeToText(type); debug << JobRequestTypeToText(type);
@@ -53,12 +70,14 @@ QDebug operator<<(QDebug debug, const JobRequest &jobRequest)
debug.nospace() << "Job<" debug.nospace() << "Job<"
<< jobRequest.id << jobRequest.id
<< "," << ","
<< QFileInfo(jobRequest.filePath).fileName()
<< ","
<< JobRequestTypeToText(jobRequest.type) << JobRequestTypeToText(jobRequest.type)
<< "," << ","
<< jobRequest.filePath << preferredTranslationUnitToText(jobRequest.preferredTranslationUnit)
<< ">"; << ">";
return debug; return debug.space();
} }
JobRequest::JobRequest() JobRequest::JobRequest()
@@ -67,6 +86,23 @@ JobRequest::JobRequest()
id = ++idCounter; id = ++idCounter;
} }
bool JobRequest::operator==(const JobRequest &other) const
{
return type == other.type
&& requirements == other.requirements
&& filePath == other.filePath
&& projectPartId == other.projectPartId
&& unsavedFilesChangeTimePoint == other.unsavedFilesChangeTimePoint
&& projectChangeTimePoint == other.projectChangeTimePoint
&& documentRevision == other.documentRevision
&& preferredTranslationUnit == other.preferredTranslationUnit
&& line == other.line
&& column == other.column
&& ticketNumber == other.ticketNumber;
}
JobRequest::Requirements JobRequest::requirementsForType(Type type) JobRequest::Requirements JobRequest::requirementsForType(Type type)
{ {
switch (type) { switch (type) {
@@ -77,6 +113,8 @@ JobRequest::Requirements JobRequest::requirementsForType(Type type)
|JobRequest::CurrentDocumentRevision); |JobRequest::CurrentDocumentRevision);
case JobRequest::Type::CompleteCode: case JobRequest::Type::CompleteCode:
case JobRequest::Type::CreateInitialDocumentPreamble: case JobRequest::Type::CreateInitialDocumentPreamble:
case JobRequest::Type::ParseSupportiveTranslationUnit:
case JobRequest::Type::ReparseSupportiveTranslationUnit:
return JobRequest::Requirements(JobRequest::DocumentValid); return JobRequest::Requirements(JobRequest::DocumentValid);
} }

View File

@@ -25,17 +25,20 @@
#pragma once #pragma once
#include "clangbackend_global.h"
#include "clangclock.h"
#include <utf8string.h> #include <utf8string.h>
#include <QFlags> #include <QFlags>
#include <QDebug> #include <QDebug>
#include <QVector> #include <QVector>
#include <chrono> #include <functional>
namespace ClangBackEnd { namespace ClangBackEnd {
using time_point = std::chrono::steady_clock::time_point; class Document;
class JobRequest class JobRequest
{ {
@@ -43,6 +46,10 @@ public:
enum class Type { enum class Type {
UpdateDocumentAnnotations, UpdateDocumentAnnotations,
CreateInitialDocumentPreamble, CreateInitialDocumentPreamble,
ParseSupportiveTranslationUnit,
ReparseSupportiveTranslationUnit,
CompleteCode, CompleteCode,
RequestDocumentAnnotations, RequestDocumentAnnotations,
}; };
@@ -64,6 +71,8 @@ public:
JobRequest(); JobRequest();
bool operator==(const JobRequest &other) const;
public: public:
quint64 id = 0; quint64 id = 0;
Type type; Type type;
@@ -72,9 +81,10 @@ public:
// General // General
Utf8String filePath; Utf8String filePath;
Utf8String projectPartId; Utf8String projectPartId;
time_point unsavedFilesChangeTimePoint; TimePoint unsavedFilesChangeTimePoint;
time_point projectChangeTimePoint; TimePoint projectChangeTimePoint;
uint documentRevision = 0; uint documentRevision = 0;
PreferredTranslationUnit preferredTranslationUnit = PreferredTranslationUnit::RecentlyParsed;
// For code completion // For code completion
quint32 line = 0; quint32 line = 0;
@@ -83,6 +93,9 @@ public:
}; };
using JobRequests = QVector<JobRequest>; using JobRequests = QVector<JobRequest>;
using JobRequestCreator = std::function<JobRequest(const Document &,
JobRequest::Type ,
PreferredTranslationUnit)>;
QDebug operator<<(QDebug debug, const JobRequest &jobRequest); QDebug operator<<(QDebug debug, const JobRequest &jobRequest);

View File

@@ -45,17 +45,25 @@ Jobs::Jobs(Documents &documents,
, m_client(client) , m_client(client)
, m_queue(documents, projectParts) , m_queue(documents, projectParts)
{ {
m_queue.setIsJobRunningHandler([this](const Utf8String &filePath, m_queue.setIsJobRunningForTranslationUnitHandler([this](const Utf8String &translationUnitId) {
const Utf8String &projectPartId) { return isJobRunningForTranslationUnit(translationUnitId);
return isJobRunning(filePath, projectPartId); });
m_queue.setIsJobRunningForJobRequestHandler([this](const JobRequest &jobRequest) {
return isJobRunningForJobRequest(jobRequest);
}); });
} }
Jobs::~Jobs() Jobs::~Jobs()
{ {
foreach (IAsyncJob *asyncJob, m_running.keys())
asyncJob->preventFinalization();
QFutureSynchronizer<void> waitForFinishedJobs; QFutureSynchronizer<void> waitForFinishedJobs;
foreach (const RunningJob &runningJob, m_running.values()) foreach (const RunningJob &runningJob, m_running.values())
waitForFinishedJobs.addFuture(runningJob.future); waitForFinishedJobs.addFuture(runningJob.future);
foreach (IAsyncJob *asyncJob, m_running.keys())
delete asyncJob;
} }
void Jobs::add(const JobRequest &job) void Jobs::add(const JobRequest &job)
@@ -87,22 +95,25 @@ JobRequests Jobs::runJobs(const JobRequests &jobsRequests)
bool Jobs::runJob(const JobRequest &jobRequest) bool Jobs::runJob(const JobRequest &jobRequest)
{ {
if (IAsyncJob *asyncJob = IAsyncJob::create(jobRequest.type)) { IAsyncJob *asyncJob = IAsyncJob::create(jobRequest.type);
JobContext context(jobRequest, &m_documents, &m_unsavedFiles, &m_client); QTC_ASSERT(asyncJob, return false);
asyncJob->setContext(context);
if (asyncJob->prepareAsyncRun()) { JobContext context(jobRequest, &m_documents, &m_unsavedFiles, &m_client);
qCDebug(jobsLog) << "Running" << jobRequest; asyncJob->setContext(context);
asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); }); if (const IAsyncJob::AsyncPrepareResult prepareResult = asyncJob->prepareAsyncRun()) {
const QFuture<void> future = asyncJob->runAsync(); qCDebug(jobsLog) << "Running" << jobRequest
<< "with TranslationUnit" << prepareResult.translationUnitId;
m_running.insert(asyncJob, RunningJob{jobRequest, future}); asyncJob->setFinishedHandler([this](IAsyncJob *asyncJob){ onJobFinished(asyncJob); });
return true; const QFuture<void> future = asyncJob->runAsync();
} else {
qCDebug(jobsLog) << "Preparation failed for " << jobRequest; const RunningJob runningJob{jobRequest, prepareResult.translationUnitId, future};
delete asyncJob; m_running.insert(asyncJob, runningJob);
} return true;
} else {
qCDebug(jobsLog) << "Preparation failed for " << jobRequest;
delete asyncJob;
} }
return false; return false;
@@ -112,15 +123,25 @@ void Jobs::onJobFinished(IAsyncJob *asyncJob)
{ {
qCDebug(jobsLog) << "Finishing" << asyncJob->context().jobRequest; qCDebug(jobsLog) << "Finishing" << asyncJob->context().jobRequest;
if (m_jobFinishedCallback) {
const RunningJob runningJob = m_running.value(asyncJob);
m_jobFinishedCallback(runningJob);
}
m_running.remove(asyncJob); m_running.remove(asyncJob);
delete asyncJob; delete asyncJob;
process(); process();
} }
int Jobs::runningJobs() const void Jobs::setJobFinishedCallback(const JobFinishedCallback &jobFinishedCallback)
{ {
return m_running.size(); m_jobFinishedCallback = jobFinishedCallback;
}
QList<Jobs::RunningJob> Jobs::runningJobs() const
{
return m_running.values();
} }
JobRequests Jobs::queue() const JobRequests Jobs::queue() const
@@ -128,12 +149,19 @@ JobRequests Jobs::queue() const
return m_queue.queue(); return m_queue.queue();
} }
bool Jobs::isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const bool Jobs::isJobRunningForTranslationUnit(const Utf8String &translationUnitId) const
{ {
const auto hasJobRequest = [filePath, projectPartId](const RunningJob &runningJob) { const auto hasTranslationUnitId = [translationUnitId](const RunningJob &runningJob) {
const JobRequest &jobRequest = runningJob.jobRequest; return runningJob.translationUnitId == translationUnitId;
return filePath == jobRequest.filePath };
&& projectPartId == jobRequest.projectPartId;
return Utils::anyOf(m_running.values(), hasTranslationUnitId);
}
bool Jobs::isJobRunningForJobRequest(const JobRequest &jobRequest) const
{
const auto hasJobRequest = [jobRequest](const RunningJob &runningJob) {
return runningJob.jobRequest == jobRequest;
}; };
return Utils::anyOf(m_running.values(), hasJobRequest); return Utils::anyOf(m_running.values(), hasJobRequest);

View File

@@ -31,6 +31,8 @@
#include <QFuture> #include <QFuture>
#include <functional>
namespace ClangBackEnd { namespace ClangBackEnd {
class ClangCodeModelClientInterface; class ClangCodeModelClientInterface;
@@ -43,9 +45,12 @@ class Jobs
public: public:
struct RunningJob { struct RunningJob {
JobRequest jobRequest; JobRequest jobRequest;
Utf8String translationUnitId;
QFuture<void> future; QFuture<void> future;
}; };
using RunningJobs = QHash<IAsyncJob *, RunningJob>; using RunningJobs = QHash<IAsyncJob *, RunningJob>;
using JobFinishedCallback = std::function<void(RunningJob)>;
public: public:
Jobs(Documents &documents, Jobs(Documents &documents,
@@ -58,10 +63,13 @@ public:
JobRequests process(); JobRequests process();
void setJobFinishedCallback(const JobFinishedCallback &jobFinishedCallback);
public /*for tests*/: public /*for tests*/:
int runningJobs() const; QList<RunningJob> runningJobs() const;
JobRequests queue() const; JobRequests queue() const;
bool isJobRunning(const Utf8String &filePath, const Utf8String &projectPartId) const; bool isJobRunningForTranslationUnit(const Utf8String &translationUnitId) const;
bool isJobRunningForJobRequest(const JobRequest &jobRequest) const;
private: private:
JobRequests runJobs(const JobRequests &jobRequest); JobRequests runJobs(const JobRequests &jobRequest);
@@ -75,6 +83,7 @@ private:
JobQueue m_queue; JobQueue m_queue;
RunningJobs m_running; RunningJobs m_running;
JobFinishedCallback m_jobFinishedCallback;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** 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 "clangparsesupportivetranslationunitjob.h"
#include <clangbackendipc/clangbackendipcdebugutils.h>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
static ParseSupportiveTranslationUnitJob::AsyncResult runAsyncHelper(
const TranslationUnit &translationUnit,
const TranslationUnitUpdateInput &translationUnitUpdateInput)
{
TIME_SCOPE_DURATION("ParseSupportiveTranslationUnitJob");
TranslationUnitUpdateInput updateInput = translationUnitUpdateInput;
updateInput.parseNeeded = true;
ParseSupportiveTranslationUnitJob::AsyncResult asyncResult;
asyncResult.updateResult = translationUnit.update(updateInput);
return asyncResult;
}
IAsyncJob::AsyncPrepareResult ParseSupportiveTranslationUnitJob::prepareAsyncRun()
{
const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::ParseSupportiveTranslationUnit, return AsyncPrepareResult());
try {
m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput();
setRunner([translationUnit, updateInput]() {
return runAsyncHelper(translationUnit, updateInput);
});
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) {
qWarning() << "Error in ParseForSupportiveTranslationUnitJob::prepareAsyncRun:"
<< exception.what();
return AsyncPrepareResult();
}
}
void ParseSupportiveTranslationUnitJob::finalizeAsyncRun()
{
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** 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 "clangdocument.h"
namespace ClangBackEnd {
struct ParseSupportiveTranslationUnitJobResult
{
TranslationUnitUpdateResult updateResult;
};
class ParseSupportiveTranslationUnitJob : public AsyncJob<ParseSupportiveTranslationUnitJobResult>
{
public:
using AsyncResult = ParseSupportiveTranslationUnitJobResult;
AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override;
private:
Document m_pinnedDocument;
FileContainer m_pinnedFileContainer;
};
} // namespace ClangBackEnd

View File

@@ -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.
**
****************************************************************************/
#include "clangreparsesupportivetranslationunitjob.h"
#include <clangbackendipc/clangbackendipcdebugutils.h>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
static ReparseSupportiveTranslationUnitJob::AsyncResult runAsyncHelper(
const TranslationUnit &translationUnit,
const TranslationUnitUpdateInput &translationUnitUpdateInput)
{
TIME_SCOPE_DURATION("ReparseSupportiveTranslationUnitJob");
TranslationUnitUpdateInput updateInput = translationUnitUpdateInput;
updateInput.reparseNeeded = true;
ReparseSupportiveTranslationUnitJob::AsyncResult asyncResult;
asyncResult.updateResult = translationUnit.reparse(updateInput);
return asyncResult;
}
IAsyncJob::AsyncPrepareResult ReparseSupportiveTranslationUnitJob::prepareAsyncRun()
{
const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::ReparseSupportiveTranslationUnit, return AsyncPrepareResult());
try {
m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput();
setRunner([translationUnit, updateInput]() {
return runAsyncHelper(translationUnit, updateInput);
});
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) {
qWarning() << "Error in ReparseSupportiveTranslationUnitJob::prepareAsyncRun:"
<< exception.what();
return AsyncPrepareResult();
}
}
void ReparseSupportiveTranslationUnitJob::finalizeAsyncRun()
{
if (!context().isOutdated()) {
const AsyncResult result = asyncResult();
m_pinnedDocument.incorporateUpdaterResult(result.updateResult);
}
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,51 @@
/****************************************************************************
**
** 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 "clangdocument.h"
namespace ClangBackEnd {
struct ReparseSupportiveTranslationUnitJobResult
{
TranslationUnitUpdateResult updateResult;
};
class ReparseSupportiveTranslationUnitJob : public AsyncJob<ReparseSupportiveTranslationUnitJobResult>
{
public:
using AsyncResult = ReparseSupportiveTranslationUnitJobResult;
AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override;
private:
Document m_pinnedDocument;
FileContainer m_pinnedFileContainer;
};
} // namespace ClangBackEnd

View File

@@ -40,33 +40,35 @@ static RequestDocumentAnnotationsJob::AsyncResult runAsyncHelper(
RequestDocumentAnnotationsJob::AsyncResult asyncResult; RequestDocumentAnnotationsJob::AsyncResult asyncResult;
translationUnit.extractDocumentAnnotations(asyncResult.diagnostics, translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic,
asyncResult.diagnostics,
asyncResult.highlightingMarks, asyncResult.highlightingMarks,
asyncResult.skippedSourceRanges); asyncResult.skippedSourceRanges);
return asyncResult; return asyncResult;
} }
bool RequestDocumentAnnotationsJob::prepareAsyncRun() IAsyncJob::AsyncPrepareResult RequestDocumentAnnotationsJob::prepareAsyncRun()
{ {
const JobRequest jobRequest = context().jobRequest; const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations, return false); QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations,
return AsyncPrepareResult());
try { try {
m_pinnedDocument = context().documentForJobRequest(); m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer(); m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
setRunner([translationUnit]() { setRunner([translationUnit]() {
return runAsyncHelper(translationUnit); return runAsyncHelper(translationUnit);
}); });
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in RequestDocumentAnnotationsJob::prepareAsyncRun:" << exception.what(); qWarning() << "Error in RequestDocumentAnnotationsJob::prepareAsyncRun:" << exception.what();
return false; return AsyncPrepareResult();
} }
return true;
} }
void RequestDocumentAnnotationsJob::finalizeAsyncRun() void RequestDocumentAnnotationsJob::finalizeAsyncRun()
@@ -82,6 +84,7 @@ void RequestDocumentAnnotationsJob::sendAnnotations(
{ {
const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer,
result.diagnostics, result.diagnostics,
result.firstHeaderErrorDiagnostic,
result.highlightingMarks, result.highlightingMarks,
result.skippedSourceRanges); result.skippedSourceRanges);

View File

@@ -36,6 +36,7 @@ namespace ClangBackEnd {
struct RequestDocumentAnnotationsJobResult struct RequestDocumentAnnotationsJobResult
{ {
ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic;
QVector<ClangBackEnd::DiagnosticContainer> diagnostics; QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
QVector<HighlightingMarkContainer> highlightingMarks; QVector<HighlightingMarkContainer> highlightingMarks;
QVector<SourceRangeContainer> skippedSourceRanges; QVector<SourceRangeContainer> skippedSourceRanges;
@@ -46,7 +47,7 @@ class RequestDocumentAnnotationsJob : public AsyncJob<RequestDocumentAnnotations
public: public:
using AsyncResult = RequestDocumentAnnotationsJobResult; using AsyncResult = RequestDocumentAnnotationsJobResult;
bool prepareAsyncRun() override; AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override; void finalizeAsyncRun() override;
private: private:

View File

@@ -0,0 +1,141 @@
/****************************************************************************
**
** 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 "clangsupportivetranslationunitinitializer.h"
#include "clangjobs.h"
#include "clangtranslationunits.h"
#include <utils/qtcassert.h>
namespace ClangBackEnd {
// TODO: Check translation unit id?
SupportiveTranslationUnitInitializer::SupportiveTranslationUnitInitializer(
const Document &document,
Jobs &jobs)
: m_document(document)
, m_jobs(jobs)
{
}
void SupportiveTranslationUnitInitializer::setJobRequestCreator(const JobRequestCreator &creator)
{
m_jobRequestCreator = creator;
}
void SupportiveTranslationUnitInitializer::setIsDocumentClosedChecker(
const IsDocumentClosedChecker &isDocumentClosedChecker)
{
m_isDocumentClosedChecker = isDocumentClosedChecker;
}
SupportiveTranslationUnitInitializer::State SupportiveTranslationUnitInitializer::state() const
{
return m_state;
}
void SupportiveTranslationUnitInitializer::startInitializing()
{
QTC_CHECK(m_state == State::NotInitialized);
if (abortIfDocumentIsClosed())
return;
m_document.translationUnits().createAndAppend();
m_jobs.setJobFinishedCallback([this](const Jobs::RunningJob &runningJob) {
checkIfParseJobFinished(runningJob);
});
addJob(JobRequest::Type::ParseSupportiveTranslationUnit);
m_jobs.process();
m_state = State::WaitingForParseJob;
}
void SupportiveTranslationUnitInitializer::checkIfParseJobFinished(const Jobs::RunningJob &job)
{
QTC_CHECK(m_state == State::WaitingForParseJob);
if (abortIfDocumentIsClosed())
return;
if (job.jobRequest.type == JobRequest::Type::ParseSupportiveTranslationUnit) {
m_jobs.setJobFinishedCallback([this](const Jobs::RunningJob &runningJob) {
checkIfReparseJobFinished(runningJob);
});
addJob(JobRequest::Type::ReparseSupportiveTranslationUnit);
m_state = State::WaitingForReparseJob;
}
}
void SupportiveTranslationUnitInitializer::checkIfReparseJobFinished(const Jobs::RunningJob &job)
{
QTC_CHECK(m_state == State::WaitingForReparseJob);
if (abortIfDocumentIsClosed())
return;
if (job.jobRequest.type == JobRequest::Type::ReparseSupportiveTranslationUnit) {
if (m_document.translationUnits().areAllTranslationUnitsParsed()) {
m_jobs.setJobFinishedCallback(nullptr);
m_state = State::Initialized;
} else {
// The supportive translation unit was reparsed, but the document
// revision changed in the meanwhile, so try again.
addJob(JobRequest::Type::ReparseSupportiveTranslationUnit);
}
}
}
bool SupportiveTranslationUnitInitializer::abortIfDocumentIsClosed()
{
QTC_CHECK(m_isDocumentClosedChecker);
if (m_isDocumentClosedChecker(m_document.filePath(), m_document.projectPartId())) {
m_state = State::Aborted;
return true;
}
return false;
}
void SupportiveTranslationUnitInitializer::addJob(JobRequest::Type jobRequestType)
{
QTC_CHECK(m_jobRequestCreator);
const JobRequest jobRequest = m_jobRequestCreator(m_document,
jobRequestType,
PreferredTranslationUnit::LastUninitialized);
m_jobs.add(jobRequest);
}
void SupportiveTranslationUnitInitializer::setState(const State &state)
{
m_state = state;
}
} // namespace ClangBackEnd

View File

@@ -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.
**
****************************************************************************/
#include "clangdocument.h"
#include "clangjobrequest.h"
#include "clangjobs.h"
#include <functional>
#pragma once
namespace ClangBackEnd {
class SupportiveTranslationUnitInitializer
{
public:
using IsDocumentClosedChecker = std::function<bool(const Utf8String &, const Utf8String &)>;
enum class State {
NotInitialized,
WaitingForParseJob,
WaitingForReparseJob,
Initialized,
Aborted
};
public:
SupportiveTranslationUnitInitializer(const Document &document, Jobs &jobs);
void setJobRequestCreator(const JobRequestCreator &creator);
void setIsDocumentClosedChecker(const IsDocumentClosedChecker &isDocumentClosedChecker);
State state() const;
void startInitializing();
public: // for tests
void setState(const State &state);
void checkIfParseJobFinished(const Jobs::RunningJob &job);
void checkIfReparseJobFinished(const Jobs::RunningJob &job);
private:
bool abortIfDocumentIsClosed();
void addJob(JobRequest::Type jobRequestType);
private:
Document m_document;
Jobs &m_jobs;
State m_state = State::NotInitialized;
JobRequestCreator m_jobRequestCreator;
IsDocumentClosedChecker m_isDocumentClosedChecker;
};
} // namespace ClangBackEnd

View File

@@ -38,10 +38,12 @@
namespace ClangBackEnd { namespace ClangBackEnd {
TranslationUnit::TranslationUnit(const Utf8String &filepath, TranslationUnit::TranslationUnit(const Utf8String &id,
const Utf8String &filepath,
CXIndex &cxIndex, CXIndex &cxIndex,
CXTranslationUnit &cxTranslationUnit) CXTranslationUnit &cxTranslationUnit)
: m_filePath(filepath) : m_id(id)
, m_filePath(filepath)
, m_cxIndex(cxIndex) , m_cxIndex(cxIndex)
, m_cxTranslationUnit(cxTranslationUnit) , m_cxTranslationUnit(cxTranslationUnit)
{ {
@@ -49,7 +51,12 @@ TranslationUnit::TranslationUnit(const Utf8String &filepath,
bool TranslationUnit::isNull() const bool TranslationUnit::isNull() const
{ {
return !m_cxTranslationUnit || !m_cxIndex || m_filePath.isEmpty(); return !m_cxTranslationUnit || !m_cxIndex || m_filePath.isEmpty() || m_id.isEmpty();
}
Utf8String TranslationUnit::id() const
{
return m_id;
} }
Utf8String TranslationUnit::filePath() const Utf8String TranslationUnit::filePath() const
@@ -70,7 +77,7 @@ CXTranslationUnit &TranslationUnit::cxTranslationUnit() const
TranslationUnitUpdateResult TranslationUnit::update( TranslationUnitUpdateResult TranslationUnit::update(
const TranslationUnitUpdateInput &parseInput) const const TranslationUnitUpdateInput &parseInput) const
{ {
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::AsNeeded); return updater.update(TranslationUnitUpdater::UpdateMode::AsNeeded);
} }
@@ -78,7 +85,7 @@ TranslationUnitUpdateResult TranslationUnit::update(
TranslationUnitUpdateResult TranslationUnit::parse( TranslationUnitUpdateResult TranslationUnit::parse(
const TranslationUnitUpdateInput &parseInput) const const TranslationUnitUpdateInput &parseInput) const
{ {
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::ParseIfNeeded); return updater.update(TranslationUnitUpdater::UpdateMode::ParseIfNeeded);
} }
@@ -86,7 +93,7 @@ TranslationUnitUpdateResult TranslationUnit::parse(
TranslationUnitUpdateResult TranslationUnit::reparse( TranslationUnitUpdateResult TranslationUnit::reparse(
const TranslationUnitUpdateInput &parseInput) const const TranslationUnitUpdateInput &parseInput) const
{ {
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput); TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::ForceReparse); return updater.update(TranslationUnitUpdater::UpdateMode::ForceReparse);
} }
@@ -105,11 +112,12 @@ TranslationUnit::CodeCompletionResult TranslationUnit::complete(
} }
void TranslationUnit::extractDocumentAnnotations( void TranslationUnit::extractDocumentAnnotations(
QVector<DiagnosticContainer> &diagnostics, DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics,
QVector<HighlightingMarkContainer> &highlightingMarks, QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const QVector<SourceRangeContainer> &skippedSourceRanges) const
{ {
diagnostics = mainFileDiagnostics(); extractDiagnostics(firstHeaderErrorDiagnostic, mainFileDiagnostics);
highlightingMarks = this->highlightingMarks().toHighlightingMarksContainers(); highlightingMarks = this->highlightingMarks().toHighlightingMarksContainers();
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers(); skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
} }
@@ -119,15 +127,6 @@ DiagnosticSet TranslationUnit::diagnostics() const
return DiagnosticSet(clang_getDiagnosticSetFromTU(m_cxTranslationUnit)); return DiagnosticSet(clang_getDiagnosticSetFromTU(m_cxTranslationUnit));
} }
QVector<DiagnosticContainer> TranslationUnit::mainFileDiagnostics() const
{
const auto isMainFileDiagnostic = [this](const Diagnostic &diagnostic) {
return diagnostic.location().filePath() == m_filePath;
};
return diagnostics().toDiagnosticContainers(isMainFileDiagnostic);
}
SourceLocation TranslationUnit::sourceLocationAt(uint line,uint column) const SourceLocation TranslationUnit::sourceLocationAt(uint line,uint column) const
{ {
return SourceLocation(m_cxTranslationUnit, m_filePath, line, column); return SourceLocation(m_cxTranslationUnit, m_filePath, line, column);
@@ -186,4 +185,35 @@ SkippedSourceRanges TranslationUnit::skippedSourceRanges() const
return SkippedSourceRanges(m_cxTranslationUnit, m_filePath.constData()); return SkippedSourceRanges(m_cxTranslationUnit, m_filePath.constData());
} }
static bool isMainFileDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic)
{
return diagnostic.location().filePath() == mainFilePath;
}
static bool isHeaderErrorDiagnostic(const Utf8String &mainFilePath, const Diagnostic &diagnostic)
{
const bool isCritical = diagnostic.severity() == DiagnosticSeverity::Error
|| diagnostic.severity() == DiagnosticSeverity::Fatal;
return isCritical && diagnostic.location().filePath() != mainFilePath;
}
void TranslationUnit::extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics) const
{
mainFileDiagnostics.clear();
mainFileDiagnostics.reserve(int(diagnostics().size()));
bool hasFirstHeaderErrorDiagnostic = false;
for (const Diagnostic &diagnostic : diagnostics()) {
if (!hasFirstHeaderErrorDiagnostic && isHeaderErrorDiagnostic(m_filePath, diagnostic)) {
hasFirstHeaderErrorDiagnostic = true;
firstHeaderErrorDiagnostic = diagnostic.toDiagnosticContainer();
}
if (isMainFileDiagnostic(m_filePath, diagnostic))
mainFileDiagnostics.push_back(diagnostic.toDiagnosticContainer());
}
}
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -57,12 +57,15 @@ public:
}; };
public: public:
TranslationUnit(const Utf8String &filePath, TranslationUnit(const Utf8String &id,
const Utf8String &filePath,
CXIndex &cxIndex, CXIndex &cxIndex,
CXTranslationUnit &cxTranslationUnit); CXTranslationUnit &cxTranslationUnit);
bool isNull() const; bool isNull() const;
Utf8String id() const;
Utf8String filePath() const; Utf8String filePath() const;
CXIndex &cxIndex() const; CXIndex &cxIndex() const;
CXTranslationUnit &cxTranslationUnit() const; CXTranslationUnit &cxTranslationUnit() const;
@@ -73,12 +76,14 @@ public:
CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column) const; CodeCompletionResult complete(UnsavedFiles &unsavedFiles, uint line, uint column) const;
void extractDocumentAnnotations(QVector<DiagnosticContainer> &diagnostics, void extractDiagnostics(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics) const;
void extractDocumentAnnotations(DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics,
QVector<HighlightingMarkContainer> &highlightingMarks, QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const; QVector<SourceRangeContainer> &skippedSourceRanges) const;
DiagnosticSet diagnostics() const; DiagnosticSet diagnostics() const;
QVector<DiagnosticContainer> mainFileDiagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const; SourceLocation sourceLocationAt(uint line, uint column) const;
SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const; SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const;
@@ -94,6 +99,7 @@ public:
SkippedSourceRanges skippedSourceRanges() const; SkippedSourceRanges skippedSourceRanges() const;
private: private:
const Utf8String m_id;
const Utf8String m_filePath; const Utf8String m_filePath;
CXIndex &m_cxIndex; CXIndex &m_cxIndex;
CXTranslationUnit &m_cxTranslationUnit; CXTranslationUnit &m_cxTranslationUnit;

View File

@@ -0,0 +1,150 @@
/****************************************************************************
**
** 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 "clangtranslationunits.h"
#include "clangexceptions.h"
#include "clangtranslationunit.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QFileInfo>
#include <QLoggingCategory>
#include <QUuid>
#include <algorithm>
Q_LOGGING_CATEGORY(tuLog, "qtc.clangbackend.translationunits");
namespace ClangBackEnd {
TranslationUnits::TranslationUnits(const Utf8String &filePath)
: m_filePath(filePath)
{
}
TranslationUnits::~TranslationUnits()
{
foreach (const TranslationUnitData &unit, m_tuDatas) {
clang_disposeTranslationUnit(unit.cxTranslationUnit);
clang_disposeIndex(unit.cxIndex);
}
}
TranslationUnit TranslationUnits::createAndAppend()
{
const Utf8String id = Utf8String::fromByteArray(QUuid::createUuid().toByteArray());
qCDebug(tuLog) << "Creating TranslationUnit" << id << "for" << QFileInfo(m_filePath).fileName();
m_tuDatas.append(TranslationUnitData(id));
TranslationUnitData &translationUnitData = m_tuDatas.last();
return toTranslationUnit(translationUnitData);
}
TranslationUnit TranslationUnits::get(PreferredTranslationUnit type)
{
if (m_tuDatas.isEmpty())
throw TranslationUnitDoesNotExist(m_filePath);
if (m_tuDatas.size() == 1)
return toTranslationUnit(m_tuDatas.first());
if (areAllTranslationUnitsParsed())
return getPreferredTranslationUnit(type);
else if (type == PreferredTranslationUnit::LastUninitialized)
return toTranslationUnit(m_tuDatas.last());
return toTranslationUnit(m_tuDatas.first());
}
void TranslationUnits::updateParseTimePoint(const Utf8String &translationUnitId,
TimePoint timePoint)
{
TranslationUnitData &unit = findUnit(translationUnitId);
QTC_CHECK(timePoint != TimePoint());
unit.parseTimePoint = timePoint;
qCDebug(tuLog) << "Updated" << translationUnitId << "for" << QFileInfo(m_filePath).fileName()
<< "RecentlyParsed:" << get(PreferredTranslationUnit::RecentlyParsed).id()
<< "PreviouslyParsed:" << get(PreferredTranslationUnit::PreviouslyParsed).id();
}
TimePoint TranslationUnits::parseTimePoint(const Utf8String &translationUnitId)
{
return findUnit(translationUnitId).parseTimePoint;
}
bool TranslationUnits::areAllTranslationUnitsParsed() const
{
return Utils::allOf(m_tuDatas, [](const TranslationUnitData &unit) {
return unit.parseTimePoint != TimePoint();
});
}
int TranslationUnits::size() const
{
return m_tuDatas.size();
}
TranslationUnit TranslationUnits::getPreferredTranslationUnit(PreferredTranslationUnit type)
{
using TuData = TranslationUnitData;
const auto lessThan = [](const TuData &a, const TuData &b) {
return a.parseTimePoint < b.parseTimePoint;
};
auto translationUnitData = type == PreferredTranslationUnit::RecentlyParsed
? std::max_element(m_tuDatas.begin(), m_tuDatas.end(), lessThan)
: std::min_element(m_tuDatas.begin(), m_tuDatas.end(), lessThan);
if (translationUnitData == m_tuDatas.end())
throw TranslationUnitDoesNotExist(m_filePath);
return toTranslationUnit(*translationUnitData);
}
TranslationUnits::TranslationUnitData &TranslationUnits::findUnit(
const Utf8String &translationUnitId)
{
for (TranslationUnitData &unit : m_tuDatas) {
if (translationUnitId == unit.id)
return unit;
}
throw TranslationUnitDoesNotExist(m_filePath);
}
TranslationUnit TranslationUnits::toTranslationUnit(TranslationUnits::TranslationUnitData &unit)
{
return TranslationUnit(unit.id,
m_filePath,
unit.cxIndex,
unit.cxTranslationUnit);
}
} // namespace ClangBackEnd

View File

@@ -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 "clangbackend_global.h"
#include "clangclock.h"
#include <utf8string.h>
#include <clang-c/Index.h>
#include <QList>
namespace ClangBackEnd {
class TranslationUnit;
class TranslationUnits
{
public:
class TranslationUnitData {
public:
TranslationUnitData(const Utf8String &id)
: id(id)
{}
Utf8String id;
CXTranslationUnit cxTranslationUnit = nullptr;
CXIndex cxIndex = nullptr;
TimePoint parseTimePoint;
};
public:
TranslationUnits(const Utf8String &filePath);
~TranslationUnits();
TranslationUnit createAndAppend();
TranslationUnit get(PreferredTranslationUnit type = PreferredTranslationUnit::RecentlyParsed);
void updateParseTimePoint(const Utf8String &translationUnitId, TimePoint timePoint);
bool areAllTranslationUnitsParsed() const;
public: // for tests
int size() const;
TimePoint parseTimePoint(const Utf8String &translationUnitId);
private:
TranslationUnit getPreferredTranslationUnit(PreferredTranslationUnit type);
TranslationUnitData &findUnit(const Utf8String &translationUnitId);
TranslationUnit toTranslationUnit(TranslationUnitData &unit);
private:
Utf8String m_filePath;
QList<TranslationUnitData> m_tuDatas;
};
} // namespace ClangBackEnd

View File

@@ -40,13 +40,15 @@ static bool isVerboseModeEnabled()
namespace ClangBackEnd { namespace ClangBackEnd {
TranslationUnitUpdater::TranslationUnitUpdater(CXIndex &index, TranslationUnitUpdater::TranslationUnitUpdater(const Utf8String translationUnitId,
CXIndex &index,
CXTranslationUnit &cxTranslationUnit, CXTranslationUnit &cxTranslationUnit,
const TranslationUnitUpdateInput &updateData) const TranslationUnitUpdateInput &updateData)
: m_cxIndex(index) : m_cxIndex(index)
, m_cxTranslationUnit(cxTranslationUnit) , m_cxTranslationUnit(cxTranslationUnit)
, m_in(updateData) , m_in(updateData)
{ {
m_out.translationUnitId = translationUnitId;
} }
TranslationUnitUpdateResult TranslationUnitUpdater::update(UpdateMode mode) TranslationUnitUpdateResult TranslationUnitUpdater::update(UpdateMode mode)
@@ -122,7 +124,7 @@ void TranslationUnitUpdater::createTranslationUnitIfNeeded()
if (parseWasSuccessful()) { if (parseWasSuccessful()) {
updateIncludeFilePaths(); updateIncludeFilePaths();
m_out.parseTimePoint = std::chrono::steady_clock::now(); m_out.parseTimePoint = Clock::now();
} else { } else {
qWarning() << "Parsing" << m_in.filePath << "failed:" qWarning() << "Parsing" << m_in.filePath << "failed:"
<< errorCodeToText(m_parseErrorCode); << errorCodeToText(m_parseErrorCode);
@@ -151,7 +153,7 @@ void TranslationUnitUpdater::reparse()
if (reparseWasSuccessful()) { if (reparseWasSuccessful()) {
updateIncludeFilePaths(); updateIncludeFilePaths();
m_out.reparseTimePoint = std::chrono::steady_clock::now(); m_out.reparseTimePoint = Clock::now();
m_out.needsToBeReparsedChangeTimePoint = m_in.needsToBeReparsedChangeTimePoint; m_out.needsToBeReparsedChangeTimePoint = m_in.needsToBeReparsedChangeTimePoint;
} else { } else {
qWarning() << "Reparsing" << m_in.filePath << "failed:" << m_reparseErrorCode; qWarning() << "Reparsing" << m_in.filePath << "failed:" << m_reparseErrorCode;

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include "clangclock.h"
#include "commandlinearguments.h" #include "commandlinearguments.h"
#include "unsavedfiles.h" #include "unsavedfiles.h"
#include "utf8stringvector.h" #include "utf8stringvector.h"
@@ -33,18 +35,14 @@
#include <QSet> #include <QSet>
#include <chrono>
namespace ClangBackEnd { namespace ClangBackEnd {
using time_point = std::chrono::steady_clock::time_point;
class TranslationUnitUpdateInput { class TranslationUnitUpdateInput {
public: public:
bool parseNeeded = false; bool parseNeeded = false;
bool reparseNeeded = false; bool reparseNeeded = false;
time_point needsToBeReparsedChangeTimePoint; TimePoint needsToBeReparsedChangeTimePoint;
Utf8String filePath; Utf8String filePath;
Utf8StringVector fileArguments; Utf8StringVector fileArguments;
@@ -57,17 +55,18 @@ public:
class TranslationUnitUpdateResult { class TranslationUnitUpdateResult {
public: public:
bool hasParsed() const bool hasParsed() const
{ return parseTimePoint != time_point(); } { return parseTimePoint != TimePoint(); }
bool hasReparsed() const bool hasReparsed() const
{ return reparseTimePoint != time_point(); } { return reparseTimePoint != TimePoint(); }
public: public:
bool hasParseOrReparseFailed = false; Utf8String translationUnitId;
time_point parseTimePoint; bool hasParseOrReparseFailed = false;
time_point reparseTimePoint; TimePoint parseTimePoint;
time_point needsToBeReparsedChangeTimePoint; TimePoint reparseTimePoint;
TimePoint needsToBeReparsedChangeTimePoint;
QSet<Utf8String> dependedOnFilePaths; QSet<Utf8String> dependedOnFilePaths;
}; };
@@ -81,7 +80,8 @@ public:
}; };
public: public:
TranslationUnitUpdater(CXIndex &index, TranslationUnitUpdater(const Utf8String translationUnitId,
CXIndex &index,
CXTranslationUnit &cxTranslationUnit, CXTranslationUnit &cxTranslationUnit,
const TranslationUnitUpdateInput &in); const TranslationUnitUpdateInput &in);

View File

@@ -69,7 +69,7 @@ bool Type::isReferencingConstant() const
return (isPointer() || isLValueReference()) && pointeeType().isConstant(); return (isPointer() || isLValueReference()) && pointeeType().isConstant();
} }
bool Type::isOutputParameter() const bool Type::isOutputArgument() const
{ {
return (isPointer() || isLValueReference()) && !pointeeType().isConstant(); return (isPointer() || isLValueReference()) && !pointeeType().isConstant();
} }

View File

@@ -49,7 +49,7 @@ public:
bool isConstantPointer() const; bool isConstantPointer() const;
bool isLValueReference() const; bool isLValueReference() const;
bool isReferencingConstant() const; bool isReferencingConstant() const;
bool isOutputParameter() const; bool isOutputArgument() const;
Utf8String utf8Spelling() const; Utf8String utf8Spelling() const;
ClangString spelling() const; ClangString spelling() const;

View File

@@ -45,34 +45,36 @@ static UpdateDocumentAnnotationsJob::AsyncResult runAsyncHelper(
asyncResult.updateResult = translationUnit.update(translationUnitUpdatInput); asyncResult.updateResult = translationUnit.update(translationUnitUpdatInput);
// Collect // Collect
translationUnit.extractDocumentAnnotations(asyncResult.diagnostics, translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic,
asyncResult.diagnostics,
asyncResult.highlightingMarks, asyncResult.highlightingMarks,
asyncResult.skippedSourceRanges); asyncResult.skippedSourceRanges);
return asyncResult; return asyncResult;
} }
bool UpdateDocumentAnnotationsJob::prepareAsyncRun() IAsyncJob::AsyncPrepareResult UpdateDocumentAnnotationsJob::prepareAsyncRun()
{ {
const JobRequest jobRequest = context().jobRequest; const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::UpdateDocumentAnnotations, return false); QTC_ASSERT(jobRequest.type == JobRequest::Type::UpdateDocumentAnnotations,
return AsyncPrepareResult());
try { try {
m_pinnedDocument = context().documentForJobRequest(); m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer(); m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit = m_pinnedDocument.translationUnit(); const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput(); const TranslationUnitUpdateInput updateInput = m_pinnedDocument.createUpdateInput();
setRunner([translationUnit, updateInput]() { setRunner([translationUnit, updateInput]() {
return runAsyncHelper(translationUnit, updateInput); return runAsyncHelper(translationUnit, updateInput);
}); });
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) { } catch (const std::exception &exception) {
qWarning() << "Error in UpdateDocumentAnnotationsJob::prepareAsyncRun:" << exception.what(); qWarning() << "Error in UpdateDocumentAnnotationsJob::prepareAsyncRun:" << exception.what();
return false; return AsyncPrepareResult();
} }
return true;
} }
void UpdateDocumentAnnotationsJob::finalizeAsyncRun() void UpdateDocumentAnnotationsJob::finalizeAsyncRun()
@@ -94,6 +96,7 @@ void UpdateDocumentAnnotationsJob::sendAnnotations(const AsyncResult &result)
{ {
const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer, const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer,
result.diagnostics, result.diagnostics,
result.firstHeaderErrorDiagnostic,
result.highlightingMarks, result.highlightingMarks,
result.skippedSourceRanges); result.skippedSourceRanges);

View File

@@ -39,6 +39,7 @@ struct UpdateDocumentAnnotationsJobResult
{ {
TranslationUnitUpdateResult updateResult; TranslationUnitUpdateResult updateResult;
ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic;
QVector<ClangBackEnd::DiagnosticContainer> diagnostics; QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
QVector<HighlightingMarkContainer> highlightingMarks; QVector<HighlightingMarkContainer> highlightingMarks;
QVector<SourceRangeContainer> skippedSourceRanges; QVector<SourceRangeContainer> skippedSourceRanges;
@@ -49,7 +50,7 @@ class UpdateDocumentAnnotationsJob : public AsyncJob<UpdateDocumentAnnotationsJo
public: public:
using AsyncResult = UpdateDocumentAnnotationsJobResult; using AsyncResult = UpdateDocumentAnnotationsJobResult;
bool prepareAsyncRun() override; AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override; void finalizeAsyncRun() override;
private: private:

View File

@@ -213,16 +213,32 @@ SourceLocation Cursor::sourceLocation() const
return clang_getCursorLocation(cxCursor); return clang_getCursorLocation(cxCursor);
} }
CXSourceLocation Cursor::cxSourceLocation() const
{
return clang_getCursorLocation(cxCursor);
}
SourceRange Cursor::sourceRange() const SourceRange Cursor::sourceRange() const
{ {
return clang_getCursorExtent(cxCursor); return clang_getCursorExtent(cxCursor);
} }
CXSourceRange Cursor::cxSourceRange() const
{
return clang_getCursorExtent(cxCursor);
}
SourceRange Cursor::commentRange() const SourceRange Cursor::commentRange() const
{ {
return clang_Cursor_getCommentRange(cxCursor); return clang_Cursor_getCommentRange(cxCursor);
} }
bool Cursor::hasSameSourceLocationAs(const Cursor &other) const
{
return clang_equalLocations(clang_getCursorLocation(cxCursor),
clang_getCursorLocation(other.cxCursor));
}
Cursor Cursor::definition() const Cursor Cursor::definition() const
{ {
return clang_getCursorDefinition(cxCursor); return clang_getCursorDefinition(cxCursor);
@@ -279,32 +295,42 @@ Cursor Cursor::argument(int index) const
{ {
return clang_Cursor_getArgument(cxCursor, index); return clang_Cursor_getArgument(cxCursor, index);
} }
namespace { namespace {
void collectOutputArguments(const Cursor &callExpression,
std::vector<Cursor> &outputArguments) bool isNotUnexposedLValueReference(const Cursor &argument, const Type &argumentType)
{ {
auto callExpressionType = callExpression.referenced().type(); return !(argument.isUnexposed() && argumentType.isLValueReference());
auto argumentCount = callExpression.argumentCount(); }
outputArguments.reserve(argumentCount);
}
void Cursor::collectOutputArgumentRangesTo(std::vector<CXSourceRange> &outputArgumentRanges) const
{
const Type callExpressionType = referenced().type();
const int argumentCount = this->argumentCount();
const std::size_t maxSize = std::size_t(std::max(0, argumentCount))
+ outputArgumentRanges.size();
outputArgumentRanges.reserve(maxSize);
for (int argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex) { for (int argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex) {
auto argument = callExpression.argument(argumentIndex); const Cursor argument = this->argument(argumentIndex);
auto argumentType = callExpressionType.argument(argumentIndex); const Type argumentType = callExpressionType.argument(argumentIndex);
if (!argument.isUnexposed() && argumentType.isOutputParameter()) if (isNotUnexposedLValueReference(argument, argumentType)
outputArguments.push_back(callExpression.argument(argumentIndex)); && argumentType.isOutputArgument()) {
outputArgumentRanges.push_back(argument.cxSourceRange());
}
} }
} }
}
std::vector<Cursor> Cursor::outputArguments() const std::vector<CXSourceRange> Cursor::outputArgumentRanges() const
{ {
std::vector<Cursor> outputArguments; std::vector<CXSourceRange> outputArgumentRanges;
if (kind() == CXCursor_CallExpr) collectOutputArgumentRangesTo(outputArgumentRanges);
collectOutputArguments(*this, outputArguments);
return outputArguments; return outputArgumentRanges;
} }
CXCursorKind Cursor::kind() const CXCursorKind Cursor::kind() const
@@ -317,6 +343,11 @@ bool operator==(const Cursor &first, const Cursor &second)
return clang_equalCursors(first.cxCursor, second.cxCursor); return clang_equalCursors(first.cxCursor, second.cxCursor);
} }
bool operator!=(const Cursor &first, const Cursor &second)
{
return !(first == second);
}
void PrintTo(CXCursorKind cursorKind, ::std::ostream *os) void PrintTo(CXCursorKind cursorKind, ::std::ostream *os)
{ {
ClangString cursorKindSpelling(clang_getCursorKindSpelling(cursorKind)); ClangString cursorKindSpelling(clang_getCursorKindSpelling(cursorKind));

View File

@@ -78,8 +78,11 @@ public:
Type nonPointerTupe() const; Type nonPointerTupe() const;
SourceLocation sourceLocation() const; SourceLocation sourceLocation() const;
CXSourceLocation cxSourceLocation() const;
SourceRange sourceRange() const; SourceRange sourceRange() const;
CXSourceRange cxSourceRange() const;
SourceRange commentRange() const; SourceRange commentRange() const;
bool hasSameSourceLocationAs(const Cursor &other) const;
Cursor definition() const; Cursor definition() const;
Cursor canonical() const; Cursor canonical() const;
@@ -90,7 +93,9 @@ public:
Cursor functionBaseDeclaration() const; Cursor functionBaseDeclaration() const;
Cursor functionBase() const; Cursor functionBase() const;
Cursor argument(int index) const; Cursor argument(int index) const;
std::vector<Cursor> outputArguments() const; void collectOutputArgumentRangesTo(
std::vector<CXSourceRange> &outputArgumentRanges) const;
std::vector<CXSourceRange> outputArgumentRanges() const;
CXCursorKind kind() const; CXCursorKind kind() const;
@@ -114,6 +119,7 @@ void Cursor::visit(VisitorCallback visitorCallback) const
} }
bool operator==(const Cursor &first, const Cursor &second); bool operator==(const Cursor &first, const Cursor &second);
bool operator!=(const Cursor &first, const Cursor &second);
void PrintTo(CXCursorKind cursorKind, ::std::ostream *os); void PrintTo(CXCursorKind cursorKind, ::std::ostream *os);
void PrintTo(const Cursor &cursor, ::std::ostream* os); void PrintTo(const Cursor &cursor, ::std::ostream* os);

View File

@@ -30,6 +30,7 @@
#include "highlightingmark.h" #include "highlightingmark.h"
#include "sourcelocation.h" #include "sourcelocation.h"
#include "sourcerange.h" #include "sourcerange.h"
#include "sourcerangecontainer.h"
#include <cstring> #include <cstring>
#include <ostream> #include <ostream>
@@ -39,16 +40,19 @@
namespace ClangBackEnd { namespace ClangBackEnd {
HighlightingMark::HighlightingMark(const CXCursor &cxCursor, HighlightingMark::HighlightingMark(const CXCursor &cxCursor,
CXToken *cxToken, CXToken *cxToken,
CXTranslationUnit cxTranslationUnit) CXTranslationUnit cxTranslationUnit,
std::vector<CXSourceRange> &currentOutputArgumentRanges)
: currentOutputArgumentRanges(&currentOutputArgumentRanges),
originalCursor(cxCursor)
{ {
const SourceRange sourceRange = clang_getTokenExtent(cxTranslationUnit, *cxToken); const SourceRange sourceRange = clang_getTokenExtent(cxTranslationUnit, *cxToken);
const auto start = sourceRange.start(); const auto start = sourceRange.start();
const auto end = sourceRange.end(); const auto end = sourceRange.end();
originalCursor = cxCursor;
line = start.line(); line = start.line();
column = start.column(); column = start.column();
offset = start.offset();
length = end.offset() - start.offset(); length = end.offset() - start.offset();
collectKinds(cxToken, originalCursor); collectKinds(cxToken, originalCursor);
} }
@@ -159,6 +163,17 @@ void HighlightingMark::variableKind(const Cursor &cursor)
types.mainHighlightingType = HighlightingType::LocalVariable; types.mainHighlightingType = HighlightingType::LocalVariable;
else else
types.mainHighlightingType = HighlightingType::GlobalVariable; types.mainHighlightingType = HighlightingType::GlobalVariable;
if (isOutputArgument())
types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument);
}
void HighlightingMark::fieldKind(const Cursor &)
{
types.mainHighlightingType = HighlightingType::Field;
if (isOutputArgument())
types.mixinHighlightingTypes.push_back(HighlightingType::OutputArgument);
} }
bool HighlightingMark::isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const bool HighlightingMark::isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const
@@ -185,6 +200,68 @@ void HighlightingMark::addExtraTypeIfFirstPass(HighlightingType type,
types.mixinHighlightingTypes.push_back(type); types.mixinHighlightingTypes.push_back(type);
} }
bool HighlightingMark::isArgumentInCurrentOutputArgumentLocations() const
{
auto originalSourceLocation = originalCursor.cxSourceLocation();
const auto isNotSameOutputArgument = [&] (const CXSourceRange &currentSourceRange) {
return !(originalSourceLocation.int_data >= currentSourceRange.begin_int_data
&& originalSourceLocation.int_data <= currentSourceRange.end_int_data);
};
auto partitionPoint = std::partition(currentOutputArgumentRanges->begin(),
currentOutputArgumentRanges->end(),
isNotSameOutputArgument);
bool isOutputArgument = partitionPoint != currentOutputArgumentRanges->end();
if (isOutputArgument)
currentOutputArgumentRanges->erase(partitionPoint, currentOutputArgumentRanges->end());
return isOutputArgument;
}
bool HighlightingMark::isOutputArgument() const
{
if (currentOutputArgumentRanges->empty())
return false;
return isArgumentInCurrentOutputArgumentLocations();
}
void HighlightingMark::collectOutputArguments(const Cursor &cursor)
{
cursor.collectOutputArgumentRangesTo(*currentOutputArgumentRanges);
filterOutPreviousOutputArguments();
}
namespace {
uint getStart(CXSourceRange cxSourceRange)
{
CXSourceLocation startSourceLocation = clang_getRangeStart(cxSourceRange);
uint startOffset;
clang_getFileLocation(startSourceLocation, nullptr, nullptr, nullptr, &startOffset);
return startOffset;
}
}
void HighlightingMark::filterOutPreviousOutputArguments()
{
auto isAfterLocation = [this] (CXSourceRange outputRange) {
return getStart(outputRange) > offset;
};
auto precedingBegin = std::partition(currentOutputArgumentRanges->begin(),
currentOutputArgumentRanges->end(),
isAfterLocation);
currentOutputArgumentRanges->erase(precedingBegin, currentOutputArgumentRanges->end());
}
void HighlightingMark::functionKind(const Cursor &cursor, Recursion recursion) void HighlightingMark::functionKind(const Cursor &cursor, Recursion recursion)
{ {
if (isRealDynamicCall(cursor) || isVirtualMethodDeclarationOrDefinition(cursor)) if (isRealDynamicCall(cursor) || isVirtualMethodDeclarationOrDefinition(cursor))
@@ -204,12 +281,13 @@ void HighlightingMark::identifierKind(const Cursor &cursor, Recursion recursion)
case CXCursor_CallExpr: case CXCursor_CallExpr:
case CXCursor_CXXMethod: functionKind(cursor, recursion); break; case CXCursor_CXXMethod: functionKind(cursor, recursion); break;
case CXCursor_NonTypeTemplateParameter: case CXCursor_NonTypeTemplateParameter:
case CXCursor_ParmDecl: types.mainHighlightingType = HighlightingType::LocalVariable; break; case CXCursor_CompoundStmt: types.mainHighlightingType = HighlightingType::LocalVariable; break;
case CXCursor_ParmDecl:
case CXCursor_VarDecl: variableKind(cursor); break; case CXCursor_VarDecl: variableKind(cursor); break;
case CXCursor_DeclRefExpr: identifierKind(cursor.referenced(), Recursion::RecursivePass); break; case CXCursor_DeclRefExpr: identifierKind(cursor.referenced(), Recursion::RecursivePass); break;
case CXCursor_MemberRefExpr: memberReferenceKind(cursor); break; case CXCursor_MemberRefExpr: memberReferenceKind(cursor); break;
case CXCursor_FieldDecl: case CXCursor_FieldDecl:
case CXCursor_MemberRef: case CXCursor_MemberRef: fieldKind(cursor); break;
case CXCursor_ObjCIvarDecl: case CXCursor_ObjCIvarDecl:
case CXCursor_ObjCPropertyDecl: case CXCursor_ObjCPropertyDecl:
case CXCursor_ObjCClassMethodDecl: case CXCursor_ObjCClassMethodDecl:
@@ -282,15 +360,17 @@ HighlightingType operatorKind(const Cursor &cursor)
return HighlightingType::Invalid; return HighlightingType::Invalid;
} }
HighlightingType punctationKind(const Cursor &cursor) }
HighlightingType HighlightingMark::punctuationKind(const Cursor &cursor)
{ {
switch (cursor.kind()) { switch (cursor.kind()) {
case CXCursor_DeclRefExpr: return operatorKind(cursor); case CXCursor_DeclRefExpr: return operatorKind(cursor);
case CXCursor_CallExpr: collectOutputArguments(cursor);
default: return HighlightingType::Invalid; default: return HighlightingType::Invalid;
} }
} }
}
void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor) void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor)
{ {
auto cxTokenKind = clang_getTokenKind(*cxToken); auto cxTokenKind = clang_getTokenKind(*cxToken);
@@ -299,7 +379,7 @@ void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor)
switch (cxTokenKind) { switch (cxTokenKind) {
case CXToken_Keyword: types.mainHighlightingType = HighlightingType::Keyword; break; case CXToken_Keyword: types.mainHighlightingType = HighlightingType::Keyword; break;
case CXToken_Punctuation: types.mainHighlightingType = punctationKind(cursor); break; case CXToken_Punctuation: types.mainHighlightingType = punctuationKind(cursor); break;
case CXToken_Identifier: identifierKind(cursor, Recursion::FirstPass); break; case CXToken_Identifier: identifierKind(cursor, Recursion::FirstPass); break;
case CXToken_Comment: types.mainHighlightingType = HighlightingType::Comment; break; case CXToken_Comment: types.mainHighlightingType = HighlightingType::Comment; break;
case CXToken_Literal: types.mainHighlightingType = literalKind(cursor); break; case CXToken_Literal: types.mainHighlightingType = literalKind(cursor); break;

View File

@@ -45,7 +45,10 @@ class HighlightingMark
}; };
public: public:
HighlightingMark(const CXCursor &cxCursor, CXToken *cxToken, CXTranslationUnit cxTranslationUnit); HighlightingMark(const CXCursor &cxCursor,
CXToken *cxToken,
CXTranslationUnit cxTranslationUnit,
std::vector<CXSourceRange> &currentOutputArgumentRanges);
HighlightingMark(uint line, uint column, uint length, HighlightingTypes types); HighlightingMark(uint line, uint column, uint length, HighlightingTypes types);
HighlightingMark(uint line, uint column, uint length, HighlightingType type); HighlightingMark(uint line, uint column, uint length, HighlightingType type);
@@ -61,18 +64,26 @@ private:
void identifierKind(const Cursor &cursor, Recursion recursion); void identifierKind(const Cursor &cursor, Recursion recursion);
void referencedTypeKind(const Cursor &cursor); void referencedTypeKind(const Cursor &cursor);
void variableKind(const Cursor &cursor); void variableKind(const Cursor &cursor);
void fieldKind(const Cursor &cursor);
bool isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const; bool isVirtualMethodDeclarationOrDefinition(const Cursor &cursor) const;
void functionKind(const Cursor &cursor, Recursion recursion); void functionKind(const Cursor &cursor, Recursion recursion);
void memberReferenceKind(const Cursor &cursor); void memberReferenceKind(const Cursor &cursor);
HighlightingType punctuationKind(const Cursor &cursor);
void collectKinds(CXToken *cxToken, const Cursor &cursor); void collectKinds(CXToken *cxToken, const Cursor &cursor);
bool isRealDynamicCall(const Cursor &cursor) const; bool isRealDynamicCall(const Cursor &cursor) const;
void addExtraTypeIfFirstPass(HighlightingType type, Recursion recursion); void addExtraTypeIfFirstPass(HighlightingType type, Recursion recursion);
bool isOutputArgument() const;
void collectOutputArguments(const Cursor &cursor);
void filterOutPreviousOutputArguments();
bool isArgumentInCurrentOutputArgumentLocations() const;
private: private:
std::vector<CXSourceRange> *currentOutputArgumentRanges = nullptr;
Cursor originalCursor; Cursor originalCursor;
uint line; uint line;
uint column; uint column;
uint length; uint length;
uint offset = 0;
HighlightingTypes types; HighlightingTypes types;
}; };

View File

@@ -47,12 +47,18 @@ HighlightingMarks::~HighlightingMarks()
HighlightingMarks::const_iterator HighlightingMarks::begin() const HighlightingMarks::const_iterator HighlightingMarks::begin() const
{ {
return const_iterator(cxCursor.cbegin(), cxToken, cxTranslationUnit); return const_iterator(cxCursor.cbegin(),
cxToken,
cxTranslationUnit,
currentOutputArgumentRanges);
} }
HighlightingMarks::const_iterator HighlightingMarks::end() const HighlightingMarks::const_iterator HighlightingMarks::end() const
{ {
return const_iterator(cxCursor.cend(), cxToken + cxTokenCount, cxTranslationUnit); return const_iterator(cxCursor.cend(),
cxToken + cxTokenCount,
cxTranslationUnit,
currentOutputArgumentRanges);
} }
QVector<HighlightingMarkContainer> HighlightingMarks::toHighlightingMarksContainers() const QVector<HighlightingMarkContainer> HighlightingMarks::toHighlightingMarksContainers() const
@@ -67,11 +73,19 @@ QVector<HighlightingMarkContainer> HighlightingMarks::toHighlightingMarksContain
&& !highlightMark.hasMainType(HighlightingType::Comment); && !highlightMark.hasMainType(HighlightingType::Comment);
}; };
std::copy_if(begin(), end(), std::back_inserter(containers), isValidHighlightMark); for (const HighlightingMark &highlightMark : *this) {
if (isValidHighlightMark(highlightMark))
containers.push_back(highlightMark);
}
return containers; return containers;
} }
bool HighlightingMarks::currentOutputArgumentRangesAreEmpty() const
{
return currentOutputArgumentRanges.empty();
}
bool HighlightingMarks::isEmpty() const bool HighlightingMarks::isEmpty() const
{ {
return cxTokenCount == 0; return cxTokenCount == 0;
@@ -89,7 +103,10 @@ uint HighlightingMarks::size() const
HighlightingMark HighlightingMarks::operator[](size_t index) const HighlightingMark HighlightingMarks::operator[](size_t index) const
{ {
return HighlightingMark(cxCursor[index], cxToken + index, cxTranslationUnit); return HighlightingMark(cxCursor[index],
cxToken + index,
cxTranslationUnit,
currentOutputArgumentRanges);
} }
} // namespace ClangBackEnd } // namespace ClangBackEnd

Some files were not shown because too many files have changed in this diff Show More