CppEditor: Rework showing/switching parse configurations

* Move the combox box for switching the parse configurations out of the
  "Additional Preprocessor Directives" dialog ('#'-button) to make it
  better visible/accessible. Also, decouple the extra preprocessor
  directives from the concrete parse context since this is not anymore
  in same dialog.
* The combo box appears only if multiple parse configurations are
  available for a file.
* The first time multiple parse configurations are detected, an info bar
  is shown that points the user to the combox box. A "Do Not Show Again"
  button is provided.
* Upon selecting an entry, the preferred parse configuration is saved as
  part of the session. The setting can be cleared with the context menu
  entry on the combo box.

Follow-up changes need to ensure that the display name and/or tooltip is
unambiguous, e.g. for qbs and cmake projects.

Change-Id: I9e9773704187291524ad7b605bfdddd83ef5b19d
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Nikolai Kosjar
2017-01-18 15:05:46 +01:00
parent 9ac96339ff
commit 38ba61ec4f
18 changed files with 538 additions and 208 deletions

View File

@@ -280,6 +280,13 @@ void ClangEditorDocumentProcessor::editorDocumentTimerRestarted()
m_updateTranslationUnitTimer.stop(); // Wait for the next call to run().
}
void ClangEditorDocumentProcessor::setParserConfig(
const CppTools::BaseEditorDocumentParser::Configuration config)
{
m_parser->setConfiguration(config);
m_builtinProcessor.parser()->setConfiguration(config);
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
{
return fileContainerWithArguments(m_projectPart.data());

View File

@@ -82,6 +82,8 @@ public:
void editorDocumentTimerRestarted() override;
void setParserConfig(const CppTools::BaseEditorDocumentParser::Configuration config) override;
ClangBackEnd::FileContainer fileContainerWithArguments() const;
void clearDiagnosticsWithFixIts();

View File

@@ -131,6 +131,8 @@ public:
QScopedPointer<FollowSymbolUnderCursor> m_followSymbolUnderCursor;
QAction *m_parseContextAction = nullptr;
ParseContextWidget *m_parseContextWidget = nullptr;
QToolButton *m_preprocessorButton = nullptr;
MinimizableInfoBars::Actions m_showInfoBarActions;
@@ -222,7 +224,7 @@ void CppEditorWidget::finalizeInitialization()
d->m_cppSelectionChanger.onCursorPositionChanged(textCursor());
});
// Tool bar creation
// Toolbar: '#' Button
d->m_preprocessorButton = new QToolButton(this);
d->m_preprocessorButton->setText(QLatin1String("#"));
Command *cmd = ActionManager::command(Constants::OPEN_PREPROCESSOR_DIALOG);
@@ -231,13 +233,25 @@ void CppEditorWidget::finalizeInitialization()
connect(d->m_preprocessorButton, &QAbstractButton::clicked, this, &CppEditorWidget::showPreProcessorWidget);
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_preprocessorButton);
// Actions to show minimized info bars
// Toolbar: Actions to show minimized info bars
d->m_showInfoBarActions = MinimizableInfoBars::createShowInfoBarActions([this](QWidget *w) {
return this->insertExtraToolBarWidget(TextEditorWidget::Left, w);
});
connect(&cppEditorDocument()->minimizableInfoBars(), &MinimizableInfoBars::showAction,
this, &CppEditorWidget::onShowInfoBarAction);
// Toolbar: Parse context
ParseContextModel &parseContextModel = cppEditorDocument()->parseContextModel();
d->m_parseContextWidget = new ParseContextWidget(parseContextModel, this);
d->m_parseContextAction = insertExtraToolBarWidget(TextEditorWidget::Left,
d->m_parseContextWidget);
d->m_parseContextAction->setVisible(false);
connect(&parseContextModel, &ParseContextModel::updated,
this, [this](bool areMultipleAvailable) {
d->m_parseContextAction->setVisible(areMultipleAvailable);
});
// Toolbar: Outline/Overview combo box
insertExtraToolBarWidget(TextEditorWidget::Left, d->m_cppEditorOutline->widget());
}
@@ -252,6 +266,10 @@ void CppEditorWidget::finalizeInitializationAfterDuplication(TextEditorWidget *o
d->m_cppEditorOutline->update();
const Id selectionKind = CodeWarningsSelection;
setExtraSelections(selectionKind, cppEditorWidget->extraSelections(selectionKind));
d->m_parseContextWidget->syncToModel();
d->m_parseContextAction->setVisible(
d->m_cppEditorDocument->parseContextModel().areMultipleAvailable());
}
CppEditorWidget::~CppEditorWidget()
@@ -972,20 +990,12 @@ void CppEditorWidget::abortDeclDefLink()
void CppEditorWidget::showPreProcessorWidget()
{
const Utils::FileName fileName = textDocument()->filePath();
const QString filePath = textDocument()->filePath().toString();
// Check if this editor belongs to a project
QList<ProjectPart::Ptr> projectParts = d->m_modelManager->projectPart(fileName);
if (projectParts.isEmpty())
projectParts = d->m_modelManager->projectPartFromDependencies(fileName);
if (projectParts.isEmpty())
projectParts << d->m_modelManager->fallbackProjectPart();
CppPreProcessorDialog preProcessorDialog(this, textDocument()->filePath().toString(), projectParts);
if (preProcessorDialog.exec() == QDialog::Accepted) {
cppEditorDocument()->setPreprocessorSettings(
preProcessorDialog.projectPart(),
preProcessorDialog.additionalPreProcessorDirectives().toUtf8());
CppPreProcessorDialog dialog(filePath, this);
if (dialog.exec() == QDialog::Accepted) {
const QByteArray extraDirectives = dialog.extraPreprocessorDirectives().toUtf8();
cppEditorDocument()->setExtraPreprocessorDirectives(extraDirectives);
cppEditorDocument()->scheduleProcessDocument();
}
}

View File

@@ -17,6 +17,7 @@ HEADERS += \
cppfunctiondecldeflink.h \
cpphighlighter.h \
cpphoverhandler.h \
cppparsecontext.h \
cppincludehierarchy.h \
cppinsertvirtualmethods.h \
cpplocalrenaming.h \
@@ -46,6 +47,7 @@ SOURCES += \
cppfunctiondecldeflink.cpp \
cpphighlighter.cpp \
cpphoverhandler.cpp \
cppparsecontext.cpp \
cppincludehierarchy.cpp \
cppinsertvirtualmethods.cpp \
cpplocalrenaming.cpp \

View File

@@ -43,6 +43,7 @@ QtcPlugin {
"cppminimizableinfobars.cpp", "cppminimizableinfobars.h",
"cppoutline.cpp", "cppoutline.h",
"cpppreprocessordialog.cpp", "cpppreprocessordialog.h", "cpppreprocessordialog.ui",
"cppparsecontext.cpp", "cppparsecontext.h",
"cppquickfix.cpp", "cppquickfix.h",
"cppquickfixassistant.cpp", "cppquickfixassistant.h",
"cppquickfixes.cpp", "cppquickfixes.h",

View File

@@ -37,6 +37,7 @@ 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 MULTIPLE_PARSE_CONTEXTS_AVAILABLE[] = "CppEditor.MultipleParseContextsAvailable";
const char NO_PROJECT_CONFIGURATION[] = "CppEditor.NoProjectConfiguration";
const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup";
const char UPDATE_CODEMODEL[] = "CppEditor.UpdateCodeModel";
@@ -50,7 +51,8 @@ const char OPEN_INCLUDE_HIERARCHY[] = "CppEditor.OpenIncludeHierarchy";
const char CPP_SNIPPETS_GROUP_ID[] = "C++";
const char CPP_PREPROCESSOR_PROJECT_PREFIX[] = "CppPreprocessorProject-";
const char EXTRA_PREPROCESSOR_DIRECTIVES[] = "CppEditor.ExtraPreprocessorDirectives-";
const char PREFERRED_PARSE_CONTEXT[] = "CppEditor.PreferredParseContext-";
} // namespace Constants
} // namespace CppEditor

