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
case.
\li \c isPassword is a boolean value that specifies that the line edit
contains a password, which will be masked.
\endlist
\section2 Path Chooser

View File

@@ -63,7 +63,8 @@ Module {
"QT_CREATOR",
'IDE_LIBRARY_BASENAME="' + libDirName + '"',
"QT_NO_CAST_TO_ASCII",
"QT_RESTRICTED_CAST_FROM_ASCII"
"QT_RESTRICTED_CAST_FROM_ASCII",
"QT_DISABLE_DEPRECATED_BEFORE=0x050600",
].concat(testsEnabled ? ["WITH_TESTS"] : [])
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 += 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
unix {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,7 +34,15 @@ QT_END_NAMESPACE
namespace ClangCodeModel {
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 ClangCodeModel

View File

@@ -57,6 +57,8 @@
#include <utils/runextensions.h>
#include <QTextBlock>
#include <QVBoxLayout>
#include <QWidget>
namespace ClangCodeModel {
namespace Internal {
@@ -72,6 +74,11 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
, m_semanticHighlighter(document)
, 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
// editor (widget) related features that are not yet implemented by the clang plugin.
connect(&m_builtinProcessor, &CppTools::BuiltinEditorDocumentProcessor::cppDocumentUpdated,
@@ -82,6 +89,8 @@ ClangEditorDocumentProcessor::ClangEditorDocumentProcessor(
ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
{
m_updateTranslationUnitTimer.stop();
m_parserWatcher.cancel();
m_parserWatcher.waitForFinished();
@@ -93,7 +102,7 @@ ClangEditorDocumentProcessor::~ClangEditorDocumentProcessor()
void ClangEditorDocumentProcessor::run()
{
updateTranslationUnitIfProjectPartExists();
m_updateTranslationUnitTimer.start();
// Run clang parser
disconnect(&m_parserWatcher, &QFutureWatcher<void>::finished,
@@ -160,14 +169,21 @@ void ClangEditorDocumentProcessor::clearProjectPart()
m_projectPart.clear();
}
void ClangEditorDocumentProcessor::updateCodeWarnings(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
uint documentRevision)
void ClangEditorDocumentProcessor::updateCodeWarnings(
const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics,
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic,
uint documentRevision)
{
if (documentRevision == revision()) {
m_diagnosticManager.processNewDiagnostics(diagnostics);
const auto codeWarnings = m_diagnosticManager.takeExtraSelections();
const auto fixitAvailableMarkers = m_diagnosticManager.takeFixItAvailableMarkers();
emit codeWarningsUpdated(revision(), codeWarnings, fixitAvailableMarkers);
const auto creator = creatorForHeaderErrorDiagnosticWidget(firstHeaderErrorDiagnostic);
emit codeWarningsUpdated(revision(),
codeWarnings,
creator,
fixitAvailableMarkers);
}
}
namespace {
@@ -251,6 +267,11 @@ void ClangEditorDocumentProcessor::addDiagnosticToolTipToLayout(uint line,
addToolTipToLayout(diagnostic, target);
}
void ClangEditorDocumentProcessor::editorDocumentTimerRestarted()
{
m_updateTranslationUnitTimer.stop(); // Wait for the next call to run().
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
{
return fileContainerWithArguments(m_projectPart.data());
@@ -321,6 +342,38 @@ void ClangEditorDocumentProcessor::requestDocumentAnnotations(const QString &pro
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)
{
if (projectPart)

View File

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

View File

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

View File

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

View File

@@ -36,12 +36,16 @@
#include <coreplugin/messagemanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cpptools/projectpartbuilder.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectnodes.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <projectexplorer/toolchain.h>
#include <utils/algorithm.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!
}
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()
{
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()
{
if (m_tempDir) // always throw away changes in the tmpdir!
@@ -656,6 +794,7 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
}
QSet<QByteArray> advancedSet;
QMap<QByteArray, QByteArray> valuesMap;
QByteArray documentation;
while (!cache.atEnd()) {
const QByteArray line = trimCMakeCacheLine(cache.readLine());
@@ -679,6 +818,8 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
if (key.endsWith("-ADVANCED") && value == "1") {
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 {
CMakeConfigItem::Type t = fromByteArray(type);
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) {
CMakeConfigItem &item = result[i];
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());

View File

@@ -45,6 +45,7 @@ QT_FORWARD_DECLARE_CLASS(QTemporaryDir);
QT_FORWARD_DECLARE_CLASS(QFileSystemWatcher);
namespace Core { class IDocument; }
namespace CppTools { class ProjectPartBuilder; }
namespace ProjectExplorer {
class FileNode;
@@ -80,6 +81,7 @@ public:
bool persistCMakeState();
void generateProjectTree(CMakeProjectNode *root);
QSet<Core::Id> updateCodeModel(CppTools::ProjectPartBuilder &ppBuilder);
QList<CMakeBuildTarget> buildTargets() const;
CMakeConfig parsedConfiguration() const;
@@ -117,6 +119,10 @@ private:
void processCMakeOutput();
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;
CMakeBuildConfiguration *m_buildConfiguration = nullptr;

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ CMakeConfigItem::CMakeConfigItem() = default;
CMakeConfigItem::CMakeConfigItem(const CMakeConfigItem &other) :
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,

View File

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

View File

@@ -94,102 +94,6 @@ CMakeProject::~CMakeProject()
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()
{
auto cmakeBc = qobject_cast<CMakeBuildConfiguration *>(sender());
@@ -225,39 +129,11 @@ void CMakeProject::updateProjectData()
activeQtVersion = CppTools::ProjectPart::Qt5;
}
const FileName sysroot = SysRootKitInformation::sysRoot(k);
ppBuilder.setQtVersion(activeQtVersion);
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 += 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);
}
const QSet<Core::Id> languages = cmakeBc->updateCodeModel(ppBuilder);
for (const auto &lid : languages)
setProjectLanguage(lid, true);
m_codeModelFuture.cancel();
pinfo.finish();

View File

@@ -120,9 +120,6 @@ private:
QStringList filesGeneratedFrom(const QString &sourceFile) const final;
void updateTargetRunConfigurations(ProjectExplorer::Target *t);
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;

View File

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

View File

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

View File

@@ -34,6 +34,11 @@ class ConfigModel : public QAbstractTableModel
Q_OBJECT
public:
enum Roles {
ItemTypeRole = Qt::UserRole,
ItemValuesRole
};
class DataItem {
public:
enum Type { BOOLEAN, FILE, DIRECTORY, STRING, UNKNOWN};
@@ -43,6 +48,7 @@ public:
bool isAdvanced = false;
QString value;
QString description;
QStringList values;
};
explicit ConfigModel(QObject *parent = nullptr);
@@ -58,7 +64,8 @@ public:
void appendConfiguration(const QString &key,
const QString &value = QString(),
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 flush();
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 <QHBoxLayout>
#include <QVBoxLayout>
#include <QLabel>
#include <QToolButton>
@@ -67,6 +68,20 @@ void InfoBarEntry::setCancelButtonInfo(const QString &_cancelButtonText, CallBac
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)
{
@@ -124,10 +139,13 @@ void InfoBar::clear()
void InfoBar::globallySuppressInfo(Id id)
{
globallySuppressed.insert(id);
QStringList list;
foreach (Id i, globallySuppressed)
list << QLatin1String(i.name());
ICore::settings()->setValue(QLatin1String(C_SUPPRESSED_WARNINGS), list);
writeGloballySuppressedToSettings();
}
void InfoBar::globallyUnsuppressInfo(Id id)
{
globallySuppressed.remove(id);
writeGloballySuppressedToSettings();
}
void InfoBar::initializeGloballySuppressed()
@@ -148,12 +166,17 @@ bool InfoBar::anyGloballySuppressed()
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)
: QObject(parent)
, m_infoBar(0)
, m_boxLayout(0)
, m_boxIndex(0)
{
}
@@ -209,13 +232,43 @@ void InfoBarDisplay::update()
infoWidget->setLineWidth(1);
infoWidget->setAutoFillBackground(true);
QHBoxLayout *hbox = new QHBoxLayout(infoWidget);
QHBoxLayout *hbox = new QHBoxLayout;
hbox->setMargin(2);
auto *vbox = new QVBoxLayout(infoWidget);
vbox->setMargin(0);
vbox->addLayout(hbox);
QLabel *infoWidgetLabel = new QLabel(info.infoText);
infoWidgetLabel->setWordWrap(true);
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()) {
QToolButton *infoWidgetButton = new QToolButton;
infoWidgetButton->setText(info.buttonText);
@@ -229,7 +282,9 @@ void InfoBarDisplay::update()
if (info.globalSuppression == InfoBarEntry::GlobalSuppressionEnabled) {
infoWidgetSuppressButton = new QToolButton;
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);
InfoBar::globallySuppressInfo(id);
});
@@ -245,12 +300,17 @@ void InfoBarDisplay::update()
});
if (info.cancelButtonText.isEmpty()) {
infoWidgetCloseButton->setAutoRaise(true);
infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon());
infoWidgetCloseButton->setToolTip(tr("Close"));
if (info.m_showDefaultCancelButton) {
infoWidgetCloseButton->setAutoRaise(true);
infoWidgetCloseButton->setIcon(Utils::Icons::CLOSE_FOREGROUND.icon());
infoWidgetCloseButton->setToolTip(tr("Close"));
}
if (infoWidgetSuppressButton)
hbox->addWidget(infoWidgetSuppressButton);
hbox->addWidget(infoWidgetCloseButton);
if (info.m_showDefaultCancelButton)
hbox->addWidget(infoWidgetCloseButton);
} else {
infoWidgetCloseButton->setText(info.cancelButtonText);
hbox->addWidget(infoWidgetCloseButton);

View File

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

View File

@@ -44,6 +44,7 @@
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/infobar.h>
#include <cpptools/cppchecksymbols.h>
#include <cpptools/cppcodeformatter.h>
@@ -77,6 +78,7 @@
#include <cplusplus/FastPreprocessor.h>
#include <cplusplus/MatchingText.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
#include <QApplication>
#include <QAction>
@@ -126,9 +128,13 @@ public:
QSharedPointer<FunctionDeclDefLink> m_declDefLink;
QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
QToolButton *m_preprocessorButton;
QToolButton *m_preprocessorButton = nullptr;
QToolButton *m_headerErrorsIndicatorButton = nullptr;
CppSelectionChanger m_cppSelectionChanger;
CppEditorWidget::HeaderErrorDiagnosticWidgetCreator m_headerErrorDiagnosticWidgetCreator;
};
CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
@@ -139,7 +145,6 @@ CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q)
, m_useSelectionsUpdater(q)
, m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q))
, m_followSymbolUnderCursor(new FollowSymbolUnderCursor(q))
, m_preprocessorButton(0)
, m_cppSelectionChanger()
{
}
@@ -224,7 +229,15 @@ void CppEditorWidget::finalizeInitialization()
connect(cmd, &Command::keySequenceChanged, this, &CppEditorWidget::updatePreprocessorButtonTooltip);
updatePreprocessorButtonTooltip();
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_headerErrorsIndicatorButton);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
}
@@ -287,6 +300,7 @@ void CppEditorWidget::onCppDocumentUpdated()
void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers)
{
if (revision != documentRevision())
@@ -294,6 +308,9 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision,
setExtraSelections(TextEditorWidget::CodeWarningsSelection, selections);
setRefactorMarkers(refactorMarkersWithoutClangMarkers() + refactorMarkers);
d->m_headerErrorDiagnosticWidgetCreator = creator;
updateHeaderErrorWidgets();
}
void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
@@ -304,6 +321,24 @@ void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision,
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()
{
if (!d->m_modelManager)
@@ -399,6 +434,25 @@ void CppEditorWidget::renameSymbolUnderCursorBuiltin()
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 {
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 CppEditor

View File

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

View File

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

View File

@@ -59,9 +59,13 @@ public:
const QByteArray &defines);
void scheduleProcessDocument();
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
signals:
void codeWarningsUpdated(unsigned contentsRevision,
const QList<QTextEdit::ExtraSelection> selections,
const HeaderErrorDiagnosticWidgetCreator &creator,
const TextEditor::RefactorMarkers &refactorMarkers);
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,
BaseEditorDocumentParser::Ptr parser,
const WorkingCopy workingCopy)

View File

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

View File

@@ -310,7 +310,10 @@ void BuiltinEditorDocumentProcessor::onCodeWarningsUpdated(
m_codeWarnings += toTextEditorSelections(codeWarnings, textDocument());
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

View File

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

View File

@@ -169,7 +169,7 @@ static QPoint flipPoint(const NSPoint &p)
NSURL *resolvedURL = data.resolvedUrl.toNSURL();
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
MIMEType:mimeType
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.google.com/search?q=%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");
}

View File

@@ -404,6 +404,7 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage)
QVariantMap tmp = data.toMap();
m_isPassword = tmp.value("isPassword", false).toBool();
m_defaultText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trText")).toString());
m_disabledText = JsonWizardFactory::localizedString(tmp.value(QLatin1String("trDisabledText")).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())
w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem);
w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal);
return w;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -313,6 +313,10 @@ TextEditorSettings::TextEditorSettings(QObject *parent)
tr("Declaration"),
tr("Declaration of a function, variable, and so on."),
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,
Constants::TEXT_EDITOR_FONT_SETTINGS,

View File

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

View File

@@ -64,6 +64,11 @@ public:
return future;
}
void preventFinalization() override
{
m_futureWatcher.disconnect();
}
private:
Runner m_runner;
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/utf8positionfromlinecolumn.h \
$$PWD/clangasyncjob.h \
$$PWD/clangbackend_global.h \
$$PWD/clangcompletecodejob.h \
$$PWD/clangcreateinitialdocumentpreamblejob.h \
$$PWD/clangfilepath.h \
@@ -41,7 +42,14 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/clangtranslationunit.h \
$$PWD/clangunsavedfilesshallowarguments.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 \
$$PWD/codecompleter.cpp \
@@ -80,4 +88,10 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/clangtranslationunit.cpp \
$$PWD/clangunsavedfilesshallowarguments.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 "clangfilesystemwatcher.h"
#include "clangtranslationunits.h"
#include "codecompleter.h"
#include "diagnosticset.h"
#include "highlightingmarks.h"
@@ -126,10 +127,16 @@ void ClangCodeModelServer::updateTranslationUnitsForEditor(const UpdateTranslati
try {
const auto newerFileContainers = documents.newerFileContainers(message.fileContainers());
if (newerFileContainers.size() > 0) {
documents.update(newerFileContainers);
const std::vector<Document> updateDocuments = documents.update(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) {
qWarning() << "Error in ClangCodeModelServer::updateTranslationUnitsForEditor:" << exception.what();
@@ -141,6 +148,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn
TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor");
try {
for (const auto &fileContainer : message.fileContainers()) {
const Document &document = documents.document(fileContainer);
documentProcessors().remove(document);
}
documents.remove(message.fileContainers());
unsavedFiles.remove(message.fileContainers());
} catch (const std::exception &exception) {
@@ -211,8 +222,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber();
jobs().add(jobRequest);
jobs().process();
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what();
}
@@ -229,8 +241,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
const JobRequest jobRequest = createJobRequest(document,
JobRequest::Type::RequestDocumentAnnotations);
jobs().add(jobRequest);
jobs().process();
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what();
}
@@ -260,9 +273,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen
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
@@ -270,32 +288,55 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const
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()
{
addJobRequestsForDirtyAndVisibleDocuments();
jobs().process();
for (const auto &document : documents.documents()) {
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)
{
for (const auto &document : documents) {
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble));
}
DocumentProcessor processor = documentProcessors().create(document);
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,
JobRequest::Type type) const
void ClangCodeModelServer::startInitializingSupportiveTranslationUnits(
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.type = type;
@@ -304,6 +345,7 @@ JobRequest ClangCodeModelServer::createJobRequest(const Document &document,
jobRequest.projectPartId = document.projectPartId();
jobRequest.unsavedFilesChangeTimePoint = unsavedFiles.lastChangeTimePoint();
jobRequest.documentRevision = document.documentRevision();
jobRequest.preferredTranslationUnit = preferredTranslationUnit;
const ProjectPart &projectPart = projects.project(document.projectPartId());
jobRequest.projectChangeTimePoint = projectPart.lastChangeTimePoint();
@@ -315,16 +357,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i
updateDocumentAnnotationsTimeOutInMs = value;
}
Jobs &ClangCodeModelServer::jobs()
DocumentProcessors &ClangCodeModelServer::documentProcessors()
{
if (!jobs_) {
// Jobs needs a reference to the client, but the client is not known at
// construction time of ClangCodeModelServer, so construct Jobs in a
// lazy manner.
jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client()));
if (!documentProcessors_) {
// DocumentProcessors needs a reference to the client, but the client
// is not known at construction time of ClangCodeModelServer, so
// construct DocumentProcessors in a lazy manner.
documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client()));
}
return *jobs_.data();
return *documentProcessors_.data();
}
} // namespace ClangBackEnd

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,6 +27,7 @@
#include "clangtranslationunitupdater.h"
#include "clangbackend_global.h"
#include "clangtranslationunit.h"
#include <utf8stringvector.h>
@@ -36,7 +37,6 @@
#include <QSet>
#include <QtGlobal>
#include <chrono>
#include <memory>
class Utf8String;
@@ -44,14 +44,13 @@ class Utf8String;
namespace ClangBackEnd {
class TranslationUnit;
class TranslationUnits;
class DocumentData;
class TranslationUnitUpdateResult;
class ProjectPart;
class FileContainer;
class Documents;
using time_point = std::chrono::steady_clock::time_point;
class Document
{
public:
@@ -85,7 +84,7 @@ public:
Utf8String projectPartId() const;
const ProjectPart &projectPart() const;
const time_point lastProjectPartChangeTimePoint() const;
const TimePoint lastProjectPartChangeTimePoint() const;
bool isProjectPartOutdated() const;
uint documentRevision() const;
@@ -104,7 +103,9 @@ public:
TranslationUnitUpdateInput createUpdateInput() const;
void incorporateUpdaterResult(const TranslationUnitUpdateResult &result) const;
TranslationUnit translationUnit() const;
TranslationUnit translationUnit(PreferredTranslationUnit preferredTranslationUnit
= PreferredTranslationUnit::RecentlyParsed) const;
TranslationUnits &translationUnits() const;
public: // for tests
void parse() const;
@@ -112,7 +113,7 @@ public: // for tests
const QSet<Utf8String> dependedFilePaths() const;
TranslationUnitUpdater createUpdater() const;
void setHasParseOrReparseFailed(bool hasFailed);
time_point isNeededReparseChangeTimePoint() const;
TimePoint isNeededReparseChangeTimePoint() const;
private:
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;
}
void Documents::update(const QVector<FileContainer> &fileContainers)
std::vector<Document> Documents::update(const QVector<FileContainer> &fileContainers)
{
checkIfDocumentsForFilePathsExist(fileContainers);
std::vector<Document> createdDocuments;
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());
}
return createdDocuments;
}
static bool removeFromFileContainer(QVector<FileContainer> &fileContainers, const Document &document)
@@ -205,12 +211,14 @@ Document Documents::createDocument(const FileContainer &fileContainer)
return documents_.back();
}
void Documents::updateDocument(const FileContainer &fileContainer)
std::vector<Document> Documents::updateDocument(const FileContainer &fileContainer)
{
const auto documents = findAllDocumentsWithFilePath(fileContainer.filePath());
for (auto document : documents)
document.setDocumentRevision(fileContainer.documentRevision());
return documents;
}
std::vector<Document>::iterator Documents::findDocument(const FileContainer &fileContainer)

View File

@@ -45,7 +45,7 @@ public:
Documents(ProjectParts &projectParts, UnsavedFiles &unsavedFiles);
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 setUsedByCurrentEditor(const Utf8String &filePath);
@@ -72,7 +72,7 @@ public:
private:
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> findAllDocumentsWithFilePath(const Utf8String &filePath);
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!");
}
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

View File

@@ -74,4 +74,24 @@ public:
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

View File

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

View File

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

View File

@@ -27,6 +27,7 @@
#include "clangjobqueue.h"
#include "clangdocument.h"
#include "clangdocuments.h"
#include "clangtranslationunits.h"
#include "projects.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);
return true;
}
int JobQueue::size() const
@@ -164,50 +176,65 @@ void JobQueue::prioritizeRequests()
JobRequests JobQueue::takeJobRequestsToRunNow()
{
JobRequests jobsToRun;
QSet<DocumentId> documentsScheduledForThisRun;
using TranslationUnitIds = QSet<Utf8String>;
TranslationUnitIds translationUnitsScheduledForThisRun;
QMutableVectorIterator<JobRequest> i(m_queue);
while (i.hasNext()) {
const JobRequest &jobRequest = i.next();
const JobRequest &request = i.next();
try {
const Document &document
= m_documents.document(jobRequest.filePath,
jobRequest.projectPartId);
const DocumentId documentId = DocumentId(jobRequest.filePath, jobRequest.projectPartId);
const Document &document = m_documents.document(request.filePath,
request.projectPartId);
if (!document.isUsedByCurrentEditor() && !document.isVisibleInEditor())
continue;
if (documentsScheduledForThisRun.contains(documentId))
const Utf8String id = document.translationUnit(request.preferredTranslationUnit).id();
if (translationUnitsScheduledForThisRun.contains(id))
continue;
if (isJobRunningForDocument(documentId))
if (isJobRunningForTranslationUnit(id))
continue;
documentsScheduledForThisRun.insert(documentId);
jobsToRun += jobRequest;
translationUnitsScheduledForThisRun.insert(id);
jobsToRun += request;
i.remove();
} catch (const std::exception &exception) {
qWarning() << "Error in Jobs::takeJobRequestsToRunNow for"
<< jobRequest << ":" << exception.what();
<< request << ":" << exception.what();
}
}
return jobsToRun;
}
bool JobQueue::isJobRunningForDocument(const JobQueue::DocumentId &documentId)
bool JobQueue::isJobRunningForTranslationUnit(const Utf8String &translationUnitId)
{
if (m_isJobRunningHandler)
return m_isJobRunningHandler(documentId.first, documentId.second);
if (m_isJobRunningForTranslationUnitHandler)
return m_isJobRunningForTranslationUnitHandler(translationUnitId);
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

View File

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

View File

@@ -25,6 +25,8 @@
#include "clangjobrequest.h"
#include <QFileInfo>
namespace ClangBackEnd {
#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) {
RETURN_TEXT_FOR_CASE(UpdateDocumentAnnotations);
RETURN_TEXT_FOR_CASE(ParseSupportiveTranslationUnit);
RETURN_TEXT_FOR_CASE(ReparseSupportiveTranslationUnit);
RETURN_TEXT_FOR_CASE(CreateInitialDocumentPreamble);
RETURN_TEXT_FOR_CASE(CompleteCode);
RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations);
@@ -41,6 +45,19 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
}
#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)
{
debug << JobRequestTypeToText(type);
@@ -53,12 +70,14 @@ QDebug operator<<(QDebug debug, const JobRequest &jobRequest)
debug.nospace() << "Job<"
<< jobRequest.id
<< ","
<< QFileInfo(jobRequest.filePath).fileName()
<< ","
<< JobRequestTypeToText(jobRequest.type)
<< ","
<< jobRequest.filePath
<< preferredTranslationUnitToText(jobRequest.preferredTranslationUnit)
<< ">";
return debug;
return debug.space();
}
JobRequest::JobRequest()
@@ -67,6 +86,23 @@ JobRequest::JobRequest()
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)
{
switch (type) {
@@ -77,6 +113,8 @@ JobRequest::Requirements JobRequest::requirementsForType(Type type)
|JobRequest::CurrentDocumentRevision);
case JobRequest::Type::CompleteCode:
case JobRequest::Type::CreateInitialDocumentPreamble:
case JobRequest::Type::ParseSupportiveTranslationUnit:
case JobRequest::Type::ReparseSupportiveTranslationUnit:
return JobRequest::Requirements(JobRequest::DocumentValid);
}

View File

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

View File

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

View File

@@ -31,6 +31,8 @@
#include <QFuture>
#include <functional>
namespace ClangBackEnd {
class ClangCodeModelClientInterface;
@@ -43,9 +45,12 @@ class Jobs
public:
struct RunningJob {
JobRequest jobRequest;
Utf8String translationUnitId;
QFuture<void> future;
};
using RunningJobs = QHash<IAsyncJob *, RunningJob>;
using JobFinishedCallback = std::function<void(RunningJob)>;
public:
Jobs(Documents &documents,
@@ -58,10 +63,13 @@ public:
JobRequests process();
void setJobFinishedCallback(const JobFinishedCallback &jobFinishedCallback);
public /*for tests*/:
int runningJobs() const;
QList<RunningJob> runningJobs() 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:
JobRequests runJobs(const JobRequests &jobRequest);
@@ -75,6 +83,7 @@ private:
JobQueue m_queue;
RunningJobs m_running;
JobFinishedCallback m_jobFinishedCallback;
};
} // 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;
translationUnit.extractDocumentAnnotations(asyncResult.diagnostics,
translationUnit.extractDocumentAnnotations(asyncResult.firstHeaderErrorDiagnostic,
asyncResult.diagnostics,
asyncResult.highlightingMarks,
asyncResult.skippedSourceRanges);
return asyncResult;
}
bool RequestDocumentAnnotationsJob::prepareAsyncRun()
IAsyncJob::AsyncPrepareResult RequestDocumentAnnotationsJob::prepareAsyncRun()
{
const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations, return false);
QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestDocumentAnnotations,
return AsyncPrepareResult());
try {
m_pinnedDocument = context().documentForJobRequest();
m_pinnedFileContainer = m_pinnedDocument.fileContainer();
const TranslationUnit translationUnit = m_pinnedDocument.translationUnit();
const TranslationUnit translationUnit
= m_pinnedDocument.translationUnit(jobRequest.preferredTranslationUnit);
setRunner([translationUnit]() {
return runAsyncHelper(translationUnit);
});
return AsyncPrepareResult{translationUnit.id()};
} catch (const std::exception &exception) {
qWarning() << "Error in RequestDocumentAnnotationsJob::prepareAsyncRun:" << exception.what();
return false;
return AsyncPrepareResult();
}
return true;
}
void RequestDocumentAnnotationsJob::finalizeAsyncRun()
@@ -82,6 +84,7 @@ void RequestDocumentAnnotationsJob::sendAnnotations(
{
const DocumentAnnotationsChangedMessage message(m_pinnedFileContainer,
result.diagnostics,
result.firstHeaderErrorDiagnostic,
result.highlightingMarks,
result.skippedSourceRanges);

View File

@@ -36,6 +36,7 @@ namespace ClangBackEnd {
struct RequestDocumentAnnotationsJobResult
{
ClangBackEnd::DiagnosticContainer firstHeaderErrorDiagnostic;
QVector<ClangBackEnd::DiagnosticContainer> diagnostics;
QVector<HighlightingMarkContainer> highlightingMarks;
QVector<SourceRangeContainer> skippedSourceRanges;
@@ -46,7 +47,7 @@ class RequestDocumentAnnotationsJob : public AsyncJob<RequestDocumentAnnotations
public:
using AsyncResult = RequestDocumentAnnotationsJobResult;
bool prepareAsyncRun() override;
AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override;
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 {
TranslationUnit::TranslationUnit(const Utf8String &filepath,
TranslationUnit::TranslationUnit(const Utf8String &id,
const Utf8String &filepath,
CXIndex &cxIndex,
CXTranslationUnit &cxTranslationUnit)
: m_filePath(filepath)
: m_id(id)
, m_filePath(filepath)
, m_cxIndex(cxIndex)
, m_cxTranslationUnit(cxTranslationUnit)
{
@@ -49,7 +51,12 @@ TranslationUnit::TranslationUnit(const Utf8String &filepath,
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
@@ -70,7 +77,7 @@ CXTranslationUnit &TranslationUnit::cxTranslationUnit() const
TranslationUnitUpdateResult TranslationUnit::update(
const TranslationUnitUpdateInput &parseInput) const
{
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput);
TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::AsNeeded);
}
@@ -78,7 +85,7 @@ TranslationUnitUpdateResult TranslationUnit::update(
TranslationUnitUpdateResult TranslationUnit::parse(
const TranslationUnitUpdateInput &parseInput) const
{
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput);
TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::ParseIfNeeded);
}
@@ -86,7 +93,7 @@ TranslationUnitUpdateResult TranslationUnit::parse(
TranslationUnitUpdateResult TranslationUnit::reparse(
const TranslationUnitUpdateInput &parseInput) const
{
TranslationUnitUpdater updater(cxIndex(), cxTranslationUnit(), parseInput);
TranslationUnitUpdater updater(id(), cxIndex(), cxTranslationUnit(), parseInput);
return updater.update(TranslationUnitUpdater::UpdateMode::ForceReparse);
}
@@ -105,11 +112,12 @@ TranslationUnit::CodeCompletionResult TranslationUnit::complete(
}
void TranslationUnit::extractDocumentAnnotations(
QVector<DiagnosticContainer> &diagnostics,
DiagnosticContainer &firstHeaderErrorDiagnostic,
QVector<DiagnosticContainer> &mainFileDiagnostics,
QVector<HighlightingMarkContainer> &highlightingMarks,
QVector<SourceRangeContainer> &skippedSourceRanges) const
{
diagnostics = mainFileDiagnostics();
extractDiagnostics(firstHeaderErrorDiagnostic, mainFileDiagnostics);
highlightingMarks = this->highlightingMarks().toHighlightingMarksContainers();
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
}
@@ -119,15 +127,6 @@ DiagnosticSet TranslationUnit::diagnostics() const
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
{
return SourceLocation(m_cxTranslationUnit, m_filePath, line, column);
@@ -186,4 +185,35 @@ SkippedSourceRanges TranslationUnit::skippedSourceRanges() const
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

View File

@@ -57,12 +57,15 @@ public:
};
public:
TranslationUnit(const Utf8String &filePath,
TranslationUnit(const Utf8String &id,
const Utf8String &filePath,
CXIndex &cxIndex,
CXTranslationUnit &cxTranslationUnit);
bool isNull() const;
Utf8String id() const;
Utf8String filePath() const;
CXIndex &cxIndex() const;
CXTranslationUnit &cxTranslationUnit() const;
@@ -73,12 +76,14 @@ public:
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<SourceRangeContainer> &skippedSourceRanges) const;
DiagnosticSet diagnostics() const;
QVector<DiagnosticContainer> mainFileDiagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const;
SourceLocation sourceLocationAt(const Utf8String &filePath, uint line, uint column) const;
@@ -94,6 +99,7 @@ public:
SkippedSourceRanges skippedSourceRanges() const;
private:
const Utf8String m_id;
const Utf8String m_filePath;
CXIndex &m_cxIndex;
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 {
TranslationUnitUpdater::TranslationUnitUpdater(CXIndex &index,
TranslationUnitUpdater::TranslationUnitUpdater(const Utf8String translationUnitId,
CXIndex &index,
CXTranslationUnit &cxTranslationUnit,
const TranslationUnitUpdateInput &updateData)
: m_cxIndex(index)
, m_cxTranslationUnit(cxTranslationUnit)
, m_in(updateData)
{
m_out.translationUnitId = translationUnitId;
}
TranslationUnitUpdateResult TranslationUnitUpdater::update(UpdateMode mode)
@@ -122,7 +124,7 @@ void TranslationUnitUpdater::createTranslationUnitIfNeeded()
if (parseWasSuccessful()) {
updateIncludeFilePaths();
m_out.parseTimePoint = std::chrono::steady_clock::now();
m_out.parseTimePoint = Clock::now();
} else {
qWarning() << "Parsing" << m_in.filePath << "failed:"
<< errorCodeToText(m_parseErrorCode);
@@ -151,7 +153,7 @@ void TranslationUnitUpdater::reparse()
if (reparseWasSuccessful()) {
updateIncludeFilePaths();
m_out.reparseTimePoint = std::chrono::steady_clock::now();
m_out.reparseTimePoint = Clock::now();
m_out.needsToBeReparsedChangeTimePoint = m_in.needsToBeReparsedChangeTimePoint;
} else {
qWarning() << "Reparsing" << m_in.filePath << "failed:" << m_reparseErrorCode;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -213,16 +213,32 @@ SourceLocation Cursor::sourceLocation() const
return clang_getCursorLocation(cxCursor);
}
CXSourceLocation Cursor::cxSourceLocation() const
{
return clang_getCursorLocation(cxCursor);
}
SourceRange Cursor::sourceRange() const
{
return clang_getCursorExtent(cxCursor);
}
CXSourceRange Cursor::cxSourceRange() const
{
return clang_getCursorExtent(cxCursor);
}
SourceRange Cursor::commentRange() const
{
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
{
return clang_getCursorDefinition(cxCursor);
@@ -279,32 +295,42 @@ Cursor Cursor::argument(int index) const
{
return clang_Cursor_getArgument(cxCursor, index);
}
namespace {
void collectOutputArguments(const Cursor &callExpression,
std::vector<Cursor> &outputArguments)
bool isNotUnexposedLValueReference(const Cursor &argument, const Type &argumentType)
{
auto callExpressionType = callExpression.referenced().type();
auto argumentCount = callExpression.argumentCount();
outputArguments.reserve(argumentCount);
return !(argument.isUnexposed() && argumentType.isLValueReference());
}
}
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) {
auto argument = callExpression.argument(argumentIndex);
auto argumentType = callExpressionType.argument(argumentIndex);
const Cursor argument = this->argument(argumentIndex);
const Type argumentType = callExpressionType.argument(argumentIndex);
if (!argument.isUnexposed() && argumentType.isOutputParameter())
outputArguments.push_back(callExpression.argument(argumentIndex));
if (isNotUnexposedLValueReference(argument, argumentType)
&& 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)
collectOutputArguments(*this, outputArguments);
collectOutputArgumentRangesTo(outputArgumentRanges);
return outputArguments;
return outputArgumentRanges;
}
CXCursorKind Cursor::kind() const
@@ -317,6 +343,11 @@ bool operator==(const Cursor &first, const Cursor &second)
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)
{
ClangString cursorKindSpelling(clang_getCursorKindSpelling(cursorKind));

View File

@@ -78,8 +78,11 @@ public:
Type nonPointerTupe() const;
SourceLocation sourceLocation() const;
CXSourceLocation cxSourceLocation() const;
SourceRange sourceRange() const;
CXSourceRange cxSourceRange() const;
SourceRange commentRange() const;
bool hasSameSourceLocationAs(const Cursor &other) const;
Cursor definition() const;
Cursor canonical() const;
@@ -90,7 +93,9 @@ public:
Cursor functionBaseDeclaration() const;
Cursor functionBase() 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;
@@ -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);
void PrintTo(CXCursorKind cursorKind, ::std::ostream *os);
void PrintTo(const Cursor &cursor, ::std::ostream* os);

View File

@@ -30,6 +30,7 @@
#include "highlightingmark.h"
#include "sourcelocation.h"
#include "sourcerange.h"
#include "sourcerangecontainer.h"
#include <cstring>
#include <ostream>
@@ -39,16 +40,19 @@
namespace ClangBackEnd {
HighlightingMark::HighlightingMark(const CXCursor &cxCursor,
CXToken *cxToken,
CXTranslationUnit cxTranslationUnit)
CXToken *cxToken,
CXTranslationUnit cxTranslationUnit,
std::vector<CXSourceRange> &currentOutputArgumentRanges)
: currentOutputArgumentRanges(&currentOutputArgumentRanges),
originalCursor(cxCursor)
{
const SourceRange sourceRange = clang_getTokenExtent(cxTranslationUnit, *cxToken);
const auto start = sourceRange.start();
const auto end = sourceRange.end();
originalCursor = cxCursor;
line = start.line();
column = start.column();
offset = start.offset();
length = end.offset() - start.offset();
collectKinds(cxToken, originalCursor);
}
@@ -159,6 +163,17 @@ void HighlightingMark::variableKind(const Cursor &cursor)
types.mainHighlightingType = HighlightingType::LocalVariable;
else
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
@@ -185,6 +200,68 @@ void HighlightingMark::addExtraTypeIfFirstPass(HighlightingType 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)
{
if (isRealDynamicCall(cursor) || isVirtualMethodDeclarationOrDefinition(cursor))
@@ -204,12 +281,13 @@ void HighlightingMark::identifierKind(const Cursor &cursor, Recursion recursion)
case CXCursor_CallExpr:
case CXCursor_CXXMethod: functionKind(cursor, recursion); break;
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_DeclRefExpr: identifierKind(cursor.referenced(), Recursion::RecursivePass); break;
case CXCursor_MemberRefExpr: memberReferenceKind(cursor); break;
case CXCursor_FieldDecl:
case CXCursor_MemberRef:
case CXCursor_MemberRef: fieldKind(cursor); break;
case CXCursor_ObjCIvarDecl:
case CXCursor_ObjCPropertyDecl:
case CXCursor_ObjCClassMethodDecl:
@@ -282,15 +360,17 @@ HighlightingType operatorKind(const Cursor &cursor)
return HighlightingType::Invalid;
}
HighlightingType punctationKind(const Cursor &cursor)
}
HighlightingType HighlightingMark::punctuationKind(const Cursor &cursor)
{
switch (cursor.kind()) {
case CXCursor_DeclRefExpr: return operatorKind(cursor);
case CXCursor_CallExpr: collectOutputArguments(cursor);
default: return HighlightingType::Invalid;
}
}
}
void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor)
{
auto cxTokenKind = clang_getTokenKind(*cxToken);
@@ -299,7 +379,7 @@ void HighlightingMark::collectKinds(CXToken *cxToken, const Cursor &cursor)
switch (cxTokenKind) {
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_Comment: types.mainHighlightingType = HighlightingType::Comment; break;
case CXToken_Literal: types.mainHighlightingType = literalKind(cursor); break;

View File

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

View File

@@ -47,12 +47,18 @@ HighlightingMarks::~HighlightingMarks()
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
{
return const_iterator(cxCursor.cend(), cxToken + cxTokenCount, cxTranslationUnit);
return const_iterator(cxCursor.cend(),
cxToken + cxTokenCount,
cxTranslationUnit,
currentOutputArgumentRanges);
}
QVector<HighlightingMarkContainer> HighlightingMarks::toHighlightingMarksContainers() const
@@ -67,11 +73,19 @@ QVector<HighlightingMarkContainer> HighlightingMarks::toHighlightingMarksContain
&& !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;
}
bool HighlightingMarks::currentOutputArgumentRangesAreEmpty() const
{
return currentOutputArgumentRanges.empty();
}
bool HighlightingMarks::isEmpty() const
{
return cxTokenCount == 0;
@@ -89,7 +103,10 @@ uint HighlightingMarks::size() 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

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