LanguageClient: compress documentHighlight requests

Change-Id: I98707a61a228c66966c72f6b4da2470b6f53820e
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2021-03-12 13:49:13 +01:00
parent 85533cce0f
commit c218f77f7e
3 changed files with 108 additions and 69 deletions

View File

@@ -140,6 +140,8 @@ Client::~Client()
} }
for (IAssistProcessor *processor : qAsConst(m_runningAssistProcessors)) for (IAssistProcessor *processor : qAsConst(m_runningAssistProcessors))
processor->setAsyncProposalAvailable(nullptr); processor->setAsyncProposalAvailable(nullptr);
qDeleteAll(m_documentHighlightsTimer);
m_documentHighlightsTimer.clear();
updateEditorToolBar(m_openedDocument.keys()); updateEditorToolBar(m_openedDocument.keys());
// do not handle messages while shutting down // do not handle messages while shutting down
disconnect(m_clientInterface.data(), &BaseClientInterface::messageReceived, disconnect(m_clientInterface.data(), &BaseClientInterface::messageReceived,
@@ -431,6 +433,64 @@ void Client::updateFunctionHintProvider(TextEditor::TextDocument *document)
} }
} }
void Client::requestDocumentHighlights(TextEditor::TextEditorWidget *widget)
{
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
TextDocumentRegistrationOptions option(
m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName));
if (!option.filterApplies(widget->textDocument()->filePath()))
return;
} else {
Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> provider
= m_serverCapabilities.documentHighlightProvider();
if (!provider.has_value())
return;
if (Utils::holds_alternative<bool>(*provider) && !Utils::get<bool>(*provider))
return;
}
auto runningRequest = m_highlightRequests.find(uri);
if (runningRequest != m_highlightRequests.end())
cancelRequest(runningRequest.value());
DocumentHighlightsRequest request(
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor())));
request.setResponseCallback(
[widget = QPointer<TextEditor::TextEditorWidget>(widget), this, uri]
(const DocumentHighlightsRequest::Response &response)
{
m_highlightRequests.remove(uri);
if (!widget)
return;
const Id &id = TextEditor::TextEditorWidget::CodeSemanticsSelection;
QList<QTextEdit::ExtraSelection> selections;
const Utils::optional<DocumentHighlightsResult> &result = response.result();
if (!result.has_value() || holds_alternative<std::nullptr_t>(result.value())) {
widget->setExtraSelections(id, selections);
return;
}
const QTextCharFormat &format =
widget->textDocument()->fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES);
QTextDocument *document = widget->document();
for (const auto &highlight : get<QList<DocumentHighlight>>(result.value())) {
QTextEdit::ExtraSelection selection{widget->textCursor(), format};
const int &start = highlight.range().start().toPositionInDocument(document);
const int &end = highlight.range().end().toPositionInDocument(document);
if (start < 0 || end < 0)
continue;
selection.cursor.setPosition(start);
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
selections << selection;
}
widget->setExtraSelections(id, selections);
});
m_highlightRequests[uri] = request.id();
sendContent(request);
}
void Client::activateDocument(TextEditor::TextDocument *document) void Client::activateDocument(TextEditor::TextDocument *document)
{ {
auto uri = DocumentUri::fromFilePath(document->filePath()); auto uri = DocumentUri::fromFilePath(document->filePath());
@@ -447,11 +507,11 @@ void Client::activateDocument(TextEditor::TextDocument *document)
for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) { for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) {
updateEditorToolBar(editor); updateEditorToolBar(editor);
if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) { if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) {
textEditor->editorWidget()->addHoverHandler(&m_hoverHandler); TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
if (symbolSupport().supportsRename(document)) { widget->addHoverHandler(&m_hoverHandler);
textEditor->editorWidget()->addOptionalActions( requestDocumentHighlights(widget);
TextEditor::TextEditorActionHandler::RenameSymbol); if (symbolSupport().supportsRename(document))
} widget->addOptionalActions(TextEditor::TextEditorActionHandler::RenameSymbol);
} }
} }
} }
@@ -466,8 +526,11 @@ void Client::deactivateDocument(TextEditor::TextDocument *document)
highlighter->clearAllExtraFormats(); highlighter->clearAllExtraFormats();
} }
for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) { for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(document)) {
if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor)) {
textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler); TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
widget->removeHoverHandler(&m_hoverHandler);
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, {});
}
} }
} }
@@ -629,61 +692,38 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation
void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget) void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
{ {
if (m_documentsToUpdate.contains(widget->textDocument())) TextEditor::TextDocument *document = widget->textDocument();
if (m_documentsToUpdate.contains(document))
return; // we are currently changing this document so postpone the DocumentHighlightsRequest return; // we are currently changing this document so postpone the DocumentHighlightsRequest
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath()); QTimer *timer = m_documentHighlightsTimer[widget];
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) { if (!timer) {
TextDocumentRegistrationOptions option( const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
m_dynamicCapabilities.option(DocumentHighlightsRequest::methodName)); auto runningRequest = m_highlightRequests.find(uri);
if (!option.filterApplies(widget->textDocument()->filePath())) if (runningRequest != m_highlightRequests.end())
return; cancelRequest(runningRequest.value());
} else { timer = new QTimer;
Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> provider timer->setSingleShot(true);
= m_serverCapabilities.documentHighlightProvider(); m_documentHighlightsTimer.insert(widget, timer);
if (!provider.has_value()) connect(timer, &QTimer::timeout, this, [this, widget]() {
return; requestDocumentHighlights(widget);
if (Utils::holds_alternative<bool>(*provider) && !Utils::get<bool>(*provider)) m_documentHighlightsTimer.take(widget)->deleteLater();
return; });
connect(widget, &QWidget::destroyed, this, [widget, this]() {
delete m_documentHighlightsTimer.take(widget);
});
} }
const Id selectionsId(TextEditor::TextEditorWidget::CodeSemanticsSelection);
auto runningRequest = m_highlightRequests.find(uri); const QList semanticSelections = widget->extraSelections(selectionsId);
if (runningRequest != m_highlightRequests.end()) if (!semanticSelections.isEmpty()) {
cancelRequest(runningRequest.value()); auto selectionContainsPos =
[pos = widget->position()](const QTextEdit::ExtraSelection &selection) {
DocumentHighlightsRequest request( const QTextCursor cursor = selection.cursor;
TextDocumentPositionParams(TextDocumentIdentifier(uri), Position(widget->textCursor()))); return cursor.selectionStart() <= pos && cursor.selectionEnd() >= pos;
request.setResponseCallback( };
[widget = QPointer<TextEditor::TextEditorWidget>(widget), this, uri] if (!Utils::anyOf(semanticSelections, selectionContainsPos))
(DocumentHighlightsRequest::Response response) widget->setExtraSelections(selectionsId, {});
{ }
m_highlightRequests.remove(uri); timer->start(250);
if (!widget)
return;
QList<QTextEdit::ExtraSelection> selections;
const DocumentHighlightsResult result = response.result().value_or(DocumentHighlightsResult());
if (!holds_alternative<QList<DocumentHighlight>>(result)) {
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, selections);
return;
}
const QTextCharFormat &format =
widget->textDocument()->fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES);
QTextDocument *document = widget->document();
for (const auto &highlight : get<QList<DocumentHighlight>>(result)) {
QTextEdit::ExtraSelection selection{widget->textCursor(), format};
const int &start = highlight.range().start().toPositionInDocument(document);
const int &end = highlight.range().end().toPositionInDocument(document);
if (start < 0 || end < 0)
continue;
selection.cursor.setPosition(start);
selection.cursor.setPosition(end, QTextCursor::KeepAnchor);
selections << selection;
}
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, selections);
});
m_highlightRequests[uri] = request.id();
sendContent(request);
} }
SymbolSupport &Client::symbolSupport() SymbolSupport &Client::symbolSupport()
@@ -887,6 +927,8 @@ bool Client::reset()
for (TextEditor::IAssistProcessor *processor : qAsConst(m_runningAssistProcessors)) for (TextEditor::IAssistProcessor *processor : qAsConst(m_runningAssistProcessors))
processor->setAsyncProposalAvailable(nullptr); processor->setAsyncProposalAvailable(nullptr);
m_runningAssistProcessors.clear(); m_runningAssistProcessors.clear();
qDeleteAll(m_documentHighlightsTimer);
m_documentHighlightsTimer.clear();
return true; return true;
} }

