forked from qt-creator/qt-creator
LSP: Add setting to provide initializationOptions to the language server
When the language server is initialized, the Initialize request can contain user provided data in initializationOptions field. Allow the user to set data inside. This can be required to let the language server have some context. Change-Id: Ib057fdb940c21b3fd032853fb84253d42ad1e321 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -250,6 +250,7 @@ void Client::initialize()
|
|||||||
InitializeRequest initRequest;
|
InitializeRequest initRequest;
|
||||||
auto params = initRequest.params().value_or(InitializeParams());
|
auto params = initRequest.params().value_or(InitializeParams());
|
||||||
params.setCapabilities(generateClientCapabilities());
|
params.setCapabilities(generateClientCapabilities());
|
||||||
|
params.setInitializationOptions(m_initializationOptions);
|
||||||
if (m_project) {
|
if (m_project) {
|
||||||
params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory()));
|
params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory()));
|
||||||
params.setWorkSpaceFolders(Utils::transform(SessionManager::projects(), [](Project *pro){
|
params.setWorkSpaceFolders(Utils::transform(SessionManager::projects(), [](Project *pro){
|
||||||
@@ -879,6 +880,11 @@ void Client::setSupportedLanguage(const LanguageFilter &filter)
|
|||||||
m_languagFilter = filter;
|
m_languagFilter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::setInitializationOptions(const QJsonObject &initializationOptions)
|
||||||
|
{
|
||||||
|
m_initializationOptions = initializationOptions;
|
||||||
|
}
|
||||||
|
|
||||||
bool Client::isSupportedDocument(const TextEditor::TextDocument *document) const
|
bool Client::isSupportedDocument(const TextEditor::TextDocument *document) const
|
||||||
{
|
{
|
||||||
QTC_ASSERT(document, return false);
|
QTC_ASSERT(document, return false);
|
||||||
@@ -900,7 +906,8 @@ bool Client::needsRestart(const BaseSettings *settings) const
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(settings, return false);
|
QTC_ASSERT(settings, return false);
|
||||||
return m_languagFilter.mimeTypes != settings->m_languageFilter.mimeTypes
|
return m_languagFilter.mimeTypes != settings->m_languageFilter.mimeTypes
|
||||||
|| m_languagFilter.filePattern != settings->m_languageFilter.filePattern;
|
|| m_languagFilter.filePattern != settings->m_languageFilter.filePattern
|
||||||
|
|| m_initializationOptions != settings->initializationOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Diagnostic> Client::diagnosticsAt(const DocumentUri &uri, const Range &range) const
|
QList<Diagnostic> Client::diagnosticsAt(const DocumentUri &uri, const Range &range) const
|
||||||
|
@@ -136,6 +136,7 @@ public:
|
|||||||
void cancelRequest(const LanguageServerProtocol::MessageId &id);
|
void cancelRequest(const LanguageServerProtocol::MessageId &id);
|
||||||
|
|
||||||
void setSupportedLanguage(const LanguageFilter &filter);
|
void setSupportedLanguage(const LanguageFilter &filter);
|
||||||
|
void setInitializationOptions(const QJsonObject& initializationOptions);
|
||||||
bool isSupportedDocument(const TextEditor::TextDocument *document) const;
|
bool isSupportedDocument(const TextEditor::TextDocument *document) const;
|
||||||
bool isSupportedFile(const Utils::FilePath &filePath, const QString &mimeType) const;
|
bool isSupportedFile(const Utils::FilePath &filePath, const QString &mimeType) const;
|
||||||
bool isSupportedUri(const LanguageServerProtocol::DocumentUri &uri) const;
|
bool isSupportedUri(const LanguageServerProtocol::DocumentUri &uri) const;
|
||||||
@@ -210,6 +211,7 @@ private:
|
|||||||
QHash<QByteArray, ContentHandler> m_contentHandler;
|
QHash<QByteArray, ContentHandler> m_contentHandler;
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
LanguageFilter m_languagFilter;
|
LanguageFilter m_languagFilter;
|
||||||
|
QJsonObject m_initializationOptions;
|
||||||
QMap<TextEditor::TextDocument *, QString> m_openedDocument;
|
QMap<TextEditor::TextDocument *, QString> m_openedDocument;
|
||||||
Core::Id m_id;
|
Core::Id m_id;
|
||||||
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
|
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
|
||||||
|
@@ -52,6 +52,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
|
#include <QJsonDocument>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
@@ -68,6 +69,7 @@ constexpr char enabledKey[] = "enabled";
|
|||||||
constexpr char startupBehaviorKey[] = "startupBehavior";
|
constexpr char startupBehaviorKey[] = "startupBehavior";
|
||||||
constexpr char mimeTypeKey[] = "mimeType";
|
constexpr char mimeTypeKey[] = "mimeType";
|
||||||
constexpr char filePatternKey[] = "filePattern";
|
constexpr char filePatternKey[] = "filePattern";
|
||||||
|
constexpr char initializationOptionsKey[] = "initializationOptions";
|
||||||
constexpr char executableKey[] = "executable";
|
constexpr char executableKey[] = "executable";
|
||||||
constexpr char argumentsKey[] = "arguments";
|
constexpr char argumentsKey[] = "arguments";
|
||||||
constexpr char settingsGroupKey[] = "LanguageClient";
|
constexpr char settingsGroupKey[] = "LanguageClient";
|
||||||
@@ -465,12 +467,19 @@ QModelIndex LanguageClientSettingsModel::indexForSetting(BaseSettings *setting)
|
|||||||
return index < 0 ? QModelIndex() : createIndex(index, 0, setting);
|
return index < 0 ? QModelIndex() : createIndex(index, 0, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonObject BaseSettings::initializationOptions() const
|
||||||
|
{
|
||||||
|
return QJsonDocument::fromJson(Utils::globalMacroExpander()->
|
||||||
|
expand(m_initializationOptions).toUtf8()).object();
|
||||||
|
}
|
||||||
|
|
||||||
void BaseSettings::applyFromSettingsWidget(QWidget *widget)
|
void BaseSettings::applyFromSettingsWidget(QWidget *widget)
|
||||||
{
|
{
|
||||||
if (auto settingsWidget = qobject_cast<BaseSettingsWidget *>(widget)) {
|
if (auto settingsWidget = qobject_cast<BaseSettingsWidget *>(widget)) {
|
||||||
m_name = settingsWidget->name();
|
m_name = settingsWidget->name();
|
||||||
m_languageFilter = settingsWidget->filter();
|
m_languageFilter = settingsWidget->filter();
|
||||||
m_startBehavior = settingsWidget->startupBehavior();
|
m_startBehavior = settingsWidget->startupBehavior();
|
||||||
|
m_initializationOptions = settingsWidget->initializationOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +514,7 @@ Client *BaseSettings::createClient()
|
|||||||
auto *client = new Client(interface);
|
auto *client = new Client(interface);
|
||||||
client->setName(Utils::globalMacroExpander()->expand(m_name));
|
client->setName(Utils::globalMacroExpander()->expand(m_name));
|
||||||
client->setSupportedLanguage(m_languageFilter);
|
client->setSupportedLanguage(m_languageFilter);
|
||||||
|
client->setInitializationOptions(initializationOptions());
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,6 +527,7 @@ QVariantMap BaseSettings::toMap() const
|
|||||||
map.insert(startupBehaviorKey, m_startBehavior);
|
map.insert(startupBehaviorKey, m_startBehavior);
|
||||||
map.insert(mimeTypeKey, m_languageFilter.mimeTypes);
|
map.insert(mimeTypeKey, m_languageFilter.mimeTypes);
|
||||||
map.insert(filePatternKey, m_languageFilter.filePattern);
|
map.insert(filePatternKey, m_languageFilter.filePattern);
|
||||||
|
map.insert(initializationOptionsKey, m_initializationOptions);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,6 +541,7 @@ void BaseSettings::fromMap(const QVariantMap &map)
|
|||||||
m_languageFilter.mimeTypes = map[mimeTypeKey].toStringList();
|
m_languageFilter.mimeTypes = map[mimeTypeKey].toStringList();
|
||||||
m_languageFilter.filePattern = map[filePatternKey].toStringList();
|
m_languageFilter.filePattern = map[filePatternKey].toStringList();
|
||||||
m_languageFilter.filePattern.removeAll({}); // remove empty entries
|
m_languageFilter.filePattern.removeAll({}); // remove empty entries
|
||||||
|
m_initializationOptions = map[initializationOptionsKey].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static LanguageClientSettingsPage &settingsPage()
|
static LanguageClientSettingsPage &settingsPage()
|
||||||
@@ -706,13 +718,16 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa
|
|||||||
, m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this))
|
, m_mimeTypes(new QLabel(settings->m_languageFilter.mimeTypes.join(filterSeparator), this))
|
||||||
, m_filePattern(new QLineEdit(settings->m_languageFilter.filePattern.join(filterSeparator), this))
|
, m_filePattern(new QLineEdit(settings->m_languageFilter.filePattern.join(filterSeparator), this))
|
||||||
, m_startupBehavior(new QComboBox)
|
, m_startupBehavior(new QComboBox)
|
||||||
|
, m_initializationOptions(new Utils::FancyLineEdit(this))
|
||||||
{
|
{
|
||||||
int row = 0;
|
int row = 0;
|
||||||
auto *mainLayout = new QGridLayout;
|
auto *mainLayout = new QGridLayout;
|
||||||
|
|
||||||
mainLayout->addWidget(new QLabel(tr("Name:")), row, 0);
|
mainLayout->addWidget(new QLabel(tr("Name:")), row, 0);
|
||||||
mainLayout->addWidget(m_name, row, 1);
|
mainLayout->addWidget(m_name, row, 1);
|
||||||
auto chooser = new Core::VariableChooser(this);
|
auto chooser = new Core::VariableChooser(this);
|
||||||
chooser->addSupportedWidget(m_name);
|
chooser->addSupportedWidget(m_name);
|
||||||
|
|
||||||
mainLayout->addWidget(new QLabel(tr("Language:")), ++row, 0);
|
mainLayout->addWidget(new QLabel(tr("Language:")), ++row, 0);
|
||||||
auto mimeLayout = new QHBoxLayout;
|
auto mimeLayout = new QHBoxLayout;
|
||||||
mimeLayout->addWidget(m_mimeTypes);
|
mimeLayout->addWidget(m_mimeTypes);
|
||||||
@@ -722,6 +737,7 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa
|
|||||||
mainLayout->addLayout(mimeLayout, row, 1);
|
mainLayout->addLayout(mimeLayout, row, 1);
|
||||||
m_filePattern->setPlaceholderText(tr("File pattern"));
|
m_filePattern->setPlaceholderText(tr("File pattern"));
|
||||||
mainLayout->addWidget(m_filePattern, ++row, 1);
|
mainLayout->addWidget(m_filePattern, ++row, 1);
|
||||||
|
|
||||||
mainLayout->addWidget(new QLabel(tr("Startup behavior:")), ++row, 0);
|
mainLayout->addWidget(new QLabel(tr("Startup behavior:")), ++row, 0);
|
||||||
for (int behavior = 0; behavior < BaseSettings::LastSentinel ; ++behavior)
|
for (int behavior = 0; behavior < BaseSettings::LastSentinel ; ++behavior)
|
||||||
m_startupBehavior->addItem(startupBehaviorString(BaseSettings::StartBehavior(behavior)));
|
m_startupBehavior->addItem(startupBehaviorString(BaseSettings::StartBehavior(behavior)));
|
||||||
@@ -757,6 +773,33 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa
|
|||||||
mainLayout->addWidget(createCapabilitiesView(QJsonValue(capabilities)), row, 1);
|
mainLayout->addWidget(createCapabilitiesView(QJsonValue(capabilities)), row, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
setLayout(mainLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -776,6 +819,11 @@ BaseSettings::StartBehavior BaseSettingsWidget::startupBehavior() const
|
|||||||
return BaseSettings::StartBehavior(m_startupBehavior->currentIndex());
|
return BaseSettings::StartBehavior(m_startupBehavior->currentIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString BaseSettingsWidget::initializationOptions() const
|
||||||
|
{
|
||||||
|
return m_initializationOptions->text();
|
||||||
|
}
|
||||||
|
|
||||||
class MimeTypeModel : public QStringListModel
|
class MimeTypeModel : public QStringListModel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
@@ -45,6 +46,7 @@ QT_END_NAMESPACE
|
|||||||
namespace Utils {
|
namespace Utils {
|
||||||
class FilePath;
|
class FilePath;
|
||||||
class PathChooser;
|
class PathChooser;
|
||||||
|
class FancyLineEdit;
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|
||||||
namespace Core { class IDocument; }
|
namespace Core { class IDocument; }
|
||||||
@@ -82,6 +84,9 @@ public:
|
|||||||
bool m_enabled = true;
|
bool m_enabled = true;
|
||||||
StartBehavior m_startBehavior = RequiresFile;
|
StartBehavior m_startBehavior = RequiresFile;
|
||||||
LanguageFilter m_languageFilter;
|
LanguageFilter m_languageFilter;
|
||||||
|
QString m_initializationOptions;
|
||||||
|
|
||||||
|
QJsonObject initializationOptions() const;
|
||||||
|
|
||||||
virtual void applyFromSettingsWidget(QWidget *widget);
|
virtual void applyFromSettingsWidget(QWidget *widget);
|
||||||
virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const;
|
virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const;
|
||||||
@@ -155,6 +160,7 @@ public:
|
|||||||
BaseSettings::StartBehavior startupBehavior() const;
|
BaseSettings::StartBehavior startupBehavior() const;
|
||||||
bool alwaysOn() const;
|
bool alwaysOn() const;
|
||||||
bool requiresProject() const;
|
bool requiresProject() const;
|
||||||
|
QString initializationOptions() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void showAddMimeTypeDialog();
|
void showAddMimeTypeDialog();
|
||||||
@@ -163,6 +169,7 @@ private:
|
|||||||
QLabel *m_mimeTypes = nullptr;
|
QLabel *m_mimeTypes = nullptr;
|
||||||
QLineEdit *m_filePattern = nullptr;
|
QLineEdit *m_filePattern = nullptr;
|
||||||
QComboBox *m_startupBehavior = nullptr;
|
QComboBox *m_startupBehavior = nullptr;
|
||||||
|
Utils::FancyLineEdit *m_initializationOptions = nullptr;
|
||||||
|
|
||||||
static constexpr char filterSeparator = ';';
|
static constexpr char filterSeparator = ';';
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user