View File

@@ -30,6 +30,8 @@
#include "cpphighlighter.h"
#include "cppquickfixassistant.h"
#include <coreplugin/infobar.h>
#include <cpptools/baseeditordocumentparser.h>
#include <cpptools/builtineditordocumentprocessor.h>
#include <cpptools/cppcodeformatter.h>
@@ -115,6 +117,9 @@ CppEditorDocument::CppEditorDocument()
connect(this, &IDocument::filePathChanged,
this, &CppEditorDocument::onFilePathChanged);
connect(&m_parseContextModel, &ParseContextModel::preferredParseContextChanged,
this, &CppEditorDocument::reparseWithPreferredParseContext);
// See also onFilePathChanged() for more initialization
}
@@ -204,6 +209,21 @@ void CppEditorDocument::onReloadFinished()
m_fileIsBeingReloaded = false;
}
void CppEditorDocument::reparseWithPreferredParseContext(const QString &parseContextId)
{
using namespace CppTools;
// Update parser
setPreferredParseContext(parseContextId);
// Remember the setting
const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString();
ProjectExplorer::SessionManager::setValue(key, parseContextId);
// Reprocess
scheduleProcessDocument();
}
void CppEditorDocument::onFilePathChanged(const Utils::FileName &oldPath,
const Utils::FileName &newPath)
{
@@ -222,7 +242,8 @@ void CppEditorDocument::onFilePathChanged(const Utils::FileName &oldPath,
m_editorDocumentHandle.reset(new CppEditorDocumentHandleImpl(this));
resetProcessor();
updatePreprocessorSettings();
applyPreferredParseContextFromSettings();
applyExtraPreprocessorDirectivesFromSettings();
m_processorRevision = document()->revision();
processDocument();
}
@@ -256,34 +277,51 @@ void CppEditorDocument::resetProcessor()
processor(); // creates a new processor
}
void CppEditorDocument::updatePreprocessorSettings()
void CppEditorDocument::applyPreferredParseContextFromSettings()
{
if (filePath().isEmpty())
return;
const QString prefix = QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX);
const QString &projectPartId = ProjectExplorer::SessionManager::value(
prefix + filePath().toString()).toString();
const QString directivesKey = projectPartId + QLatin1Char(',') + filePath().toString();
const QByteArray additionalDirectives = ProjectExplorer::SessionManager::value(
directivesKey).toString().toUtf8();
const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString();
const QString parseContextId = ProjectExplorer::SessionManager::value(key).toString();
setPreprocessorSettings(mm()->projectPartForId(projectPartId), additionalDirectives);
setPreferredParseContext(parseContextId);
}
void CppEditorDocument::setPreprocessorSettings(const CppTools::ProjectPart::Ptr &projectPart,
const QByteArray &defines)
void CppEditorDocument::applyExtraPreprocessorDirectivesFromSettings()
{
if (filePath().isEmpty())
return;
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + filePath().toString();
const QByteArray directives = ProjectExplorer::SessionManager::value(key).toString().toUtf8();
setExtraPreprocessorDirectives(directives);
}
void CppEditorDocument::setExtraPreprocessorDirectives(const QByteArray &directives)
{
const auto parser = processor()->parser();
QTC_ASSERT(parser, return);
if (parser->projectPartInfo().projectPart != projectPart
|| parser->configuration().editorDefines != defines) {
CppTools::BaseEditorDocumentParser::Configuration config = parser->configuration();
config.preferredProjectPartId = projectPart->id();
config.editorDefines = defines;
parser->setConfiguration(config);
emit preprocessorSettingsChanged(!defines.trimmed().isEmpty());
CppTools::BaseEditorDocumentParser::Configuration config = parser->configuration();
if (config.editorDefines != directives) {
config.editorDefines = directives;
processor()->setParserConfig(config);
emit preprocessorSettingsChanged(!directives.trimmed().isEmpty());
}
}
void CppEditorDocument::setPreferredParseContext(const QString &parseContextId)
{
const CppTools::BaseEditorDocumentParser::Ptr parser = processor()->parser();
QTC_ASSERT(parser, return);
CppTools::BaseEditorDocumentParser::Configuration config = parser->configuration();
if (config.preferredProjectPartId != parseContextId) {
config.preferredProjectPartId = parseContextId;
processor()->setParserConfig(config);
}
}
@@ -299,6 +337,23 @@ void CppEditorDocument::releaseResources()
m_processor.reset();
}
void CppEditorDocument::showHideInfoBarAboutMultipleParseContexts(bool show)
{
const Core::Id id = Constants::MULTIPLE_PARSE_CONTEXTS_AVAILABLE;
if (show) {
Core::InfoBarEntry info(id,
tr("Note: Multiple parse contexts are available for this file. "
"Choose the preferred one from the editor toolbar."),
Core::InfoBarEntry::GlobalSuppressionEnabled);
info.removeCancelButton();
if (infoBar()->canInfoBeAdded(id))
infoBar()->addInfo(info);
} else {
infoBar()->removeInfo(id);
}
}
void CppEditorDocument::initializeTimer()
{
m_processorTimer.setSingleShot(true);
@@ -311,6 +366,11 @@ void CppEditorDocument::initializeTimer()
Qt::UniqueConnection);
}
ParseContextModel &CppEditorDocument::parseContextModel()
{
return m_parseContextModel;
}
const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const
{
return m_minimizableInfoBars;
@@ -321,11 +381,15 @@ CppTools::BaseEditorDocumentProcessor *CppEditorDocument::processor()
if (!m_processor) {
m_processor.reset(mm()->editorDocumentProcessor(this));
connect(m_processor.data(), &CppTools::BaseEditorDocumentProcessor::projectPartInfoUpdated,
[this] (const CppTools::ProjectPartInfo &projectPartInfo)
[this] (const CppTools::ProjectPartInfo &info)
{
const bool hasProjectPart
= projectPartInfo.hint != CppTools::ProjectPartInfo::IsFallbackMatch;
using namespace CppTools;
const bool hasProjectPart = !(info.hints & ProjectPartInfo::IsFallbackMatch);
m_minimizableInfoBars.processHasProjectPart(hasProjectPart);
m_parseContextModel.update(info);
const bool isAmbiguous = info.hints & ProjectPartInfo::IsAmbiguousMatch;
const bool isProjectFile = info.hints & ProjectPartInfo::IsFromProjectMatch;
showHideInfoBarAboutMultipleParseContexts(isAmbiguous && isProjectFile);
});
connect(m_processor.data(), &CppTools::BaseEditorDocumentProcessor::codeWarningsUpdated,
[this] (unsigned revision,

View File

@@ -26,6 +26,7 @@
#pragma once
#include "cppminimizableinfobars.h"
#include "cppparsecontext.h"
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/cppcompletionassistprovider.h>
@@ -57,11 +58,13 @@ public:
void recalculateSemanticInfoDetached();
CppTools::SemanticInfo recalculateSemanticInfo(); // TODO: Remove me
void setPreprocessorSettings(const CppTools::ProjectPart::Ptr &projectPart,
const QByteArray &defines);
void setPreferredParseContext(const QString &parseContextId);
void setExtraPreprocessorDirectives(const QByteArray &directives);
void scheduleProcessDocument();
const MinimizableInfoBars &minimizableInfoBars() const;
ParseContextModel &parseContextModel();
signals:
void codeWarningsUpdated(unsigned contentsRevision,
@@ -87,6 +90,8 @@ private:
void onAboutToReload();
void onReloadFinished();
void reparseWithPreferredParseContext(const QString &id);
void processDocument();
QByteArray contentsText() const;
@@ -94,9 +99,12 @@ private:
CppTools::BaseEditorDocumentProcessor *processor();
void resetProcessor();
void updatePreprocessorSettings();
void applyPreferredParseContextFromSettings();
void applyExtraPreprocessorDirectivesFromSettings();
void releaseResources();
void showHideInfoBarAboutMultipleParseContexts(bool show);
void initializeTimer();
private:
@@ -118,6 +126,7 @@ private:
QScopedPointer<CppTools::CppEditorDocumentHandle> m_editorDocumentHandle;
MinimizableInfoBars m_minimizableInfoBars;
ParseContextModel m_parseContextModel;
};
} // namespace Internal

View File

@@ -0,0 +1,172 @@
/****************************************************************************
**
** Copyright (C) 2017 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 "cppparsecontext.h"
#include <QAction>
#include <QDir>
#include <QDebug>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
namespace CppEditor {
namespace Internal {
void ParseContextModel::update(const CppTools::ProjectPartInfo &projectPartInfo)
{
beginResetModel();
reset(projectPartInfo);
endResetModel();
emit updated(areMultipleAvailable());
}
QString ParseContextModel::currentToolTip() const
{
const QModelIndex index = createIndex(m_currentIndex, 0);
if (!index.isValid())
return QString();
return tr("<p><b>Active Parse Context</b>:<br/>%1</p>"
"<p>Multiple parse contexts (set of defines, include paths, and so on) "
"are available for this file.</p>"
"<p>Choose a parse context to set it as the preferred one. "
"Clear the preference from the context menu.</p>")
.arg(data(index, Qt::ToolTipRole).toString());
}
void ParseContextModel::setPreferred(int index)
{
if (index < 0)
return;
emit preferredParseContextChanged(m_projectParts[index]->id());
}
void ParseContextModel::clearPreferred()
{
emit preferredParseContextChanged(QString());
}
bool ParseContextModel::areMultipleAvailable() const
{
return m_projectParts.size() >= 2;
}
void ParseContextModel::reset(const CppTools::ProjectPartInfo &projectPartInfo)
{
// Sort
m_hints = projectPartInfo.hints;
m_projectParts = projectPartInfo.projectParts;
Utils::sort(m_projectParts, &CppTools::ProjectPart::displayName);
// Determine index for current
const QString id = projectPartInfo.projectPart->id();
m_currentIndex = Utils::indexOf(m_projectParts, [id](const CppTools::ProjectPart::Ptr &pp) {
return pp->id() == id;
});
QTC_CHECK(m_currentIndex >= 0);
}
int ParseContextModel::currentIndex() const
{
return m_currentIndex;
}
bool ParseContextModel::isCurrentPreferred() const
{
return m_hints & CppTools::ProjectPartInfo::IsPreferredMatch;
}
QString ParseContextModel::currentId() const
{
if (m_currentIndex < 0)
return QString();
return m_projectParts[m_currentIndex]->id();
}
int ParseContextModel::rowCount(const QModelIndex &) const
{
return m_projectParts.size();
}
QVariant ParseContextModel::data(const QModelIndex &index, int role) const
{
if (m_projectParts.isEmpty())
return QVariant();
const int row = index.row();
if (role == Qt::DisplayRole)
return m_projectParts[row]->displayName;
else if (role == Qt::ToolTipRole)
return QDir::toNativeSeparators(m_projectParts[row]->projectFile);
return QVariant();
}
ParseContextWidget::ParseContextWidget(ParseContextModel &parseContextModel, QWidget *parent)
: QComboBox(parent)
, m_parseContextModel(parseContextModel)
{
// Set up context menu with a clear action
setContextMenuPolicy(Qt::ActionsContextMenu);
m_clearPreferredAction = new QAction(tr("Clear Preferred Parse Context"), this);
connect(m_clearPreferredAction, &QAction::triggered,[&]() {
m_parseContextModel.clearPreferred();
});
addAction(m_clearPreferredAction);
// Set up sync of this widget and model in both directions
connect(this,
static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
&m_parseContextModel,
&ParseContextModel::setPreferred);
connect(&m_parseContextModel, &ParseContextModel::updated,
this, &ParseContextWidget::syncToModel);
// Set up model
setModel(&m_parseContextModel);
}
void ParseContextWidget::syncToModel()
{
const int index = m_parseContextModel.currentIndex();
if (index < 0)
return; // E.g. editor was duplicated but no project context was determined yet.
if (currentIndex() != index)
setCurrentIndex(index);
setToolTip(m_parseContextModel.currentToolTip());
const bool isPreferred = m_parseContextModel.isCurrentPreferred();
m_clearPreferredAction->setEnabled(isPreferred);
QComboBox::setProperty("highlightWidget", isPreferred);
}
} // namespace Internal
} // namespace CppEditor

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2017 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 <cpptools/cpptools_utils.h>
#include <QAbstractListModel>
#include <QComboBox>
namespace CppEditor {
namespace Internal {
class ParseContextModel : public QAbstractListModel
{
Q_OBJECT
public:
void update(const CppTools::ProjectPartInfo &projectPartInfo);
void setPreferred(int index);
void clearPreferred();
int currentIndex() const;
bool isCurrentPreferred() const;
QString currentId() const;
QString currentToolTip() const;
bool areMultipleAvailable() const;
signals:
void updated(bool areMultipleAvailable);
void preferredParseContextChanged(const QString &id);
private:
void reset(const CppTools::ProjectPartInfo &projectPartInfo);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
private:
CppTools::ProjectPartInfo::Hints m_hints;
QList<CppTools::ProjectPart::Ptr> m_projectParts;
int m_currentIndex = -1;
};
class ParseContextWidget : public QComboBox
{
Q_OBJECT
public:
ParseContextWidget(ParseContextModel &parseContextModel, QWidget *parent);
void syncToModel();
private:
ParseContextModel &m_parseContextModel;
QAction *m_clearPreferredAction = nullptr;
};
} // namespace Internal
} // namespace CppEditor

View File

@@ -31,18 +31,9 @@
#include <projectexplorer/session.h>
#include <algorithm>
using namespace CppEditor::Internal;
static bool projectPartLessThan(const CppTools::ProjectPart::Ptr &projectPart1,
const CppTools::ProjectPart::Ptr &projectPart2)
{
return projectPart1->displayName < projectPart2->displayName;
}
CppPreProcessorDialog::CppPreProcessorDialog(QWidget *parent, const QString &filePath,
const QList<CppTools::ProjectPart::Ptr> &projectParts)
CppPreProcessorDialog::CppPreProcessorDialog(const QString &filePath, QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::CppPreProcessorDialog())
, m_filePath(filePath)
@@ -55,32 +46,9 @@ CppPreProcessorDialog::CppPreProcessorDialog(QWidget *parent, const QString &fil
CppSnippetProvider().decorateEditor(m_ui->editWidget);
const QString &projectPartIdToUse = ProjectExplorer::SessionManager::value(
QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX) + m_filePath).toString();
int currentIndex = 0;
QList<CppTools::ProjectPart::Ptr> sortedProjectParts(projectParts);
std::stable_sort(sortedProjectParts.begin(), sortedProjectParts.end(), projectPartLessThan);
foreach (CppTools::ProjectPart::Ptr projectPart, sortedProjectParts) {
m_ui->projectComboBox->addItem(projectPart->displayName);
ProjectPartAddition addition;
addition.projectPart = projectPart;
addition.additionalDirectives = ProjectExplorer::SessionManager::value(
projectPart->id() + QLatin1Char(',') + m_filePath).toString();
if (projectPart->id() == projectPartIdToUse)
currentIndex = m_ui->projectComboBox->count() - 1;
m_partAdditions << addition;
}
if (m_ui->projectComboBox->count() <= 1)
m_ui->projectComboBox->setEnabled(false);
m_ui->projectComboBox->setCurrentIndex(currentIndex);
m_ui->editWidget->setPlainText(m_partAdditions.value(currentIndex).additionalDirectives);
connect(m_ui->projectComboBox,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &CppPreProcessorDialog::projectChanged);
connect(m_ui->editWidget, &QPlainTextEdit::textChanged, this, &CppPreProcessorDialog::textChanged);
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath;
const QString directives = ProjectExplorer::SessionManager::value(key).toString();
m_ui->editWidget->setPlainText(directives);
}
CppPreProcessorDialog::~CppPreProcessorDialog()
@@ -93,41 +61,13 @@ int CppPreProcessorDialog::exec()
if (QDialog::exec() == Rejected)
return Rejected;
ProjectExplorer::SessionManager::setValue(
QLatin1String(Constants::CPP_PREPROCESSOR_PROJECT_PREFIX) + m_filePath,
m_partAdditions[m_ui->projectComboBox->currentIndex()].projectPart->id());
const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath;
ProjectExplorer::SessionManager::setValue(key, extraPreprocessorDirectives());
foreach (ProjectPartAddition partAddition, m_partAdditions) {
const QString &previousDirectives = ProjectExplorer::SessionManager::value(
partAddition.projectPart->id()
+ QLatin1Char(',')
+ m_filePath).toString();
if (previousDirectives != partAddition.additionalDirectives) {
ProjectExplorer::SessionManager::setValue(
partAddition.projectPart->id() + QLatin1Char(',') + m_filePath,
partAddition.additionalDirectives);
}
}
return Accepted;
}
CppTools::ProjectPart::Ptr CppPreProcessorDialog::projectPart() const
{
return m_partAdditions[m_ui->projectComboBox->currentIndex()].projectPart;
}
QString CppPreProcessorDialog::additionalPreProcessorDirectives() const
QString CppPreProcessorDialog::extraPreprocessorDirectives() const
{
return m_ui->editWidget->toPlainText();
}
void CppPreProcessorDialog::projectChanged(int index)
{
m_ui->editWidget->setPlainText(m_partAdditions[index].additionalDirectives);
}
void CppPreProcessorDialog::textChanged()
{
m_partAdditions[m_ui->projectComboBox->currentIndex()].additionalDirectives
= m_ui->editWidget->toPlainText();
}

View File

@@ -25,9 +25,8 @@
#pragma once
#include <cpptools/cppmodelmanager.h>
#include <QDialog>
#include <QString>
namespace CppEditor {
namespace Internal {
@@ -38,27 +37,17 @@ class CppPreProcessorDialog : public QDialog
Q_OBJECT
public:
explicit CppPreProcessorDialog(QWidget *parent, const QString &filePath,
const QList<CppTools::ProjectPart::Ptr> &projectParts);
explicit CppPreProcessorDialog(const QString &filePath, QWidget *parent);
~CppPreProcessorDialog();
int exec();
CppTools::ProjectPart::Ptr projectPart() const;
QString additionalPreProcessorDirectives() const;
QString extraPreprocessorDirectives() const;
private:
void projectChanged(int index);
void textChanged();
struct ProjectPartAddition {
CppTools::ProjectPart::Ptr projectPart;
QString additionalDirectives;
};
Ui::CppPreProcessorDialog *m_ui;
QList<ProjectPartAddition> m_partAdditions;
QString m_filePath;
const QString m_filePath;
const QString m_projectPartId;
};
} // namespace Internal

View File

@@ -14,34 +14,6 @@
<string>Additional C++ Preprocessor Directives</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="projectLabel">
<property name="text">
<string>Project:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="projectComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="separator">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="editorLabel">
<property name="text">

View File

@@ -87,6 +87,12 @@ void BaseEditorDocumentProcessor::editorDocumentTimerRestarted()
{
}
void BaseEditorDocumentProcessor::setParserConfig(
const BaseEditorDocumentParser::Configuration config)
{
parser()->setConfiguration(config);
}
void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams)

View File

@@ -70,6 +70,8 @@ public:
virtual void editorDocumentTimerRestarted();
virtual void setParserConfig(const BaseEditorDocumentParser::Configuration config);
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;

View File

@@ -37,7 +37,7 @@ public:
struct PrioritizedProjectPart
{
PrioritizedProjectPart(const ProjectPart::Ptr &projectPart, int priority)
: projectPart(projectPart) , priority(priority) {}
: projectPart(projectPart), priority(priority) {}
ProjectPart::Ptr projectPart;
int priority = 0;
@@ -46,38 +46,42 @@ public:
ProjectPartPrioritizer(const QList<ProjectPart::Ptr> &projectParts,
const QString &preferredProjectPartId,
const ProjectExplorer::Project *activeProject,
Language languagePreference)
: m_projectParts(projectParts)
, m_preferredProjectPartId(preferredProjectPartId)
Language languagePreference,
bool areProjectPartsFromDependencies)
: m_preferredProjectPartId(preferredProjectPartId)
, m_activeProject(activeProject)
, m_languagePreference(languagePreference)
{
// Determine best project part
const QList<PrioritizedProjectPart> prioritized = prioritize();
m_projectPart = prioritized.first().projectPart;
// Prioritize
const QList<PrioritizedProjectPart> prioritized = prioritize(projectParts);
for (const PrioritizedProjectPart &ppp : prioritized)
m_info.projectParts << ppp.projectPart;
// Determine ambiguity
m_isAmbiguous = prioritized.size() > 1
? prioritized[0].priority == prioritized[1].priority
: false;
// Best project part
m_info.projectPart = m_info.projectParts.first();
// Hints
if (m_info.projectParts.size() > 1)
m_info.hints |= ProjectPartInfo::IsAmbiguousMatch;
if (prioritized.first().priority > 1000)
m_info.hints |= ProjectPartInfo::IsPreferredMatch;
if (areProjectPartsFromDependencies)
m_info.hints |= ProjectPartInfo::IsFromDependenciesMatch;
else
m_info.hints |= ProjectPartInfo::IsFromProjectMatch;
}
ProjectPart::Ptr projectPart()
ProjectPartInfo info() const
{
return m_projectPart;
}
bool isAmbiguous() const
{
return m_isAmbiguous;
return m_info;
}
private:
QList<PrioritizedProjectPart> prioritize()
QList<PrioritizedProjectPart> prioritize(const QList<ProjectPart::Ptr> &projectParts)
{
// Prioritize
QList<PrioritizedProjectPart> prioritized = Utils::transform(m_projectParts,
[&](const ProjectPart::Ptr &projectPart) {
QList<PrioritizedProjectPart> prioritized = Utils::transform(projectParts,
[&](const ProjectPart::Ptr &projectPart) {
return PrioritizedProjectPart{projectPart, priority(*projectPart)};
});
@@ -118,14 +122,12 @@ private:
}
private:
const QList<ProjectPart::Ptr> m_projectParts;
const QString m_preferredProjectPartId;
const ProjectExplorer::Project *m_activeProject = nullptr;
Language m_languagePreference = Language::Cxx;
// Results
ProjectPart::Ptr m_projectPart;
bool m_isAmbiguous = false;
ProjectPartInfo m_info;
};
ProjectPartInfo ProjectPartChooser::choose(
@@ -141,40 +143,30 @@ ProjectPartInfo ProjectPartChooser::choose(
QTC_CHECK(m_fallbackProjectPart);
ProjectPart::Ptr projectPart = currentProjectPartInfo.projectPart;
ProjectPartInfo::Hint hint = ProjectPartInfo::NoHint;
QList<ProjectPart::Ptr> projectParts = m_projectPartsForFile(filePath);
bool areProjectPartsFromDependencies = false;
if (projectParts.isEmpty()) {
if (!projectsUpdated && projectPart
&& currentProjectPartInfo.hint == ProjectPartInfo::IsFallbackMatch)
&& currentProjectPartInfo.hints & ProjectPartInfo::IsFallbackMatch)
// Avoid re-calculating the expensive dependency table for non-project files.
return {projectPart, ProjectPartInfo::IsFallbackMatch};
return ProjectPartInfo(projectPart, {projectPart}, ProjectPartInfo::IsFallbackMatch);
// Fall-back step 1: Get some parts through the dependency table:
projectParts = m_projectPartsFromDependenciesForFile(filePath);
if (projectParts.isEmpty()) {
// Fall-back step 2: Use fall-back part from the model manager:
projectPart = m_fallbackProjectPart();
hint = ProjectPartInfo::IsFallbackMatch;
} else {
ProjectPartPrioritizer prioritizer(projectParts,
preferredProjectPartId,
activeProject,
languagePreference);
projectPart = prioritizer.projectPart();
return ProjectPartInfo(projectPart, {projectPart}, ProjectPartInfo::IsFallbackMatch);
}
} else {
ProjectPartPrioritizer prioritizer(projectParts,
preferredProjectPartId,
activeProject,
languagePreference);
projectPart = prioritizer.projectPart();
hint = prioritizer.isAmbiguous()
? ProjectPartInfo::IsAmbiguousMatch
: ProjectPartInfo::NoHint;
areProjectPartsFromDependencies = true;
}
return {projectPart, hint};
return ProjectPartPrioritizer(projectParts,
preferredProjectPartId,
activeProject,
languagePreference,
areProjectPartsFromDependencies).info();
}
void ProjectPartChooser::setFallbackProjectPart(const FallBackProjectPart &getter)

View File

@@ -34,14 +34,29 @@ enum class Language { C, Cxx };
class ProjectPartInfo {
public:
enum Hint {
NoHint,
IsFallbackMatch,
IsAmbiguousMatch
NoHint = 0,
IsFallbackMatch = 1 << 0,
IsAmbiguousMatch = 1 << 1,
IsPreferredMatch = 1 << 2,
IsFromProjectMatch = 1 << 3,
IsFromDependenciesMatch = 1 << 4,
};
Q_DECLARE_FLAGS(Hints, Hint)
ProjectPartInfo() = default;
ProjectPartInfo(const ProjectPart::Ptr &projectPart,
const QList<ProjectPart::Ptr> &projectParts,
Hints hints)
: projectPart(projectPart)
, projectParts(projectParts)
, hints(hints)
{
}
public:
ProjectPart::Ptr projectPart;
Hint hint;
QList<ProjectPart::Ptr> projectParts; // The one above as first plus alternatives.
Hints hints = NoHint;
};
} // namespace CppTools

View File

@@ -49,7 +49,9 @@ protected:
protected:
QString filePath;
ProjectPartInfo currentProjectPartInfo{ProjectPart::Ptr(new ProjectPart),
ProjectPart::Ptr currentProjectPart{new ProjectPart};
ProjectPartInfo currentProjectPartInfo{currentProjectPart,
{currentProjectPart},
ProjectPartInfo::NoHint};
QString preferredProjectPartId;
const ProjectExplorer::Project *activeProject = nullptr;
@@ -74,6 +76,37 @@ TEST_F(ProjectPartChooser, ChooseManuallySet)
ASSERT_THAT(chosen, Eq(p2));
}
TEST_F(ProjectPartChooser, IndicateManuallySet)
{
ProjectPart::Ptr p1(new ProjectPart);
ProjectPart::Ptr p2(new ProjectPart);
p2->projectFile = preferredProjectPartId = "someId";
projectPartsForFile += {p1, p2};
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_TRUE(hints & ProjectPartInfo::IsPreferredMatch);
}
TEST_F(ProjectPartChooser, IndicateManuallySetForFallbackToProjectPartFromDependencies)
{
ProjectPart::Ptr p1(new ProjectPart);
ProjectPart::Ptr p2(new ProjectPart);
p2->projectFile = preferredProjectPartId = "someId";
projectPartsFromDependenciesForFile += {p1, p2};
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_TRUE(hints & ProjectPartInfo::IsPreferredMatch);
}
TEST_F(ProjectPartChooser, DoNotIndicateNotManuallySet)
{
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_FALSE(hints & ProjectPartInfo::IsPreferredMatch);
}
TEST_F(ProjectPartChooser, ForMultipleChooseFromActiveProject)
{
const QList<ProjectPart::Ptr> projectParts = createProjectPartsWithDifferentProjects();
@@ -154,9 +187,20 @@ TEST_F(ProjectPartChooser, IndicateMultiple)
const ProjectPart::Ptr p2{new ProjectPart};
projectPartsForFile += { p1, p2 };
const ProjectPartInfo::Hint hint = choose().hint;
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_THAT(hint, Eq(ProjectPartInfo::Hint::IsAmbiguousMatch));
ASSERT_TRUE(hints & ProjectPartInfo::IsAmbiguousMatch);
}
TEST_F(ProjectPartChooser, IndicateMultipleForFallbackToProjectPartFromDependencies)
{
const ProjectPart::Ptr p1{new ProjectPart};
const ProjectPart::Ptr p2{new ProjectPart};
projectPartsFromDependenciesForFile += { p1, p2 };
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_TRUE(hints & ProjectPartInfo::IsAmbiguousMatch);
}
TEST_F(ProjectPartChooser, ForMultipleChooseNewIfPreviousIsGone)
@@ -193,7 +237,7 @@ TEST_F(ProjectPartChooser, ContinueUsingFallbackFromModelManagerIfProjectDoesNot
// ...without re-calculating the dependency table.
fallbackProjectPart.reset(new ProjectPart);
currentProjectPartInfo.projectPart = fallbackProjectPart;
currentProjectPartInfo.hint = ProjectPartInfo::IsFallbackMatch;
currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
projectPartsFromDependenciesForFile += ProjectPart::Ptr(new ProjectPart);
const ProjectPart::Ptr chosen = choose().projectPart;
@@ -205,7 +249,7 @@ TEST_F(ProjectPartChooser, StopUsingFallbackFromModelManagerIfProjectChanges1)
{
fallbackProjectPart.reset(new ProjectPart);
currentProjectPartInfo.projectPart = fallbackProjectPart;
currentProjectPartInfo.hint = ProjectPartInfo::IsFallbackMatch;
currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
const ProjectPart::Ptr addedProject(new ProjectPart);
projectPartsForFile += addedProject;
@@ -218,7 +262,7 @@ TEST_F(ProjectPartChooser, StopUsingFallbackFromModelManagerIfProjectChanges2)
{
fallbackProjectPart.reset(new ProjectPart);
currentProjectPartInfo.projectPart = fallbackProjectPart;
currentProjectPartInfo.hint = ProjectPartInfo::IsFallbackMatch;
currentProjectPartInfo.hints |= ProjectPartInfo::IsFallbackMatch;
const ProjectPart::Ptr addedProject(new ProjectPart);
projectPartsFromDependenciesForFile += addedProject;
projectsChanged = true;
@@ -232,9 +276,27 @@ TEST_F(ProjectPartChooser, IndicateFallbacktoProjectPartFromModelManager)
{
fallbackProjectPart.reset(new ProjectPart);
const ProjectPartInfo::Hint hint = choose().hint;
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_THAT(hint, Eq(ProjectPartInfo::Hint::IsFallbackMatch));
ASSERT_TRUE(hints & ProjectPartInfo::IsFallbackMatch);
}
TEST_F(ProjectPartChooser, IndicateFromDependencies)
{
projectPartsFromDependenciesForFile += ProjectPart::Ptr(new ProjectPart);
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_TRUE(hints & ProjectPartInfo::IsFromDependenciesMatch);
}
TEST_F(ProjectPartChooser, DoNotIndicateFromDependencies)
{
projectPartsForFile += ProjectPart::Ptr(new ProjectPart);
const ProjectPartInfo::Hints hints = choose().hints;
ASSERT_FALSE(hints & ProjectPartInfo::IsFromDependenciesMatch);
}
void ProjectPartChooser::SetUp()