View File

@@ -197,6 +197,7 @@ private:
void updateCompletionProvider(TextEditor::TextDocument *document); void updateCompletionProvider(TextEditor::TextDocument *document);
void updateFunctionHintProvider(TextEditor::TextDocument *document); void updateFunctionHintProvider(TextEditor::TextDocument *document);
void requestDocumentHighlights(TextEditor::TextEditorWidget *widget);
void rehighlight(); void rehighlight();
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &, using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
@@ -214,6 +215,7 @@ private:
QMap<TextEditor::TextDocument *, QMap<TextEditor::TextDocument *,
QList<LanguageServerProtocol::DidChangeTextDocumentParams::TextDocumentContentChangeEvent>> QList<LanguageServerProtocol::DidChangeTextDocumentParams::TextDocumentContentChangeEvent>>
m_documentsToUpdate; m_documentsToUpdate;
QMap<TextEditor::TextEditorWidget *, QTimer *> m_documentHighlightsTimer;
QTimer m_documentUpdateTimer; QTimer m_documentUpdateTimer;
Utils::Id m_id; Utils::Id m_id;
LanguageServerProtocol::ServerCapabilities m_serverCapabilities; LanguageServerProtocol::ServerCapabilities m_serverCapabilities;

View File

@@ -432,15 +432,10 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor)
if (auto client = clientForDocument(document)) if (auto client = clientForDocument(document))
client->symbolSupport().renameSymbol(document, cursor); client->symbolSupport().renameSymbol(document, cursor);
}); });
connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget]() { connect(widget, &TextEditorWidget::cursorPositionChanged, this, [widget]() {
// TODO This would better be a compressing timer if (Client *client = clientForDocument(widget->textDocument()))
QTimer::singleShot(50, this, [widget = QPointer<TextEditorWidget>(widget)]() { if (client->reachable())
if (!widget) client->cursorPositionChanged(widget);
return;
if (Client *client = clientForDocument(widget->textDocument()))
if (client->reachable())
client->cursorPositionChanged(widget);
});
}); });
updateEditorToolBar(editor); updateEditorToolBar(editor);
if (TextEditor::TextDocument *document = textEditor->textDocument()) { if (TextEditor::TextDocument *document = textEditor->textDocument()) {