Files
qt-creator/src/plugins/languageclient/languageclientsettings.cpp
David Schulz 605df0c4b6 Java: use a temporary directory for the workspace
Change-Id: Ie8fc01f680393f141f099f57b98eb16a10b37d7e
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
2021-02-23 08:48:49 +00:00

1010 lines
33 KiB
C++

/****************************************************************************
**
** Copyright (C) 2018 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 "languageclientsettings.h"
#include "client.h"
#include "languageclientmanager.h"
#include "languageclient_global.h"
#include "languageclientinterface.h"
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <projectexplorer/project.h>
#include <projectexplorer/session.h>
#include <utils/algorithm.h>
#include <utils/utilsicons.h>
#include <utils/delegates.h>
#include <utils/fancylineedit.h>
#include <utils/mimetypes/mimedatabase.h>
#include <utils/jsontreeitem.h>
#include <utils/stringutils.h>
#include <utils/variablechooser.h>
#include <QBoxLayout>
#include <QComboBox>
#include <QCompleter>
#include <QCoreApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QDir>
#include <QFileInfo>
#include <QHeaderView>
#include <QJsonDocument>
#include <QLabel>
#include <QListView>
#include <QMenu>
#include <QMimeData>
#include <QPushButton>
#include <QSettings>
#include <QSortFilterProxyModel>
#include <QStringListModel>
#include <QToolButton>
#include <QTreeView>
constexpr char typeIdKey[] = "typeId";
constexpr char nameKey[] = "name";
constexpr char idKey[] = "id";
constexpr char enabledKey[] = "enabled";
constexpr char startupBehaviorKey[] = "startupBehavior";
constexpr char mimeTypeKey[] = "mimeType";
constexpr char filePatternKey[] = "filePattern";
constexpr char initializationOptionsKey[] = "initializationOptions";
constexpr char executableKey[] = "executable";
constexpr char argumentsKey[] = "arguments";
constexpr char settingsGroupKey[] = "LanguageClient";
constexpr char clientsKey[] = "clients";
constexpr char mimeType[] = "application/language.client.setting";
namespace LanguageClient {
class LanguageClientSettingsModel : public QAbstractListModel
{
public:
LanguageClientSettingsModel() = default;
~LanguageClientSettingsModel() override;
// QAbstractItemModel interface
int rowCount(const QModelIndex &/*parent*/ = QModelIndex()) const final { return m_settings.count(); }
QVariant data(const QModelIndex &index, int role) const final;
bool removeRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final;
bool insertRows(int row, int count = 1, const QModelIndex &parent = QModelIndex()) final;
bool setData(const QModelIndex &index, const QVariant &value, int role) final;
Qt::ItemFlags flags(const QModelIndex &index) const final;
Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; }
QStringList mimeTypes() const override { return {mimeType}; }
QMimeData *mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
const QModelIndex &parent) override;
void reset(const QList<BaseSettings *> &settings);
QList<BaseSettings *> settings() const { return m_settings; }
int insertSettings(BaseSettings *settings);
void enableSetting(const QString &id);
QList<BaseSettings *> removed() const { return m_removed; }
BaseSettings *settingForIndex(const QModelIndex &index) const;
QModelIndex indexForSetting(BaseSettings *setting) const;
private:
static constexpr int idRole = Qt::UserRole + 1;
QList<BaseSettings *> m_settings; // owned
QList<BaseSettings *> m_removed;
};
class LanguageClientSettingsPageWidget : public QWidget
{
public:
LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings);
void currentChanged(const QModelIndex &index);
int currentRow() const;
void resetCurrentSettings(int row);
void applyCurrentSettings();
private:
LanguageClientSettingsModel &m_settings;
QTreeView *m_view = nullptr;
struct CurrentSettings {
BaseSettings *setting = nullptr;
QWidget *widget = nullptr;
} m_currentSettings;
void addItem(const Utils::Id &clientTypeId);
void deleteItem();
};
class LanguageClientSettingsPage : public Core::IOptionsPage
{
Q_DECLARE_TR_FUNCTIONS(LanguageClientSettingsPage)
public:
LanguageClientSettingsPage();
~LanguageClientSettingsPage() override;
void init();
// IOptionsPage interface
QWidget *widget() override;
void apply() override;
void finish() override;
QList<BaseSettings *> settings() const;
QList<BaseSettings *> changedSettings() const;
void addSettings(BaseSettings *settings);
void enableSettings(const QString &id);
private:
LanguageClientSettingsModel m_model;
QSet<QString> m_changedSettings;
QPointer<LanguageClientSettingsPageWidget> m_widget;
};
QMap<Utils::Id, ClientType> &clientTypes()
{
static QMap<Utils::Id, ClientType> types;
return types;
}
LanguageClientSettingsPageWidget::LanguageClientSettingsPageWidget(LanguageClientSettingsModel &settings)
: m_settings(settings)
, m_view(new QTreeView())
{
auto mainLayout = new QVBoxLayout();
auto layout = new QHBoxLayout();
m_view->setModel(&m_settings);
m_view->setHeaderHidden(true);
m_view->setSelectionMode(QAbstractItemView::SingleSelection);
m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
m_view->setDragEnabled(true);
m_view->viewport()->setAcceptDrops(true);
m_view->setDropIndicatorShown(true);
m_view->setDragDropMode(QAbstractItemView::InternalMove);
connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged,
this, &LanguageClientSettingsPageWidget::currentChanged);
auto buttonLayout = new QVBoxLayout();
auto addButton = new QPushButton(LanguageClientSettingsPage::tr("&Add"));
auto addMenu = new QMenu;
addMenu->clear();
for (const ClientType &type : clientTypes()) {
auto action = new QAction(tr("New %1").arg(type.name));
connect(action, &QAction::triggered, this, [this, id = type.id]() { addItem(id); });
addMenu->addAction(action);
}
addButton->setMenu(addMenu);
auto deleteButton = new QPushButton(LanguageClientSettingsPage::tr("&Delete"));
connect(deleteButton, &QPushButton::pressed, this, &LanguageClientSettingsPageWidget::deleteItem);
mainLayout->addLayout(layout);
setLayout(mainLayout);
layout->addWidget(m_view);
layout->addLayout(buttonLayout);
buttonLayout->addWidget(addButton);
buttonLayout->addWidget(deleteButton);
buttonLayout->addStretch(10);
}
void LanguageClientSettingsPageWidget::currentChanged(const QModelIndex &index)
{
if (m_currentSettings.widget) {
applyCurrentSettings();
layout()->removeWidget(m_currentSettings.widget);
delete m_currentSettings.widget;
}
if (index.isValid()) {
m_currentSettings.setting = m_settings.settingForIndex(index);
m_currentSettings.widget = m_currentSettings.setting->createSettingsWidget(this);
layout()->addWidget(m_currentSettings.widget);
} else {
m_currentSettings.setting = nullptr;
m_currentSettings.widget = nullptr;
}
}
int LanguageClientSettingsPageWidget::currentRow() const
{
return m_settings.indexForSetting(m_currentSettings.setting).row();
}
void LanguageClientSettingsPageWidget::resetCurrentSettings(int row)
{
if (m_currentSettings.widget) {
layout()->removeWidget(m_currentSettings.widget);
delete m_currentSettings.widget;
}
m_currentSettings.setting = nullptr;
m_currentSettings.widget = nullptr;
m_view->setCurrentIndex(m_settings.index(row));
}
void LanguageClientSettingsPageWidget::applyCurrentSettings()
{
if (!m_currentSettings.setting)
return;
if (m_currentSettings.setting->applyFromSettingsWidget(m_currentSettings.widget)) {
auto index = m_settings.indexForSetting(m_currentSettings.setting);
emit m_settings.dataChanged(index, index);
}
}
BaseSettings *generateSettings(const Utils::Id &clientTypeId)
{
if (auto generator = clientTypes().value(clientTypeId).generator) {
auto settings = generator();
settings->m_settingsTypeId = clientTypeId;
return settings;
}
return nullptr;
}
void LanguageClientSettingsPageWidget::addItem(const Utils::Id &clientTypeId)
{
auto newSettings = generateSettings(clientTypeId);
QTC_ASSERT(newSettings, return);
m_view->setCurrentIndex(m_settings.index(m_settings.insertSettings(newSettings)));
}
void LanguageClientSettingsPageWidget::deleteItem()
{
auto index = m_view->currentIndex();
if (!index.isValid())
return;
m_settings.removeRows(index.row());
}
LanguageClientSettingsPage::LanguageClientSettingsPage()
{
setId(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
setDisplayName(tr("General"));
setCategory(Constants::LANGUAGECLIENT_SETTINGS_CATEGORY);
setDisplayCategory(QCoreApplication::translate("LanguageClient",
Constants::LANGUAGECLIENT_SETTINGS_TR));
setCategoryIconPath(":/languageclient/images/settingscategory_languageclient.png");
connect(&m_model, &LanguageClientSettingsModel::dataChanged, [this](const QModelIndex &index) {
if (BaseSettings *setting = m_model.settingForIndex(index))
m_changedSettings << setting->m_id;
});
}
LanguageClientSettingsPage::~LanguageClientSettingsPage()
{
if (m_widget)
delete m_widget;
}
void LanguageClientSettingsPage::init()
{
m_model.reset(LanguageClientSettings::fromSettings(Core::ICore::settings()));
apply();
finish();
}
QWidget *LanguageClientSettingsPage::widget()
{
if (!m_widget)
m_widget = new LanguageClientSettingsPageWidget(m_model);
return m_widget;
}
void LanguageClientSettingsPage::apply()
{
if (m_widget)
m_widget->applyCurrentSettings();
LanguageClientManager::applySettings();
for (BaseSettings *setting : m_model.removed()) {
for (Client *client : LanguageClientManager::clientForSetting(setting))
LanguageClientManager::shutdownClient(client);
}
if (m_widget) {
int row = m_widget->currentRow();
m_model.reset(LanguageClientManager::currentSettings());
m_widget->resetCurrentSettings(row);
} else {
m_model.reset(LanguageClientManager::currentSettings());
}
}
void LanguageClientSettingsPage::finish()
{
m_model.reset(LanguageClientManager::currentSettings());
m_changedSettings.clear();
}
QList<BaseSettings *> LanguageClientSettingsPage::settings() const
{
return m_model.settings();
}
QList<BaseSettings *> LanguageClientSettingsPage::changedSettings() const
{
QList<BaseSettings *> result;
const QList<BaseSettings *> &all = settings();
for (BaseSettings *setting : all) {
if (m_changedSettings.contains(setting->m_id))
result << setting;
}
return result;
}
void LanguageClientSettingsPage::addSettings(BaseSettings *settings)
{
m_model.insertSettings(settings);
}
void LanguageClientSettingsPage::enableSettings(const QString &id)
{
m_model.enableSetting(id);
}
LanguageClientSettingsModel::~LanguageClientSettingsModel()
{
qDeleteAll(m_settings);
}
QVariant LanguageClientSettingsModel::data(const QModelIndex &index, int role) const
{
BaseSettings *setting = settingForIndex(index);
if (!setting)
return QVariant();
if (role == Qt::DisplayRole)
return Utils::globalMacroExpander()->expand(setting->m_name);
else if (role == Qt::CheckStateRole)
return setting->m_enabled ? Qt::Checked : Qt::Unchecked;
else if (role == idRole)
return setting->m_id;
return QVariant();
}
bool LanguageClientSettingsModel::removeRows(int row, int count, const QModelIndex &parent)
{
if (row >= int(m_settings.size()))
return false;
const int end = qMin(row + count - 1, int(m_settings.size()) - 1);
beginRemoveRows(parent, row, end);
for (auto i = end; i >= row; --i)
m_removed << m_settings.takeAt(i);
endRemoveRows();
return true;
}
bool LanguageClientSettingsModel::insertRows(int row, int count, const QModelIndex &parent)
{
if (row > m_settings.size() || row < 0)
return false;
beginInsertRows(parent, row, row + count - 1);
for (int i = 0; i < count; ++i)
m_settings.insert(row + i, new StdIOSettings());
endInsertRows();
return true;
}
bool LanguageClientSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
BaseSettings *setting = settingForIndex(index);
if (!setting || role != Qt::CheckStateRole)
return false;
if (setting->m_enabled != value.toBool()) {
setting->m_enabled = !setting->m_enabled;
emit dataChanged(index, index, { Qt::CheckStateRole });
}
return true;
}
Qt::ItemFlags LanguageClientSettingsModel::flags(const QModelIndex &index) const
{
const Qt::ItemFlags dragndropFlags = index.isValid() ? Qt::ItemIsDragEnabled
: Qt::ItemIsDropEnabled;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | dragndropFlags;
}
QMimeData *LanguageClientSettingsModel::mimeData(const QModelIndexList &indexes) const
{
QTC_ASSERT(indexes.count() == 1, return nullptr);
QMimeData *mimeData = new QMimeData;
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
for (const QModelIndex &index : indexes) {
if (index.isValid())
stream << data(index, idRole).toString();
}
mimeData->setData(mimeType, indexes.first().data(idRole).toString().toUtf8());
return mimeData;
}
bool LanguageClientSettingsModel::dropMimeData(
const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!canDropMimeData(data, action, row, column, parent))
return false;
if (action == Qt::IgnoreAction)
return true;
const QString id = QString::fromUtf8(data->data(mimeType));
auto setting = Utils::findOrDefault(m_settings, [id](const BaseSettings *setting) {
return setting->m_id == id;
});
if (!setting)
return false;
if (row == -1)
row = parent.isValid() ? parent.row() : rowCount(QModelIndex());
beginInsertRows(parent, row, row);
m_settings.insert(row, setting->copy());
endInsertRows();
return true;
}
void LanguageClientSettingsModel::reset(const QList<BaseSettings *> &settings)
{
beginResetModel();
qDeleteAll(m_settings);
qDeleteAll(m_removed);
m_removed.clear();
m_settings = Utils::transform(settings, [](const BaseSettings *other) { return other->copy(); });
endResetModel();
}
int LanguageClientSettingsModel::insertSettings(BaseSettings *settings)
{
int row = rowCount();
beginInsertRows(QModelIndex(), row, row);
m_settings.insert(row, settings);
endInsertRows();
return row;
}
void LanguageClientSettingsModel::enableSetting(const QString &id)
{
BaseSettings *setting = Utils::findOrDefault(m_settings, Utils::equal(&BaseSettings::m_id, id));
if (!setting)
return;
setting->m_enabled = true;
const QModelIndex &index = indexForSetting(setting);
if (index.isValid())
emit dataChanged(index, index, {Qt::CheckStateRole});
}
BaseSettings *LanguageClientSettingsModel::settingForIndex(const QModelIndex &index) const
{
if (!index.isValid() || index.row() >= m_settings.size())
return nullptr;
return m_settings[index.row()];
}
QModelIndex LanguageClientSettingsModel::indexForSetting(BaseSettings *setting) const
{
const int index = m_settings.indexOf(setting);
return index < 0 ? QModelIndex() : createIndex(index, 0, setting);
}
QJsonObject BaseSettings::initializationOptions() const
{
return QJsonDocument::fromJson(Utils::globalMacroExpander()->
expand(m_initializationOptions).toUtf8()).object();
}
bool BaseSettings::applyFromSettingsWidget(QWidget *widget)
{
bool changed = false;
if (auto settingsWidget = qobject_cast<BaseSettingsWidget *>(widget)) {
if (m_name != settingsWidget->name()) {
m_name = settingsWidget->name();
changed = true;
}
if (m_languageFilter != settingsWidget->filter()) {
m_languageFilter = settingsWidget->filter();
changed = true;
}
if (m_startBehavior != settingsWidget->startupBehavior()) {
m_startBehavior = settingsWidget->startupBehavior();
changed = true;
}
if (m_initializationOptions != settingsWidget->initializationOptions()) {
m_initializationOptions = settingsWidget->initializationOptions();
changed = true;
}
}
return changed;
}
QWidget *BaseSettings::createSettingsWidget(QWidget *parent) const
{
return new BaseSettingsWidget(this, parent);
}
bool BaseSettings::isValid() const
{
return !m_name.isEmpty();
}
Client *BaseSettings::createClient()
{
if (!isValid() || !m_enabled)
return nullptr;
BaseClientInterface *interface = createInterface();
QTC_ASSERT(interface, return nullptr);
auto *client = createClient(interface);
client->setName(Utils::globalMacroExpander()->expand(m_name));
client->setSupportedLanguage(m_languageFilter);
client->setInitializationOptions(initializationOptions());
return client;
}
Client *BaseSettings::createClient(BaseClientInterface *interface) const
{
return new Client(interface);
}
QVariantMap BaseSettings::toMap() const
{
QVariantMap map;
map.insert(typeIdKey, m_settingsTypeId.toSetting());
map.insert(nameKey, m_name);
map.insert(idKey, m_id);
map.insert(enabledKey, m_enabled);
map.insert(startupBehaviorKey, m_startBehavior);
map.insert(mimeTypeKey, m_languageFilter.mimeTypes);
map.insert(filePatternKey, m_languageFilter.filePattern);
map.insert(initializationOptionsKey, m_initializationOptions);
return map;
}
void BaseSettings::fromMap(const QVariantMap &map)
{
m_name = map[nameKey].toString();
m_id = map.value(idKey, QUuid::createUuid().toString()).toString();
m_enabled = map[enabledKey].toBool();
m_startBehavior = BaseSettings::StartBehavior(
map.value(startupBehaviorKey, BaseSettings::RequiresFile).toInt());
m_languageFilter.mimeTypes = map[mimeTypeKey].toStringList();
m_languageFilter.filePattern = map[filePatternKey].toStringList();
m_languageFilter.filePattern.removeAll(QString()); // remove empty entries
m_initializationOptions = map[initializationOptionsKey].toString();
}
static LanguageClientSettingsPage &settingsPage()
{
static LanguageClientSettingsPage settingsPage;
return settingsPage;
}
void LanguageClientSettings::init()
{
settingsPage().init();
}
QList<BaseSettings *> LanguageClientSettings::fromSettings(QSettings *settingsIn)
{
settingsIn->beginGroup(settingsGroupKey);
QList<BaseSettings *> result;
for (const QVariant& var : settingsIn->value(clientsKey).toList()) {
const QMap<QString, QVariant> &map = var.toMap();
Utils::Id typeId = Utils::Id::fromSetting(map.value(typeIdKey));
if (!typeId.isValid())
typeId = Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID;
if (BaseSettings *settings = generateSettings(typeId)) {
settings->fromMap(var.toMap());
result << settings;
}
}
settingsIn->endGroup();
return result;
}
QList<BaseSettings *> LanguageClientSettings::pageSettings()
{
return settingsPage().settings();
}
QList<BaseSettings *> LanguageClientSettings::changedSettings()
{
return settingsPage().changedSettings();
}
void LanguageClientSettings::registerClientType(const ClientType &type)
{
QTC_ASSERT(!clientTypes().contains(type.id), return);
clientTypes()[type.id] = type;
}
void LanguageClientSettings::addSettings(BaseSettings *settings)
{
settingsPage().addSettings(settings);
}
void LanguageClientSettings::enableSettings(const QString &id)
{
settingsPage().enableSettings(id);
}
void LanguageClientSettings::toSettings(QSettings *settings,
const QList<BaseSettings *> &languageClientSettings)
{
settings->beginGroup(settingsGroupKey);
settings->setValue(clientsKey, Utils::transform(languageClientSettings,
[](const BaseSettings *setting){
return QVariant(setting->toMap());
}));
settings->endGroup();
}
bool StdIOSettings::applyFromSettingsWidget(QWidget *widget)
{
bool changed = false;
if (auto settingsWidget = qobject_cast<StdIOSettingsWidget *>(widget)) {
changed = BaseSettings::applyFromSettingsWidget(settingsWidget);
if (m_executable != settingsWidget->executable()) {
m_executable = settingsWidget->executable();
changed = true;
}
if (m_arguments != settingsWidget->arguments()) {
m_arguments = settingsWidget->arguments();
changed = true;
}
}
return changed;
}
QWidget *StdIOSettings::createSettingsWidget(QWidget *parent) const
{
return new StdIOSettingsWidget(this, parent);
}
bool StdIOSettings::isValid() const
{
return BaseSettings::isValid() && !m_executable.isEmpty();
}
QVariantMap StdIOSettings::toMap() const
{
QVariantMap map = BaseSettings::toMap();
map.insert(executableKey, m_executable);
map.insert(argumentsKey, m_arguments);
return map;
}
void StdIOSettings::fromMap(const QVariantMap &map)
{
BaseSettings::fromMap(map);
m_executable = map[executableKey].toString();
m_arguments = map[argumentsKey].toString();
}
QString StdIOSettings::arguments() const
{
return Utils::globalMacroExpander()->expand(m_arguments);
}
Utils::CommandLine StdIOSettings::command() const
{
return Utils::CommandLine(Utils::FilePath::fromUserInput(m_executable),
arguments(),
Utils::CommandLine::Raw);
}
BaseClientInterface *StdIOSettings::createInterface() const
{
auto interface = new StdIOClientInterface;
interface->setExecutable(m_executable);
interface->setArguments(arguments());
return interface;
}
class JsonTreeItemDelegate : public QStyledItemDelegate
{
public:
QString displayText(const QVariant &value, const QLocale &) const override
{
QString result = value.toString();
if (result.size() == 1) {
switch (result.at(0).toLatin1()) {
case '\n':
return QString("\\n");
case '\t':
return QString("\\t");
case '\r':
return QString("\\r");
}
}
return result;
}
};
static QString startupBehaviorString(BaseSettings::StartBehavior behavior)
{
switch (behavior) {
case BaseSettings::AlwaysOn:
return QCoreApplication::translate("LanguageClient::BaseSettings", "Always On");
case BaseSettings::RequiresFile:
return QCoreApplication::translate("LanguageClient::BaseSettings", "Requires an Open File");
case BaseSettings::RequiresProject:
return QCoreApplication::translate("LanguageClient::BaseSettings",
"Start Server per Project");
default:
break;
}
return {};
}
BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent)
: QWidget(parent)
, m_name(new QLineEdit(settings->m_name, this))
, m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this))
, m_filePattern(new QLineEdit(settings->m_languageFilter.filePattern.join(filterSeparator), this))
, m_startupBehavior(new QComboBox)
, m_initializationOptions(new Utils::FancyLineEdit(this))
{
int row = 0;
auto *mainLayout = new QGridLayout;
mainLayout->addWidget(new QLabel(tr("Name:")), row, 0);
mainLayout->addWidget(m_name, row, 1);
auto chooser = new Utils::VariableChooser(this);
chooser->addSupportedWidget(m_name);
mainLayout->addWidget(new QLabel(tr("Language:")), ++row, 0);
auto mimeLayout = new QHBoxLayout;
mimeLayout->addWidget(m_mimeTypes);
mimeLayout->addStretch();
auto addMimeTypeButton = new QPushButton(tr("Set MIME Types..."), this);
mimeLayout->addWidget(addMimeTypeButton);
mainLayout->addLayout(mimeLayout, row, 1);
m_filePattern->setPlaceholderText(tr("File pattern"));
mainLayout->addWidget(m_filePattern, ++row, 1);
mainLayout->addWidget(new QLabel(tr("Startup behavior:")), ++row, 0);
for (int behavior = 0; behavior < BaseSettings::LastSentinel ; ++behavior)
m_startupBehavior->addItem(startupBehaviorString(BaseSettings::StartBehavior(behavior)));
m_startupBehavior->setCurrentIndex(settings->m_startBehavior);
mainLayout->addWidget(m_startupBehavior, row, 1);
connect(addMimeTypeButton, &QPushButton::pressed,
this, &BaseSettingsWidget::showAddMimeTypeDialog);
mainLayout->addWidget(new QLabel(tr("Initialization options:")), ++row, 0);
mainLayout->addWidget(m_initializationOptions, row, 1);
chooser->addSupportedWidget(m_initializationOptions);
m_initializationOptions->setValidationFunction([](Utils::FancyLineEdit *edit, QString *errorMessage) {
const QString value = Utils::globalMacroExpander()->expand(edit->text());
if (value.isEmpty())
return true;
QJsonParseError parseInfo;
const QJsonDocument json = QJsonDocument::fromJson(value.toUtf8(), &parseInfo);
if (json.isNull()) {
if (errorMessage)
*errorMessage = tr("Failed to parse JSON at %1: %2")
.arg(parseInfo.offset)
.arg(parseInfo.errorString());
return false;
}
return true;
});
m_initializationOptions->setText(settings->m_initializationOptions);
m_initializationOptions->setPlaceholderText(tr("Language server-specific JSON to pass via "
"\"initializationOptions\" field of \"initialize\" "
"request."));
setLayout(mainLayout);
}
QString BaseSettingsWidget::name() const
{
return m_name->text();
}
LanguageFilter BaseSettingsWidget::filter() const
{
return {m_mimeTypes->text().split(filterSeparator, Qt::SkipEmptyParts),
m_filePattern->text().split(filterSeparator, Qt::SkipEmptyParts)};
}
BaseSettings::StartBehavior BaseSettingsWidget::startupBehavior() const
{
return BaseSettings::StartBehavior(m_startupBehavior->currentIndex());
}
QString BaseSettingsWidget::initializationOptions() const
{
return m_initializationOptions->text();
}
class MimeTypeModel : public QStringListModel
{
public:
using QStringListModel::QStringListModel;
QVariant data(const QModelIndex &index, int role) const final
{
if (index.isValid() && role == Qt::CheckStateRole)
return m_selectedMimeTypes.contains(index.data().toString()) ? Qt::Checked : Qt::Unchecked;
return QStringListModel::data(index, role);
}
bool setData(const QModelIndex &index, const QVariant &value, int role) final
{
if (index.isValid() && role == Qt::CheckStateRole) {
QString mimeType = index.data().toString();
if (value.toInt() == Qt::Checked) {
if (!m_selectedMimeTypes.contains(mimeType))
m_selectedMimeTypes.append(index.data().toString());
} else {
m_selectedMimeTypes.removeAll(index.data().toString());
}
return true;
}
return QStringListModel::setData(index, value, role);
}
Qt::ItemFlags flags(const QModelIndex &index) const final
{
if (!index.isValid())
return Qt::NoItemFlags;
return (QStringListModel::flags(index)
& ~(Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled))
| Qt::ItemIsUserCheckable;
}
QStringList m_selectedMimeTypes;
};
class MimeTypeDialog : public QDialog
{
Q_DECLARE_TR_FUNCTIONS(MimeTypeDialog)
public:
explicit MimeTypeDialog(const QStringList &selectedMimeTypes, QWidget *parent = nullptr)
: QDialog(parent)
{
setWindowTitle(tr("Select MIME Types"));
auto mainLayout = new QVBoxLayout;
auto filter = new Utils::FancyLineEdit(this);
filter->setFiltering(true);
mainLayout->addWidget(filter);
auto listView = new QListView(this);
mainLayout->addWidget(listView);
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
mainLayout->addWidget(buttons);
setLayout(mainLayout);
filter->setPlaceholderText(tr("Filter"));
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
auto proxy = new QSortFilterProxyModel(this);
m_mimeTypeModel = new MimeTypeModel(Utils::transform(Utils::allMimeTypes(),
&Utils::MimeType::name), this);
m_mimeTypeModel->m_selectedMimeTypes = selectedMimeTypes;
proxy->setSourceModel(m_mimeTypeModel);
proxy->sort(0);
connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterWildcard);
listView->setModel(proxy);
setModal(true);
}
MimeTypeDialog(const MimeTypeDialog &other) = delete;
MimeTypeDialog(MimeTypeDialog &&other) = delete;
MimeTypeDialog operator=(const MimeTypeDialog &other) = delete;
MimeTypeDialog operator=(MimeTypeDialog &&other) = delete;
QStringList mimeTypes() const
{
return m_mimeTypeModel->m_selectedMimeTypes;
}
private:
MimeTypeModel *m_mimeTypeModel = nullptr;
};
void BaseSettingsWidget::showAddMimeTypeDialog()
{
MimeTypeDialog dialog(m_mimeTypes->text().split(filterSeparator, Qt::SkipEmptyParts),
Core::ICore::dialogParent());
if (dialog.exec() == QDialog::Rejected)
return;
m_mimeTypes->setText(dialog.mimeTypes().join(filterSeparator));
}
StdIOSettingsWidget::StdIOSettingsWidget(const StdIOSettings *settings, QWidget *parent)
: BaseSettingsWidget(settings, parent)
, m_executable(new Utils::PathChooser(this))
, m_arguments(new QLineEdit(settings->m_arguments, this))
{
auto mainLayout = qobject_cast<QGridLayout *>(layout());
QTC_ASSERT(mainLayout, return);
const int baseRows = mainLayout->rowCount();
mainLayout->addWidget(new QLabel(tr("Executable:")), baseRows, 0);
mainLayout->addWidget(m_executable, baseRows, 1);
mainLayout->addWidget(new QLabel(tr("Arguments:")), baseRows + 1, 0);
m_executable->setExpectedKind(Utils::PathChooser::ExistingCommand);
m_executable->setPath(QDir::toNativeSeparators(settings->m_executable));
mainLayout->addWidget(m_arguments, baseRows + 1, 1);
auto chooser = new Utils::VariableChooser(this);
chooser->addSupportedWidget(m_arguments);
}
QString StdIOSettingsWidget::executable() const
{
return m_executable->filePath().toString();
}
QString StdIOSettingsWidget::arguments() const
{
return m_arguments->text();
}
bool LanguageFilter::isSupported(const Utils::FilePath &filePath, const QString &mimeType) const
{
if (mimeTypes.contains(mimeType))
return true;
if (filePattern.isEmpty() && filePath.isEmpty())
return mimeTypes.isEmpty();
const QRegularExpression::PatternOptions options
= Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive
? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
auto regexps = Utils::transform(filePattern, [&options](const QString &pattern){
return QRegularExpression(QRegularExpression::wildcardToRegularExpression(pattern),
options);
});
return Utils::anyOf(regexps, [filePath](const QRegularExpression &reg){
return reg.match(filePath.toString()).hasMatch()
|| reg.match(filePath.fileName()).hasMatch();
});
}
bool LanguageFilter::isSupported(const Core::IDocument *document) const
{
return isSupported(document->filePath(), document->mimeType());
}
bool LanguageFilter::operator==(const LanguageFilter &other) const
{
return this->filePattern == other.filePattern && this->mimeTypes == other.mimeTypes;
}
bool LanguageFilter::operator!=(const LanguageFilter &other) const
{
return this->filePattern != other.filePattern || this->mimeTypes != other.mimeTypes;
}
} // namespace LanguageClient