forked from qt-creator/qt-creator
LSP: Compress document change notifications
Change-Id: Iaf6cb99784f4e1ed1291ace1f4cc18cf6af88672 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -107,6 +107,10 @@ Client::Client(BaseClientInterface *clientInterface)
|
|||||||
m_clientProviders.functionHintProvider = new FunctionHintAssistProvider(this);
|
m_clientProviders.functionHintProvider = new FunctionHintAssistProvider(this);
|
||||||
m_clientProviders.quickFixAssistProvider = new LanguageClientQuickFixProvider(this);
|
m_clientProviders.quickFixAssistProvider = new LanguageClientQuickFixProvider(this);
|
||||||
|
|
||||||
|
m_documentUpdateTimer.setSingleShot(true);
|
||||||
|
m_documentUpdateTimer.setInterval(500);
|
||||||
|
connect(&m_documentUpdateTimer, &QTimer::timeout, this, &Client::sendPostponedDocumentUpdates);
|
||||||
|
|
||||||
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
|
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
|
||||||
&JsonRpcMessageHandler::parseContent);
|
&JsonRpcMessageHandler::parseContent);
|
||||||
QTC_ASSERT(clientInterface, return);
|
QTC_ASSERT(clientInterface, return);
|
||||||
@@ -347,6 +351,7 @@ void Client::sendContent(const IContent &content)
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(m_clientInterface, return);
|
QTC_ASSERT(m_clientInterface, return);
|
||||||
QTC_ASSERT(m_state == Initialized, return);
|
QTC_ASSERT(m_state == Initialized, return);
|
||||||
|
sendPostponedDocumentUpdates();
|
||||||
content.registerResponseHandler(&m_responseHandlers);
|
content.registerResponseHandler(&m_responseHandlers);
|
||||||
QString error;
|
QString error;
|
||||||
if (!QTC_GUARD(content.isValid(&error)))
|
if (!QTC_GUARD(content.isValid(&error)))
|
||||||
@@ -514,15 +519,8 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document,
|
|||||||
syncKind = option.isValid(nullptr) ? option.syncKind() : syncKind;
|
syncKind = option.isValid(nullptr) ? option.syncKind() : syncKind;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
|
|
||||||
|
|
||||||
const auto uri = DocumentUri::fromFilePath(document->filePath());
|
|
||||||
m_highlights[uri].clear();
|
|
||||||
if (syncKind != TextDocumentSyncKind::None) {
|
if (syncKind != TextDocumentSyncKind::None) {
|
||||||
VersionedTextDocumentIdentifier docId(uri);
|
|
||||||
docId.setVersion(textDocument ? textDocument->document()->revision() : 0);
|
|
||||||
DidChangeTextDocumentParams params;
|
|
||||||
params.setTextDocument(docId);
|
|
||||||
if (syncKind == TextDocumentSyncKind::Incremental) {
|
if (syncKind == TextDocumentSyncKind::Incremental) {
|
||||||
DidChangeTextDocumentParams::TextDocumentContentChangeEvent change;
|
DidChangeTextDocumentParams::TextDocumentContentChangeEvent change;
|
||||||
QTextDocument oldDoc(m_openedDocument[document]);
|
QTextDocument oldDoc(m_openedDocument[document]);
|
||||||
@@ -539,22 +537,24 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document,
|
|||||||
change.setRange(Range(cursor));
|
change.setRange(Range(cursor));
|
||||||
change.setRangeLength(cursor.selectionEnd() - cursor.selectionStart());
|
change.setRangeLength(cursor.selectionEnd() - cursor.selectionStart());
|
||||||
change.setText(document->textAt(position, charsAdded));
|
change.setText(document->textAt(position, charsAdded));
|
||||||
params.setContentChanges({change});
|
m_documentsToUpdate[document] << change;
|
||||||
} else {
|
} else {
|
||||||
params.setContentChanges({document->plainText()});
|
m_documentsToUpdate[document] = {document->plainText()};
|
||||||
}
|
}
|
||||||
m_openedDocument[document] = document->plainText();
|
m_openedDocument[document] = document->plainText();
|
||||||
sendContent(DidChangeTextDocumentNotification(params));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textDocument) {
|
|
||||||
using namespace TextEditor;
|
using namespace TextEditor;
|
||||||
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(textDocument))
|
for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(document)) {
|
||||||
if (TextEditorWidget *widget = editor->editorWidget())
|
if (TextEditorWidget *widget = editor->editorWidget()) {
|
||||||
widget->setRefactorMarkers(RefactorMarker::filterOutType(widget->refactorMarkers(), id()));
|
widget->setRefactorMarkers(
|
||||||
|
RefactorMarker::filterOutType(widget->refactorMarkers(), id()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_documentUpdateTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
void Client::registerCapabilities(const QList<Registration> ®istrations)
|
void Client::registerCapabilities(const QList<Registration> ®istrations)
|
||||||
{
|
{
|
||||||
m_dynamicCapabilities.registerCapability(registrations);
|
m_dynamicCapabilities.registerCapability(registrations);
|
||||||
@@ -578,6 +578,8 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation
|
|||||||
|
|
||||||
void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
|
void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
|
||||||
{
|
{
|
||||||
|
if (m_documentsToUpdate.contains(widget->textDocument()))
|
||||||
|
return; // we are currently changing this document so postpone the DocumentHighlightsRequest
|
||||||
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
|
const auto uri = DocumentUri::fromFilePath(widget->textDocument()->filePath());
|
||||||
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
|
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
|
||||||
TextDocumentRegistrationOptions option(
|
TextDocumentRegistrationOptions option(
|
||||||
@@ -1131,6 +1133,30 @@ void Client::resetAssistProviders(TextEditor::TextDocument *document)
|
|||||||
document->setQuickFixAssistProvider(providers.quickFixAssistProvider);
|
document->setQuickFixAssistProvider(providers.quickFixAssistProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::sendPostponedDocumentUpdates()
|
||||||
|
{
|
||||||
|
m_documentUpdateTimer.stop();
|
||||||
|
if (m_documentsToUpdate.isEmpty())
|
||||||
|
return;
|
||||||
|
TextEditor::TextEditorWidget *currentWidget
|
||||||
|
= TextEditor::TextEditorWidget::currentTextEditorWidget();
|
||||||
|
const QList<TextEditor::TextDocument *> documents = m_documentsToUpdate.keys();
|
||||||
|
for (auto document : documents) {
|
||||||
|
const auto uri = DocumentUri::fromFilePath(document->filePath());
|
||||||
|
m_highlights[uri].clear();
|
||||||
|
VersionedTextDocumentIdentifier docId(uri);
|
||||||
|
docId.setVersion(document->document()->revision());
|
||||||
|
DidChangeTextDocumentParams params;
|
||||||
|
params.setTextDocument(docId);
|
||||||
|
params.setContentChanges(m_documentsToUpdate.take(document));
|
||||||
|
sendContent(DidChangeTextDocumentNotification(params));
|
||||||
|
emit documentUpdated(document);
|
||||||
|
|
||||||
|
if (currentWidget->textDocument() == document)
|
||||||
|
cursorPositionChanged(currentWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Client::handleResponse(const MessageId &id, const QByteArray &content, QTextCodec *codec)
|
void Client::handleResponse(const MessageId &id, const QByteArray &content, QTextCodec *codec)
|
||||||
{
|
{
|
||||||
if (auto handler = m_responseHandlers[id])
|
if (auto handler = m_responseHandlers[id])
|
||||||
@@ -1283,6 +1309,13 @@ void Client::rehighlight()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::documentUpdatePostponed(const QString &fileName) const
|
||||||
|
{
|
||||||
|
return Utils::contains(m_documentsToUpdate.keys(), [fileName](const TextEditor::TextDocument *doc) {
|
||||||
|
return doc->filePath() == Utils::FilePath::fromString(fileName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Client::initializeCallback(const InitializeRequest::Response &initResponse)
|
void Client::initializeCallback(const InitializeRequest::Response &initResponse)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(m_state == InitializeRequested, return);
|
QTC_ASSERT(m_state == InitializeRequested, return);
|
||||||
|
@@ -178,8 +178,11 @@ public:
|
|||||||
HoverHandler *hoverHandler();
|
HoverHandler *hoverHandler();
|
||||||
void rehighlight();
|
void rehighlight();
|
||||||
|
|
||||||
|
bool documentUpdatePostponed(const QString &fileName) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void initialized(LanguageServerProtocol::ServerCapabilities capabilities);
|
void initialized(LanguageServerProtocol::ServerCapabilities capabilities);
|
||||||
|
void documentUpdated(TextEditor::TextDocument *document);
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -207,6 +210,7 @@ private:
|
|||||||
void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri);
|
void showDiagnostics(const LanguageServerProtocol::DocumentUri &uri);
|
||||||
void removeDiagnostics(const LanguageServerProtocol::DocumentUri &uri);
|
void removeDiagnostics(const LanguageServerProtocol::DocumentUri &uri);
|
||||||
void resetAssistProviders(TextEditor::TextDocument *document);
|
void resetAssistProviders(TextEditor::TextDocument *document);
|
||||||
|
void sendPostponedDocumentUpdates();
|
||||||
|
|
||||||
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
|
using ContentHandler = std::function<void(const QByteArray &, QTextCodec *, QString &,
|
||||||
LanguageServerProtocol::ResponseHandlers,
|
LanguageServerProtocol::ResponseHandlers,
|
||||||
@@ -219,6 +223,10 @@ private:
|
|||||||
LanguageFilter m_languagFilter;
|
LanguageFilter m_languagFilter;
|
||||||
QJsonObject m_initializationOptions;
|
QJsonObject m_initializationOptions;
|
||||||
QMap<TextEditor::TextDocument *, QString> m_openedDocument;
|
QMap<TextEditor::TextDocument *, QString> m_openedDocument;
|
||||||
|
QMap<TextEditor::TextDocument *,
|
||||||
|
QList<LanguageServerProtocol::DidChangeTextDocumentParams::TextDocumentContentChangeEvent>>
|
||||||
|
m_documentsToUpdate;
|
||||||
|
QTimer m_documentUpdateTimer;
|
||||||
Core::Id m_id;
|
Core::Id m_id;
|
||||||
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
|
LanguageServerProtocol::ServerCapabilities m_serverCapabilities;
|
||||||
DynamicCapabilities m_dynamicCapabilities;
|
DynamicCapabilities m_dynamicCapabilities;
|
||||||
|
@@ -274,6 +274,7 @@ class LanguageClientCompletionAssistProcessor : public IAssistProcessor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LanguageClientCompletionAssistProcessor(Client *client);
|
LanguageClientCompletionAssistProcessor(Client *client);
|
||||||
|
~LanguageClientCompletionAssistProcessor() override;
|
||||||
IAssistProposal *perform(const AssistInterface *interface) override;
|
IAssistProposal *perform(const AssistInterface *interface) override;
|
||||||
bool running() override;
|
bool running() override;
|
||||||
bool needsRestart() const override { return true; }
|
bool needsRestart() const override { return true; }
|
||||||
@@ -285,6 +286,7 @@ private:
|
|||||||
QPointer<QTextDocument> m_document;
|
QPointer<QTextDocument> m_document;
|
||||||
QPointer<Client> m_client;
|
QPointer<Client> m_client;
|
||||||
Utils::optional<MessageId> m_currentRequest;
|
Utils::optional<MessageId> m_currentRequest;
|
||||||
|
QMetaObject::Connection m_postponedUpdateConnection;
|
||||||
int m_pos = -1;
|
int m_pos = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -292,6 +294,11 @@ LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor
|
|||||||
: m_client(client)
|
: m_client(client)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
LanguageClientCompletionAssistProcessor::~LanguageClientCompletionAssistProcessor()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(!running(), cancel());
|
||||||
|
}
|
||||||
|
|
||||||
static QString assistReasonString(AssistReason reason)
|
static QString assistReasonString(AssistReason reason)
|
||||||
{
|
{
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
@@ -315,7 +322,20 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(const AssistIn
|
|||||||
++delta;
|
++delta;
|
||||||
if (delta < 3)
|
if (delta < 3)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
if (m_client->documentUpdatePostponed(interface->fileName())) {
|
||||||
|
m_postponedUpdateConnection
|
||||||
|
= QObject::connect(m_client,
|
||||||
|
&Client::documentUpdated,
|
||||||
|
[this, interface](TextEditor::TextDocument *document) {
|
||||||
|
if (document->filePath()
|
||||||
|
== Utils::FilePath::fromString(interface->fileName()))
|
||||||
|
perform(interface);
|
||||||
|
});
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (m_postponedUpdateConnection)
|
||||||
|
QObject::disconnect(m_postponedUpdateConnection);
|
||||||
CompletionRequest completionRequest;
|
CompletionRequest completionRequest;
|
||||||
CompletionParams::CompletionContext context;
|
CompletionParams::CompletionContext context;
|
||||||
if (interface->reason() == ActivationCharacter) {
|
if (interface->reason() == ActivationCharacter) {
|
||||||
@@ -353,15 +373,17 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(const AssistIn
|
|||||||
|
|
||||||
bool LanguageClientCompletionAssistProcessor::running()
|
bool LanguageClientCompletionAssistProcessor::running()
|
||||||
{
|
{
|
||||||
return m_currentRequest.has_value();
|
return m_currentRequest.has_value() || m_postponedUpdateConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClientCompletionAssistProcessor::cancel()
|
void LanguageClientCompletionAssistProcessor::cancel()
|
||||||
{
|
{
|
||||||
if (running()) {
|
if (m_currentRequest.has_value()) {
|
||||||
m_client->cancelRequest(m_currentRequest.value());
|
m_client->cancelRequest(m_currentRequest.value());
|
||||||
m_client->removeAssistProcessor(this);
|
m_client->removeAssistProcessor(this);
|
||||||
m_currentRequest.reset();
|
m_currentRequest.reset();
|
||||||
|
} else if (m_postponedUpdateConnection) {
|
||||||
|
QObject::disconnect(m_postponedUpdateConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user