Commit 8566d2de authored by Mark Nauwelaerts's avatar Mark Nauwelaerts
Browse files

lspclient: show diagnostics on hover

Fixes issue #11
parent 5dd1fba9
......@@ -82,13 +82,16 @@ LSPClientConfigPage::LSPClientConfigPage(QWidget *parent, LSPClientPlugin *plugi
ui->chkRefDeclaration,
ui->chkDiagnostics,
ui->chkDiagnosticsMark,
ui->chkDiagnosticsHover,
ui->chkMessages,
ui->chkOnTypeFormatting,
ui->chkIncrementalSync,
ui->chkSemanticHighlighting,
ui->chkAutoHover})
connect(cb, &QCheckBox::toggled, this, &LSPClientConfigPage::changed);
connect(ui->comboMessagesSwitch, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int) { changed(); });
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);
connect(ui->edtConfigPath, &KUrlRequester::textChanged, this, &LSPClientConfigPage::configUrlChanged);
connect(ui->edtConfigPath, &KUrlRequester::urlSelected, this, &LSPClientConfigPage::configUrlChanged);
connect(ui->userConfig, &QTextEdit::textChanged, this, &LSPClientConfigPage::configTextChanged);
......@@ -98,6 +101,9 @@ LSPClientConfigPage::LSPClientConfigPage(QWidget *parent, LSPClientPlugin *plugi
bool enabled = ui->chkDiagnostics->isChecked();
ui->chkDiagnosticsHighlight->setEnabled(enabled);
ui->chkDiagnosticsMark->setEnabled(enabled);
ui->chkDiagnosticsHover->setEnabled(enabled);
enabled = enabled && ui->chkDiagnosticsHover->isChecked();
ui->spinDiagnosticsSize->setEnabled(enabled);
enabled = ui->chkMessages->isChecked();
ui->comboMessagesSwitch->setEnabled(enabled);
};
......@@ -137,6 +143,8 @@ void LSPClientConfigPage::apply()
m_plugin->m_diagnostics = ui->chkDiagnostics->isChecked();
m_plugin->m_diagnosticsHighlight = ui->chkDiagnosticsHighlight->isChecked();
m_plugin->m_diagnosticsMark = ui->chkDiagnosticsMark->isChecked();
m_plugin->m_diagnosticsHover = ui->chkDiagnosticsHover->isChecked();
m_plugin->m_diagnosticsSize = ui->spinDiagnosticsSize->value();
m_plugin->m_autoHover = ui->chkAutoHover->isChecked();
m_plugin->m_onTypeFormatting = ui->chkOnTypeFormatting->isChecked();
......@@ -173,6 +181,8 @@ void LSPClientConfigPage::reset()
ui->chkDiagnostics->setChecked(m_plugin->m_diagnostics);
ui->chkDiagnosticsHighlight->setChecked(m_plugin->m_diagnosticsHighlight);
ui->chkDiagnosticsMark->setChecked(m_plugin->m_diagnosticsMark);
ui->chkDiagnosticsHover->setChecked(m_plugin->m_diagnosticsHover);
ui->spinDiagnosticsSize->setValue(m_plugin->m_diagnosticsSize);
ui->chkAutoHover->setChecked(m_plugin->m_autoHover);
ui->chkOnTypeFormatting->setChecked(m_plugin->m_onTypeFormatting);
......
......@@ -49,6 +49,8 @@ static const QString CONFIG_INCREMENTAL_SYNC {QStringLiteral("IncrementalSync")}
static const QString CONFIG_DIAGNOSTICS {QStringLiteral("Diagnostics")};
static const QString CONFIG_DIAGNOSTICS_HIGHLIGHT {QStringLiteral("DiagnosticsHighlight")};
static const QString CONFIG_DIAGNOSTICS_MARK {QStringLiteral("DiagnosticsMark")};
static const QString CONFIG_DIAGNOSTICS_HOVER {QStringLiteral("DiagnosticsHover")};
static const QString CONFIG_DIAGNOSTICS_SIZE {QStringLiteral("DiagnosticsSize")};
static const QString CONFIG_MESSAGES {QStringLiteral("Messages")};
static const QString CONFIG_MESSAGES_AUTO_SWITCH {QStringLiteral("MessagesAutoSwitch")};
static const QString CONFIG_SERVER_CONFIG {QStringLiteral("ServerConfiguration")};
......@@ -116,6 +118,8 @@ void LSPClientPlugin::readConfig()
m_diagnostics = config.readEntry(CONFIG_DIAGNOSTICS, true);
m_diagnosticsHighlight = config.readEntry(CONFIG_DIAGNOSTICS_HIGHLIGHT, true);
m_diagnosticsMark = config.readEntry(CONFIG_DIAGNOSTICS_MARK, true);
m_diagnosticsHover = config.readEntry(CONFIG_DIAGNOSTICS_HOVER, true);
m_diagnosticsSize = config.readEntry(CONFIG_DIAGNOSTICS_SIZE, 1024);
m_messages = config.readEntry(CONFIG_MESSAGES, true);
m_messagesAutoSwitch = config.readEntry(CONFIG_MESSAGES_AUTO_SWITCH, 1);
m_configPath = config.readEntry(CONFIG_SERVER_CONFIG, QUrl());
......@@ -139,6 +143,8 @@ void LSPClientPlugin::writeConfig() const
config.writeEntry(CONFIG_DIAGNOSTICS, m_diagnostics);
config.writeEntry(CONFIG_DIAGNOSTICS_HIGHLIGHT, m_diagnosticsHighlight);
config.writeEntry(CONFIG_DIAGNOSTICS_MARK, m_diagnosticsMark);
config.writeEntry(CONFIG_DIAGNOSTICS_HOVER, m_diagnosticsHover);
config.writeEntry(CONFIG_DIAGNOSTICS_SIZE, m_diagnosticsSize);
config.writeEntry(CONFIG_MESSAGES, m_messages);
config.writeEntry(CONFIG_MESSAGES_AUTO_SWITCH, m_messagesAutoSwitch);
config.writeEntry(CONFIG_SERVER_CONFIG, m_configPath);
......
......@@ -63,6 +63,8 @@ public:
bool m_diagnostics = false;
bool m_diagnosticsHighlight = false;
bool m_diagnosticsMark = false;
bool m_diagnosticsHover = false;
unsigned m_diagnosticsSize = 0;
bool m_messages = false;
int m_messagesAutoSwitch = 0;
bool m_autoHover = false;
......
......@@ -219,6 +219,7 @@ class LSPClientActionView : public QObject
QScopedPointer<LSPClientViewTracker> m_viewTracker;
QScopedPointer<LSPClientCompletion> m_completion;
QScopedPointer<LSPClientHover> m_hover;
QScopedPointer<KTextEditor::TextHintProvider> m_forwardHover;
QScopedPointer<QObject> m_symbolView;
QPointer<QAction> m_findDef;
......@@ -236,6 +237,7 @@ class LSPClientActionView : public QObject
QPointer<QAction> m_diagnostics;
QPointer<QAction> m_diagnosticsHighlight;
QPointer<QAction> m_diagnosticsMark;
QPointer<QAction> m_diagnosticsHover;
QPointer<QAction> m_diagnosticsSwitch;
QPointer<QAction> m_messages;
QPointer<KSelectAction> m_messagesAutoSwitch;
......@@ -302,6 +304,23 @@ class LSPClientActionView : public QObject
return m_client->actionCollection();
}
// inner class that forwards directly to method for convenience
class ForwardingTextHintProvider : public KTextEditor::TextHintProvider
{
LSPClientActionView *m_parent;
public:
ForwardingTextHintProvider(LSPClientActionView *parent)
: m_parent(parent)
{
Q_ASSERT(m_parent);
}
virtual QString textHint(KTextEditor::View *view, const KTextEditor::Cursor &position) override
{
return m_parent->onTextHint(view, position);
}
};
public:
LSPClientActionView(LSPClientPlugin *plugin, KTextEditor::MainWindow *mainWin, KXMLGUIClient *client, QSharedPointer<LSPClientServerManager> serverManager)
: QObject(mainWin)
......@@ -311,6 +330,7 @@ public:
, m_serverManager(std::move(serverManager))
, m_completion(LSPClientCompletion::new_(m_serverManager))
, m_hover(LSPClientHover::new_(m_serverManager))
, m_forwardHover(new ForwardingTextHintProvider(this))
, m_symbolView(LSPClientSymbolView::new_(plugin, mainWin, m_serverManager))
{
connect(m_mainWindow, &KTextEditor::MainWindow::viewChanged, this, &self_type::updateState);
......@@ -362,6 +382,9 @@ public:
m_diagnosticsMark = actionCollection()->addAction(QStringLiteral("lspclient_diagnostics_mark"), this, &self_type::displayOptionChanged);
m_diagnosticsMark->setText(i18n("Show diagnostics marks"));
m_diagnosticsMark->setCheckable(true);
m_diagnosticsHover = actionCollection()->addAction(QStringLiteral("lspclient_diagnostics_hover"), this, &self_type::displayOptionChanged);
m_diagnosticsHover->setText(i18n("Show diagnostics on hover"));
m_diagnosticsHover->setCheckable(true);
m_diagnosticsSwitch = actionCollection()->addAction(QStringLiteral("lspclient_diagnostic_switch"), this, &self_type::switchToDiagnostics);
m_diagnosticsSwitch->setText(i18n("Switch to diagnostics tab"));
......@@ -404,6 +427,7 @@ public:
menu->addAction(m_diagnostics);
menu->addAction(m_diagnosticsHighlight);
menu->addAction(m_diagnosticsMark);
menu->addAction(m_diagnosticsHover);
menu->addAction(m_diagnosticsSwitch);
menu->addSeparator();
menu->addAction(m_messages);
......@@ -489,6 +513,7 @@ public:
{
m_diagnosticsHighlight->setEnabled(m_diagnostics->isChecked());
m_diagnosticsMark->setEnabled(m_diagnostics->isChecked());
m_diagnosticsHover->setEnabled(m_diagnostics->isChecked());
// messages tab should go first
int messagesIndex = m_tabWidget->indexOf(m_messagesView);
if (m_messages->isChecked() && m_messagesViewOwn) {
......@@ -533,6 +558,8 @@ public:
m_diagnosticsHighlight->setChecked(m_plugin->m_diagnosticsHighlight);
if (m_diagnosticsMark)
m_diagnosticsMark->setChecked(m_plugin->m_diagnosticsMark);
if (m_diagnosticsHover)
m_diagnosticsHover->setChecked(m_plugin->m_diagnosticsHover);
if (m_messages)
m_messages->setChecked(m_plugin->m_messages);
if (m_messagesAutoSwitch)
......@@ -1521,6 +1548,44 @@ public:
syncDiagnostics(currentView->document(), currentView->cursorPosition().line(), false, false);
}
QString onTextHint(KTextEditor::View *view, const KTextEditor::Cursor &position)
{
QString result;
auto document = view->document();
if (!m_diagnosticsTree || !m_diagnosticsModel || !document)
return result;
bool autoHover = m_autoHover && m_autoHover->isChecked();
bool diagHover = m_diagnostics && m_diagnostics->isChecked() && m_diagnosticsHover && m_diagnosticsHover->isChecked();
QStandardItem *topItem = diagHover ? getItem(*m_diagnosticsModel, document->url()) : nullptr;
QStandardItem *targetItem = getItem(topItem, position, false);
if (targetItem) {
result = targetItem->text();
// also include related info
int count = targetItem->rowCount();
for (int i = 0; i < count; ++i) {
auto item = targetItem->child(i);
result += QStringLiteral("\n<br>");
result += item->text();
}
// but let's not get carried away too far
const int maxsize = m_plugin->m_diagnosticsSize;
if (result.size() > maxsize) {
result.resize(maxsize);
result.append(QStringLiteral("..."));
}
} else if (autoHover) {
// only trigger generic hover if no diagnostic to show;
// avoids interference by generic hover info
// (which is likely not so useful in this case/position anyway)
result = m_hover->textHint(view, position);
}
return result;
}
KTextEditor::View *viewForUrl(const QUrl &url) const
{
for (auto *view : m_mainWindow->views()) {
......@@ -1875,8 +1940,10 @@ public:
updateCompletion(activeView, server.data());
// update hover with relevant server
m_hover->setServer(server);
updateHover(activeView, (m_autoHover && m_autoHover->isChecked()) ? server.data() : nullptr);
m_hover->setServer(server && server->capabilities().hoverProvider ? server : nullptr);
// need hover either for generic documentHover or for diagnostics
// so register anyway if server available and will sort out what to do/show later
updateHover(activeView, server.data());
// update marks if applicable
if (m_markModel && doc)
......@@ -1932,15 +1999,15 @@ public:
KTextEditor::TextHintInterface *cci = qobject_cast<KTextEditor::TextHintInterface *>(view);
Q_ASSERT(cci);
if (!registered && server && server->capabilities().hoverProvider) {
qCInfo(LSPCLIENT) << "registering cci";
cci->registerTextHintProvider(m_hover.data());
if (!registered && server) {
qCInfo(LSPCLIENT) << "registering thi";
cci->registerTextHintProvider(m_forwardHover.data());
m_hoverViews.insert(view);
}
if (registered && !server) {
qCInfo(LSPCLIENT) << "unregistering cci";
cci->unregisterTextHintProvider(m_hover.data());
qCInfo(LSPCLIENT) << "unregistering thi";
cci->unregisterTextHintProvider(m_forwardHover.data());
m_hoverViews.remove(view);
}
}
......
......@@ -106,6 +106,26 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkDiagnosticsHover">
<property name="text">
<string>On hover</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinDiagnosticsSize">
<property name="toolTip">
<string>max diagnostics tooltip size</string>
</property>
<property name="minimum">
<number>80</number>
</property>
<property name="maximum">
<number>10240</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kpartgui>
<gui name="lspclient" library="lspclient" version="6" translationDomain="lspclient">
<gui name="lspclient" library="lspclient" version="7" translationDomain="lspclient">
<MenuBar>
<Menu name="LSPClient Menubar">
<text>LSP Client</text>
......@@ -21,6 +21,7 @@
<Action name="lspclient_diagnostics"/>
<Action name="lspclient_diagnostics_highlight"/>
<Action name="lspclient_diagnostics_mark"/>
<Action name="lspclient_diagnostics_hover"/>
<Action name="lspclient_diagnostic_switch"/>
<Separator/>
<Action name="lspclient_messages"/>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment