lspclientconfigpage.cpp 9.04 KB
Newer Older
1 2
/*  SPDX-License-Identifier: MIT

3 4 5
    SPDX-FileCopyrightText: 2019 Mark Nauwelaerts <mark.nauwelaerts@gmail.com>

    SPDX-License-Identifier: MIT
6
*/
7 8 9

#include "lspclientconfigpage.h"
#include "lspclientplugin.h"
10
#include "ui_lspconfigwidget.h"
11 12 13

#include <KLocalizedString>

14 15 16 17
#include <KSyntaxHighlighting/Definition>
#include <KSyntaxHighlighting/SyntaxHighlighter>
#include <KSyntaxHighlighting/Theme>

18 19
#include <QJsonDocument>
#include <QJsonParseError>
20 21
#include <QPalette>

22
LSPClientConfigPage::LSPClientConfigPage(QWidget *parent, LSPClientPlugin *plugin)
23 24
    : KTextEditor::ConfigPage(parent)
    , m_plugin(plugin)
25
{
26 27
    ui = new Ui::LspConfigWidget();
    ui->setupUi(this);
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
    // fix-up our two text edits to be proper JSON file editors
    for (auto textEdit : {ui->userConfig, static_cast<QTextEdit *>(ui->defaultConfig)}) {
        // setup JSON highlighter for the default json stuff
        auto highlighter = new KSyntaxHighlighting::SyntaxHighlighter(textEdit->document());
        highlighter->setDefinition(m_repository.definitionForFileName(QStringLiteral("settings.json")));

        // we want mono-spaced font
        textEdit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));

        // we want to have the proper theme for the current palette
        const auto theme = (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme);
        auto pal = qApp->palette();
        if (theme.isValid()) {
            pal.setColor(QPalette::Base, theme.editorColor(KSyntaxHighlighting::Theme::BackgroundColor));
            pal.setColor(QPalette::Highlight, theme.editorColor(KSyntaxHighlighting::Theme::TextSelection));
        }
        textEdit->setPalette(pal);
        highlighter->setTheme(theme);
47 48
    }

49 50 51 52 53 54
    // setup default json settings
    QFile defaultConfigFile(QStringLiteral(":/lspclient/settings.json"));
    defaultConfigFile.open(QIODevice::ReadOnly);
    Q_ASSERT(defaultConfigFile.isOpen());
    ui->defaultConfig->setPlainText(QString::fromUtf8(defaultConfigFile.readAll()));

55 56 57
    // setup default config path as placeholder to show user where it is
    ui->edtConfigPath->setPlaceholderText(m_plugin->m_defaultConfigPath.toLocalFile());

58 59
    reset();

60 61 62 63 64 65 66 67
    for (const auto &cb : {ui->chkSymbolDetails,
                           ui->chkSymbolExpand,
                           ui->chkSymbolSort,
                           ui->chkSymbolTree,
                           ui->chkComplDoc,
                           ui->chkRefDeclaration,
                           ui->chkDiagnostics,
                           ui->chkDiagnosticsMark,
68
                           ui->chkDiagnosticsHover,
69
                           ui->chkMessages,
70 71
                           ui->chkOnTypeFormatting,
                           ui->chkIncrementalSync,
72
                           ui->chkSemanticHighlighting,
73
                           ui->chkAutoHover})
74
        connect(cb, &QCheckBox::toggled, this, &LSPClientConfigPage::changed);
75 76 77
    auto ch = [this](int) { this->changed(); };
    connect(ui->comboMessagesSwitch, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, ch);
    connect(ui->spinDiagnosticsSize, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, ch);
78 79
    connect(ui->edtConfigPath, &KUrlRequester::textChanged, this, &LSPClientConfigPage::configUrlChanged);
    connect(ui->edtConfigPath, &KUrlRequester::urlSelected, this, &LSPClientConfigPage::configUrlChanged);
80 81 82 83 84 85 86 87 88

    auto cfgh = [this](int position, int added, int removed) {
        Q_UNUSED(position);
        // discard format change
        // (e.g. due to syntax highlighting)
        if (added || removed)
            configTextChanged();
    };
    connect(ui->userConfig->document(), &QTextDocument::contentsChange, this, cfgh);
89 90

    // custom control logic
91
    auto h = [this]() {
92 93 94
        bool enabled = ui->chkDiagnostics->isChecked();
        ui->chkDiagnosticsHighlight->setEnabled(enabled);
        ui->chkDiagnosticsMark->setEnabled(enabled);
95 96 97
        ui->chkDiagnosticsHover->setEnabled(enabled);
        enabled = enabled && ui->chkDiagnosticsHover->isChecked();
        ui->spinDiagnosticsSize->setEnabled(enabled);
98 99
        enabled = ui->chkMessages->isChecked();
        ui->comboMessagesSwitch->setEnabled(enabled);
100 101
    };
    connect(this, &LSPClientConfigPage::changed, this, h);
102 103
}

104 105 106 107 108
LSPClientConfigPage::~LSPClientConfigPage()
{
    delete ui;
}

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
QString LSPClientConfigPage::name() const
{
    return QString(i18n("LSP Client"));
}

QString LSPClientConfigPage::fullName() const
{
    return QString(i18n("LSP Client"));
}

QIcon LSPClientConfigPage::icon() const
{
    return QIcon::fromTheme(QLatin1String("code-context"));
}

void LSPClientConfigPage::apply()
{
126 127 128 129
    m_plugin->m_symbolDetails = ui->chkSymbolDetails->isChecked();
    m_plugin->m_symbolTree = ui->chkSymbolTree->isChecked();
    m_plugin->m_symbolExpand = ui->chkSymbolExpand->isChecked();
    m_plugin->m_symbolSort = ui->chkSymbolSort->isChecked();
130

131 132
    m_plugin->m_complDoc = ui->chkComplDoc->isChecked();
    m_plugin->m_refDeclaration = ui->chkRefDeclaration->isChecked();
133

134 135 136
    m_plugin->m_diagnostics = ui->chkDiagnostics->isChecked();
    m_plugin->m_diagnosticsHighlight = ui->chkDiagnosticsHighlight->isChecked();
    m_plugin->m_diagnosticsMark = ui->chkDiagnosticsMark->isChecked();
137 138
    m_plugin->m_diagnosticsHover = ui->chkDiagnosticsHover->isChecked();
    m_plugin->m_diagnosticsSize = ui->spinDiagnosticsSize->value();
139

140 141 142
    m_plugin->m_autoHover = ui->chkAutoHover->isChecked();
    m_plugin->m_onTypeFormatting = ui->chkOnTypeFormatting->isChecked();
    m_plugin->m_incrementalSync = ui->chkIncrementalSync->isChecked();
143
    m_plugin->m_semanticHighlighting = ui->chkSemanticHighlighting->isChecked();
144

145 146 147
    m_plugin->m_messages = ui->chkMessages->isChecked();
    m_plugin->m_messagesAutoSwitch = ui->comboMessagesSwitch->currentIndex();

148
    m_plugin->m_configPath = ui->edtConfigPath->url();
149

150 151 152 153 154 155 156 157 158
    // own scope to ensure file is flushed before we signal below in writeConfig!
    {
        QFile configFile(m_plugin->configPath().toLocalFile());
        configFile.open(QIODevice::WriteOnly);
        if (configFile.isOpen()) {
            configFile.write(ui->userConfig->toPlainText().toUtf8());
        }
    }

159 160 161 162 163
    m_plugin->writeConfig();
}

void LSPClientConfigPage::reset()
{
164 165 166 167
    ui->chkSymbolDetails->setChecked(m_plugin->m_symbolDetails);
    ui->chkSymbolTree->setChecked(m_plugin->m_symbolTree);
    ui->chkSymbolExpand->setChecked(m_plugin->m_symbolExpand);
    ui->chkSymbolSort->setChecked(m_plugin->m_symbolSort);
168

169 170
    ui->chkComplDoc->setChecked(m_plugin->m_complDoc);
    ui->chkRefDeclaration->setChecked(m_plugin->m_refDeclaration);
171

172 173 174
    ui->chkDiagnostics->setChecked(m_plugin->m_diagnostics);
    ui->chkDiagnosticsHighlight->setChecked(m_plugin->m_diagnosticsHighlight);
    ui->chkDiagnosticsMark->setChecked(m_plugin->m_diagnosticsMark);
175 176
    ui->chkDiagnosticsHover->setChecked(m_plugin->m_diagnosticsHover);
    ui->spinDiagnosticsSize->setValue(m_plugin->m_diagnosticsSize);
177

178 179 180
    ui->chkAutoHover->setChecked(m_plugin->m_autoHover);
    ui->chkOnTypeFormatting->setChecked(m_plugin->m_onTypeFormatting);
    ui->chkIncrementalSync->setChecked(m_plugin->m_incrementalSync);
181
    ui->chkSemanticHighlighting->setChecked(m_plugin->m_semanticHighlighting);
182

183 184 185
    ui->chkMessages->setChecked(m_plugin->m_messages);
    ui->comboMessagesSwitch->setCurrentIndex(m_plugin->m_messagesAutoSwitch);

186
    ui->edtConfigPath->setUrl(m_plugin->m_configPath);
187 188

    readUserConfig(m_plugin->configPath().toLocalFile());
189 190 191 192 193 194
}

void LSPClientConfigPage::defaults()
{
    reset();
}
195 196 197 198 199 200 201 202 203 204

void LSPClientConfigPage::readUserConfig(const QString &fileName)
{
    QFile configFile(fileName);
    configFile.open(QIODevice::ReadOnly);
    if (configFile.isOpen()) {
        ui->userConfig->setPlainText(QString::fromUtf8(configFile.readAll()));
    } else {
        ui->userConfig->clear();
    }
205 206 207 208 209 210 211 212 213 214 215 216 217

    updateConfigTextErrorState();
}

void LSPClientConfigPage::updateConfigTextErrorState()
{
    const auto data = ui->userConfig->toPlainText().toUtf8();
    if (data.isEmpty()) {
        ui->userConfigError->setText(i18n("No JSON data to validate."));
        return;
    }

    // check json validity
Christoph Cullmann's avatar
Christoph Cullmann committed
218
    QJsonParseError error {};
219
    auto json = QJsonDocument::fromJson(data, &error);
220
    if (error.error == QJsonParseError::NoError) {
221 222 223 224 225
        if (json.isObject()) {
            ui->userConfigError->setText(i18n("JSON data is valid."));
        } else {
            ui->userConfigError->setText(i18n("JSON data is invalid: no JSON object"));
        }
226 227 228 229 230 231 232 233 234 235 236 237
    } else {
        ui->userConfigError->setText(i18n("JSON data is invalid: %1", error.errorString()));
    }
}

void LSPClientConfigPage::configTextChanged()
{
    // check for errors
    updateConfigTextErrorState();

    // remember changed
    changed();
238 239 240 241 242 243 244 245 246 247
}

void LSPClientConfigPage::configUrlChanged()
{
    // re-read config
    readUserConfig(ui->edtConfigPath->url().isEmpty() ? m_plugin->m_defaultConfigPath.toLocalFile() : ui->edtConfigPath->url().toLocalFile());

    // remember changed
    changed();
}