ClangCodeModel: Let users configure the clangd index location

Fixes: QTCREATORBUG-27346
Change-Id: I9bc59f759682e70b761c0f22a011868008fc0360
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Christian Kandeler
2024-02-08 15:00:55 +01:00
parent 7a055a2f0a
commit 1af555ad09
5 changed files with 122 additions and 26 deletions

View File

@@ -45,6 +45,7 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/async.h> #include <utils/async.h>
#include <utils/infobar.h> #include <utils/infobar.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QApplication> #include <QApplication>
@@ -472,17 +473,15 @@ void ClangModelManagerSupport::connectToWidgetsMarkContextMenuRequested(QWidget
} }
} }
static FilePath getJsonDbDir(const Project *project) static FilePath getJsonDbDir(Project *project)
{ {
static const QString dirName(".qtc_clangd"); if (!project)
if (!project) { return ClangdSettings::instance().sessionIndexPath(*globalMacroExpander());
const QString sessionDirName = FileUtils::fileSystemFriendlyName(
SessionManager::activeSession());
return ICore::userResourcePath() / dirName / sessionDirName; // TODO: Make configurable?
}
if (const Target *const target = project->activeTarget()) { if (const Target *const target = project->activeTarget()) {
if (const BuildConfiguration * const bc = target->activeBuildConfiguration()) if (const BuildConfiguration *const bc = target->activeBuildConfiguration()) {
return bc->buildDirectory() / dirName; return ClangdSettings(ClangdProjectSettings(project).settings())
.projectIndexPath(*bc->macroExpander());
}
} }
return {}; return {};
} }
@@ -696,7 +695,7 @@ ClangdClient *ClangModelManagerSupport::clientWithProject(const Project *project
void ClangModelManagerSupport::updateStaleIndexEntries() void ClangModelManagerSupport::updateStaleIndexEntries()
{ {
QHash<FilePath, QDateTime> lastModifiedCache; QHash<FilePath, QDateTime> lastModifiedCache;
for (const Project * const project : ProjectManager::projects()) { for (Project * const project : ProjectManager::projects()) {
const FilePath jsonDbDir = getJsonDbDir(project); const FilePath jsonDbDir = getJsonDbDir(project);
if (jsonDbDir.isEmpty()) if (jsonDbDir.isEmpty())
continue; continue;

View File

@@ -226,6 +226,10 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
Tr::tr("The directory where %1 finds its pre-installed resources.") Tr::tr("The directory where %1 finds its pre-installed resources.")
.arg(QGuiApplication::applicationDisplayName()), .arg(QGuiApplication::applicationDisplayName()),
[] { return ICore::resourcePath().toString(); }); [] { return ICore::resourcePath().toString(); });
expander->registerVariable("IDE:UserResourcePath",
Tr::tr("The directory where %1 puts custom user data.")
.arg(QGuiApplication::applicationDisplayName()),
[] { return ICore::userResourcePath().toString(); });
expander->registerPrefix("CurrentDate:", Tr::tr("The current date (QDate formatstring)."), expander->registerPrefix("CurrentDate:", Tr::tr("The current date (QDate formatstring)."),
[](const QString &fmt) { return QDate::currentDate().toString(fmt); }); [](const QString &fmt) { return QDate::currentDate().toString(fmt); });
expander->registerPrefix("CurrentTime:", Tr::tr("The current time (QTime formatstring)."), expander->registerPrefix("CurrentTime:", Tr::tr("The current time (QTime formatstring)."),

View File

@@ -15,10 +15,12 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/macroexpander.h>
#include <utils/process.h> #include <utils/process.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QDateTime> #include <QDateTime>
#include <QDir>
#include <QHash> #include <QHash>
#include <QPair> #include <QPair>
#include <QSettings> #include <QSettings>
@@ -45,6 +47,8 @@ static Key clangdSettingsKey() { return "ClangdSettings"; }
static Key useClangdKey() { return "UseClangdV7"; } static Key useClangdKey() { return "UseClangdV7"; }
static Key clangdPathKey() { return "ClangdPath"; } static Key clangdPathKey() { return "ClangdPath"; }
static Key clangdIndexingKey() { return "ClangdIndexing"; } static Key clangdIndexingKey() { return "ClangdIndexing"; }
static Key clangdProjectIndexPathKey() { return "ClangdProjectIndexPath"; }
static Key clangdSessionIndexPathKey() { return "ClangdSessionIndexPath"; }
static Key clangdIndexingPriorityKey() { return "ClangdIndexingPriority"; } static Key clangdIndexingPriorityKey() { return "ClangdIndexingPriority"; }
static Key clangdHeaderSourceSwitchModeKey() { return "ClangdHeaderSourceSwitchMode"; } static Key clangdHeaderSourceSwitchModeKey() { return "ClangdHeaderSourceSwitchMode"; }
static Key clangdCompletionRankingModelKey() { return "ClangdCompletionRankingModel"; } static Key clangdCompletionRankingModelKey() { return "ClangdCompletionRankingModel"; }
@@ -248,6 +252,16 @@ QString ClangdSettings::rankingModelToDisplayString(CompletionRankingModel model
QTC_ASSERT(false, return {}); QTC_ASSERT(false, return {});
} }
QString ClangdSettings::defaultProjectIndexPathTemplate()
{
return QDir::toNativeSeparators("%{BuildConfig:BuildDirectory:FilePath}/.qtc_clangd");
}
QString ClangdSettings::defaultSessionIndexPathTemplate()
{
return QDir::toNativeSeparators("%{IDE:UserResourcePath}/.qtc_clangd/%{Session:FileBaseName}");
}
ClangdSettings &ClangdSettings::instance() ClangdSettings &ClangdSettings::instance()
{ {
static ClangdSettings settings; static ClangdSettings settings;
@@ -318,6 +332,16 @@ FilePath ClangdSettings::clangdFilePath() const
return fallbackClangdFilePath(); return fallbackClangdFilePath();
} }
FilePath ClangdSettings::projectIndexPath(const Utils::MacroExpander &expander) const
{
return FilePath::fromUserInput(expander.expand(m_data.projectIndexPathTemplate));
}
FilePath ClangdSettings::sessionIndexPath(const Utils::MacroExpander &expander) const
{
return FilePath::fromUserInput(expander.expand(m_data.sessionIndexPathTemplate));
}
bool ClangdSettings::sizeIsOkay(const Utils::FilePath &fp) const bool ClangdSettings::sizeIsOkay(const Utils::FilePath &fp) const
{ {
return !sizeThresholdEnabled() || sizeThresholdInKb() * 1024 >= fp.fileSize(); return !sizeThresholdEnabled() || sizeThresholdInKb() * 1024 >= fp.fileSize();
@@ -559,6 +583,10 @@ Store ClangdSettings::Data::toMap() const
map.insertValueWithDefault(clangdIndexingPriorityKey(), map.insertValueWithDefault(clangdIndexingPriorityKey(),
int(indexingPriority), int(indexingPriority),
int(DefaultIndexingPriority)); int(DefaultIndexingPriority));
map.insertValueWithDefault(clangdProjectIndexPathKey(), projectIndexPathTemplate,
defaultProjectIndexPathTemplate());
map.insertValueWithDefault(clangdSessionIndexPathKey(), sessionIndexPathTemplate,
defaultSessionIndexPathTemplate());
map.insertValueWithDefault(clangdHeaderSourceSwitchModeKey(), map.insertValueWithDefault(clangdHeaderSourceSwitchModeKey(),
int(headerSourceSwitchMode), int(headerSourceSwitchMode),
@@ -610,6 +638,10 @@ void ClangdSettings::Data::fromMap(const Store &map)
const auto it = map.find(clangdIndexingKey()); const auto it = map.find(clangdIndexingKey());
if (it != map.end() && !it->toBool()) if (it != map.end() && !it->toBool())
indexingPriority = IndexingPriority::Off; indexingPriority = IndexingPriority::Off;
projectIndexPathTemplate
= map.value(clangdProjectIndexPathKey(), defaultProjectIndexPathTemplate()).toString();
sessionIndexPathTemplate
= map.value(clangdSessionIndexPathKey(), defaultSessionIndexPathTemplate()).toString();
headerSourceSwitchMode = HeaderSourceSwitchMode( headerSourceSwitchMode = HeaderSourceSwitchMode(
map.value(clangdHeaderSourceSwitchModeKey(), int(DefaultHeaderSourceSwitchMode)).toInt()); map.value(clangdHeaderSourceSwitchModeKey(), int(DefaultHeaderSourceSwitchMode)).toInt());
completionRankingModel = CompletionRankingModel( completionRankingModel = CompletionRankingModel(

View File

@@ -17,6 +17,7 @@
#include <QVersionNumber> #include <QVersionNumber>
namespace ProjectExplorer { class Project; } namespace ProjectExplorer { class Project; }
namespace Utils { class MacroExpander; }
namespace CppEditor { namespace CppEditor {
@@ -92,6 +93,8 @@ public:
static QString headerSourceSwitchModeToDisplayString(HeaderSourceSwitchMode mode); static QString headerSourceSwitchModeToDisplayString(HeaderSourceSwitchMode mode);
static QString rankingModelToCmdLineString(CompletionRankingModel model); static QString rankingModelToCmdLineString(CompletionRankingModel model);
static QString rankingModelToDisplayString(CompletionRankingModel model); static QString rankingModelToDisplayString(CompletionRankingModel model);
static QString defaultProjectIndexPathTemplate();
static QString defaultSessionIndexPathTemplate();
class CPPEDITOR_EXPORT Data class CPPEDITOR_EXPORT Data
{ {
@@ -103,6 +106,8 @@ public:
{ {
return s1.useClangd == s2.useClangd return s1.useClangd == s2.useClangd
&& s1.executableFilePath == s2.executableFilePath && s1.executableFilePath == s2.executableFilePath
&& s1.projectIndexPathTemplate == s2.projectIndexPathTemplate
&& s1.sessionIndexPathTemplate == s2.sessionIndexPathTemplate
&& s1.sessionsWithOneClangd == s2.sessionsWithOneClangd && s1.sessionsWithOneClangd == s2.sessionsWithOneClangd
&& s1.customDiagnosticConfigs == s2.customDiagnosticConfigs && s1.customDiagnosticConfigs == s2.customDiagnosticConfigs
&& s1.diagnosticConfigId == s2.diagnosticConfigId && s1.diagnosticConfigId == s2.diagnosticConfigId
@@ -119,13 +124,28 @@ public:
} }
friend bool operator!=(const Data &s1, const Data &s2) { return !(s1 == s2); } friend bool operator!=(const Data &s1, const Data &s2) { return !(s1 == s2); }
static int defaultCompletionResults();
Utils::FilePath executableFilePath; Utils::FilePath executableFilePath;
QStringList sessionsWithOneClangd; QStringList sessionsWithOneClangd;
ClangDiagnosticConfigs customDiagnosticConfigs; ClangDiagnosticConfigs customDiagnosticConfigs;
Utils::Id diagnosticConfigId; Utils::Id diagnosticConfigId;
int workerThreadLimit = DefaultWorkerThreadLimit;
int documentUpdateThreshold = DefaultDocumentUpdateThreshold;
qint64 sizeThresholdInKb = DefaultSizeThresholdInKb;
bool useClangd = DefaultUseClangd;
IndexingPriority indexingPriority = DefaultIndexingPriority;
QString projectIndexPathTemplate = defaultProjectIndexPathTemplate();
QString sessionIndexPathTemplate = defaultSessionIndexPathTemplate();
HeaderSourceSwitchMode headerSourceSwitchMode = DefaultHeaderSourceSwitchMode;
CompletionRankingModel completionRankingModel = DefaultCompletionRankingModel;
bool autoIncludeHeaders = DefaultAutoIncludeHeaders;
bool sizeThresholdEnabled = DefaultSizeThresholdEnabled;
bool haveCheckedHardwareReqirements = false;
int completionResults = defaultCompletionResults();
private:
static int defaultCompletionResults();
static constexpr auto DefaultWorkerThreadLimit = 0; static constexpr auto DefaultWorkerThreadLimit = 0;
static constexpr auto DefaultDocumentUpdateThreshold = 500; static constexpr auto DefaultDocumentUpdateThreshold = 500;
static constexpr auto DefaultSizeThresholdInKb = 1024ll; static constexpr auto DefaultSizeThresholdInKb = 1024ll;
@@ -135,18 +155,6 @@ public:
static constexpr auto DefaultCompletionRankingModel = CompletionRankingModel::Default; static constexpr auto DefaultCompletionRankingModel = CompletionRankingModel::Default;
static constexpr auto DefaultAutoIncludeHeaders = false; static constexpr auto DefaultAutoIncludeHeaders = false;
static constexpr auto DefaultSizeThresholdEnabled = false; static constexpr auto DefaultSizeThresholdEnabled = false;
int workerThreadLimit = DefaultWorkerThreadLimit;
int documentUpdateThreshold = DefaultDocumentUpdateThreshold;
qint64 sizeThresholdInKb = DefaultSizeThresholdInKb;
bool useClangd = DefaultUseClangd;
IndexingPriority indexingPriority = DefaultIndexingPriority;
HeaderSourceSwitchMode headerSourceSwitchMode = DefaultHeaderSourceSwitchMode;
CompletionRankingModel completionRankingModel = DefaultCompletionRankingModel;
bool autoIncludeHeaders = DefaultAutoIncludeHeaders;
bool sizeThresholdEnabled = DefaultSizeThresholdEnabled;
bool haveCheckedHardwareReqirements = false;
int completionResults = defaultCompletionResults();
}; };
ClangdSettings(const Data &data) : m_data(data) {} ClangdSettings(const Data &data) : m_data(data) {}
@@ -163,6 +171,8 @@ public:
static void setCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs); static void setCustomDiagnosticConfigs(const ClangDiagnosticConfigs &configs);
Utils::FilePath clangdFilePath() const; Utils::FilePath clangdFilePath() const;
IndexingPriority indexingPriority() const { return m_data.indexingPriority; } IndexingPriority indexingPriority() const { return m_data.indexingPriority; }
Utils::FilePath projectIndexPath(const Utils::MacroExpander &expander) const;
Utils::FilePath sessionIndexPath(const Utils::MacroExpander &expander) const;
HeaderSourceSwitchMode headerSourceSwitchMode() const { return m_data.headerSourceSwitchMode; } HeaderSourceSwitchMode headerSourceSwitchMode() const { return m_data.headerSourceSwitchMode; }
CompletionRankingModel completionRankingModel() const { return m_data.completionRankingModel; } CompletionRankingModel completionRankingModel() const { return m_data.completionRankingModel; }
bool autoIncludeHeaders() const { return m_data.autoIncludeHeaders; } bool autoIncludeHeaders() const { return m_data.autoIncludeHeaders; }

View File

@@ -18,11 +18,13 @@
#include <projectexplorer/projectsettingswidget.h> #include <projectexplorer/projectsettingswidget.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/fancylineedit.h>
#include <utils/infolabel.h> #include <utils/infolabel.h>
#include <utils/itemviews.h> #include <utils/itemviews.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/variablechooser.h>
#include <QCheckBox> #include <QCheckBox>
#include <QComboBox> #include <QComboBox>
@@ -36,6 +38,7 @@
#include <QStringListModel> #include <QStringListModel>
#include <QTextBlock> #include <QTextBlock>
#include <QTextStream> #include <QTextStream>
#include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QVersionNumber> #include <QVersionNumber>
@@ -220,6 +223,8 @@ signals:
private: private:
QCheckBox m_useClangdCheckBox; QCheckBox m_useClangdCheckBox;
QComboBox m_indexingComboBox; QComboBox m_indexingComboBox;
Utils::FancyLineEdit m_projectIndexPathTemplateLineEdit;
Utils::FancyLineEdit m_sessionIndexPathTemplateLineEdit;
QComboBox m_headerSourceSwitchComboBox; QComboBox m_headerSourceSwitchComboBox;
QComboBox m_completionRankingModelComboBox; QComboBox m_completionRankingModelComboBox;
QCheckBox m_autoIncludeHeadersCheckBox; QCheckBox m_autoIncludeHeadersCheckBox;
@@ -249,6 +254,12 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
"cores unused.</p>" "cores unused.</p>"
"<p>Normal Priority: Reduced priority compared to interactive work.</p>" "<p>Normal Priority: Reduced priority compared to interactive work.</p>"
"<p>Low Priority: Same priority as other clangd work.</p>"); "<p>Low Priority: Same priority as other clangd work.</p>");
const QString projectIndexPathToolTip = Tr::tr(
"The location of the per-project clangd index.<p>"
"This is also where the compile_commands.json file will go.");
const QString sessionIndexPathToolTip = Tr::tr(
"The location of the per-session clangd index.<p>"
"This is also where the compile_commands.json file will go.");
const QString headerSourceSwitchToolTip = Tr::tr( const QString headerSourceSwitchToolTip = Tr::tr(
"<p>The C/C++ backend to use for switching between header and source files.</p>" "<p>The C/C++ backend to use for switching between header and source files.</p>"
"<p>While the clangd implementation has more capabilities than the built-in " "<p>While the clangd implementation has more capabilities than the built-in "
@@ -296,6 +307,8 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
m_indexingComboBox.setCurrentIndex(m_indexingComboBox.count() - 1); m_indexingComboBox.setCurrentIndex(m_indexingComboBox.count() - 1);
} }
m_indexingComboBox.setToolTip(indexingToolTip); m_indexingComboBox.setToolTip(indexingToolTip);
m_projectIndexPathTemplateLineEdit.setText(settings.data().projectIndexPathTemplate);
m_sessionIndexPathTemplateLineEdit.setText(settings.data().sessionIndexPathTemplate);
using SwitchMode = ClangdSettings::HeaderSourceSwitchMode; using SwitchMode = ClangdSettings::HeaderSourceSwitchMode;
for (SwitchMode mode : {SwitchMode::BuiltinOnly, SwitchMode::ClangdOnly, SwitchMode::Both}) { for (SwitchMode mode : {SwitchMode::BuiltinOnly, SwitchMode::ClangdOnly, SwitchMode::Both}) {
m_headerSourceSwitchComboBox.addItem( m_headerSourceSwitchComboBox.addItem(
@@ -360,6 +373,33 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
indexingPriorityLabel->setToolTip(indexingToolTip); indexingPriorityLabel->setToolTip(indexingToolTip);
formLayout->addRow(indexingPriorityLabel, indexingPriorityLayout); formLayout->addRow(indexingPriorityLabel, indexingPriorityLayout);
for (const auto &[text, edit, toolTip, defaultValue] :
{std::make_tuple(Tr::tr("Per-project index location:"),
&m_projectIndexPathTemplateLineEdit,
projectIndexPathToolTip,
ClangdSettings::defaultProjectIndexPathTemplate()),
std::make_tuple(Tr::tr("Per-session index location:"),
&m_sessionIndexPathTemplateLineEdit,
sessionIndexPathToolTip,
ClangdSettings::defaultSessionIndexPathTemplate())}) {
if (isForProject && edit == &m_sessionIndexPathTemplateLineEdit)
continue;
const auto chooser = new Utils::VariableChooser(edit);
chooser->addSupportedWidget(edit);
chooser->addMacroExpanderProvider([] { return Utils::globalMacroExpander(); });
const auto resetButton = new QPushButton(Tr::tr("Reset"));
connect(resetButton, &QPushButton::clicked, [e = edit, v = defaultValue] { e->setText(v); });
const auto layout = new QHBoxLayout;
const auto label = new QLabel(text);
label->setToolTip(toolTip);
edit->setToolTip(toolTip);
layout->addWidget(edit);
layout->addWidget(resetButton);
formLayout->addRow(label, layout);
}
const auto headerSourceSwitchLayout = new QHBoxLayout; const auto headerSourceSwitchLayout = new QHBoxLayout;
headerSourceSwitchLayout->addWidget(&m_headerSourceSwitchComboBox); headerSourceSwitchLayout->addWidget(&m_headerSourceSwitchComboBox);
headerSourceSwitchLayout->addStretch(1); headerSourceSwitchLayout->addStretch(1);
@@ -530,6 +570,10 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD
this, &ClangdSettingsWidget::settingsDataChanged); this, &ClangdSettingsWidget::settingsDataChanged);
connect(&m_indexingComboBox, &QComboBox::currentIndexChanged, connect(&m_indexingComboBox, &QComboBox::currentIndexChanged,
this, &ClangdSettingsWidget::settingsDataChanged); this, &ClangdSettingsWidget::settingsDataChanged);
connect(&m_projectIndexPathTemplateLineEdit, &QLineEdit::textChanged,
this, &ClangdSettingsWidget::settingsDataChanged);
connect(&m_sessionIndexPathTemplateLineEdit, &QLineEdit::textChanged,
this, &ClangdSettingsWidget::settingsDataChanged);
connect(&m_headerSourceSwitchComboBox, &QComboBox::currentIndexChanged, connect(&m_headerSourceSwitchComboBox, &QComboBox::currentIndexChanged,
this, &ClangdSettingsWidget::settingsDataChanged); this, &ClangdSettingsWidget::settingsDataChanged);
connect(&m_completionRankingModelComboBox, &QComboBox::currentIndexChanged, connect(&m_completionRankingModelComboBox, &QComboBox::currentIndexChanged,
@@ -559,6 +603,8 @@ ClangdSettings::Data ClangdSettingsWidget::settingsData() const
data.executableFilePath = m_clangdChooser.filePath(); data.executableFilePath = m_clangdChooser.filePath();
data.indexingPriority = ClangdSettings::IndexingPriority( data.indexingPriority = ClangdSettings::IndexingPriority(
m_indexingComboBox.currentData().toInt()); m_indexingComboBox.currentData().toInt());
data.projectIndexPathTemplate = m_projectIndexPathTemplateLineEdit.text();
data.sessionIndexPathTemplate = m_sessionIndexPathTemplateLineEdit.text();
data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode( data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode(
m_headerSourceSwitchComboBox.currentData().toInt()); m_headerSourceSwitchComboBox.currentData().toInt());
data.completionRankingModel = ClangdSettings::CompletionRankingModel( data.completionRankingModel = ClangdSettings::CompletionRankingModel(
@@ -641,9 +687,14 @@ public:
m_settings.setSettings(m_widget.settingsData()); m_settings.setSettings(m_widget.settingsData());
}); });
connect(&m_widget, &ClangdSettingsWidget::settingsDataChanged, this, [this] { const auto timer = new QTimer(this);
timer->setSingleShot(true);
timer->setInterval(5000);
connect(timer, &QTimer::timeout, this, [this] {
m_settings.setSettings(m_widget.settingsData()); m_settings.setSettings(m_widget.settingsData());
}); });
connect(&m_widget, &ClangdSettingsWidget::settingsDataChanged,
timer, qOverload<>(&QTimer::start));
} }
private: private: