1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "languageclientformatter.h"
#include "client.h"
#include "dynamiccapabilities.h"
#include "languageclientutils.h"
#include <texteditor/tabsettings.h>
#include <texteditor/textdocument.h>
#include <utils/mimeutils.h>
#include <QTextDocument>
using namespace LanguageServerProtocol;
using namespace Utils;
namespace LanguageClient {
LanguageClientFormatter::LanguageClientFormatter(TextEditor::TextDocument *document, Client *client)
: m_client(client)
, m_document(document)
{
m_cancelConnection = QObject::connect(document->document(),
&QTextDocument::contentsChanged,
[this]() {
if (m_ignoreCancel)
m_ignoreCancel = false;
else
cancelCurrentRequest();
});
}
LanguageClientFormatter::~LanguageClientFormatter()
{
QObject::disconnect(m_cancelConnection);
cancelCurrentRequest();
}
static const FormattingOptions formattingOptions(const TextEditor::TabSettings &settings)
{
FormattingOptions options;
options.setTabSize(settings.m_tabSize);
options.setInsertSpace(settings.m_tabPolicy == TextEditor::TabSettings::SpacesOnlyTabPolicy);
return options;
}
QFutureWatcher<ChangeSet> *LanguageClientFormatter::format(
const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings)
{
QTC_ASSERT(m_client, return nullptr);
cancelCurrentRequest();
m_progress = QFutureInterface<ChangeSet>();
const FilePath &filePath = m_document->filePath();
const DynamicCapabilities dynamicCapabilities = m_client->dynamicCapabilities();
const QString method(DocumentRangeFormattingRequest::methodName);
if (std::optional<bool> registered = dynamicCapabilities.isRegistered(method)) {
if (!*registered)
return nullptr;
const TextDocumentRegistrationOptions option(dynamicCapabilities.option(method).toObject());
if (option.isValid()
&& !option.filterApplies(filePath, Utils::mimeTypeForName(m_document->mimeType()))) {
return nullptr;
}
} else {
const std::optional<std::variant<bool, WorkDoneProgressOptions>> &provider
= m_client->capabilities().documentRangeFormattingProvider();
if (!provider.has_value())
return nullptr;
if (std::holds_alternative<bool>(*provider) && !std::get<bool>(*provider))
return nullptr;
}
DocumentRangeFormattingParams params;
const DocumentUri uri = DocumentUri::fromFilePath(filePath);
params.setTextDocument(TextDocumentIdentifier(uri));
params.setOptions(formattingOptions(tabSettings));
if (!cursor.hasSelection()) {
QTextCursor c = cursor;
c.select(QTextCursor::LineUnderCursor);
params.setRange(Range(c));
} else {
params.setRange(Range(cursor));
}
DocumentRangeFormattingRequest request(params);
request.setResponseCallback([this](const DocumentRangeFormattingRequest::Response &response) {
handleResponse(response);
});
m_currentRequest = request.id();
m_client->sendMessage(request);
// ignore first contents changed, because this function is called inside a begin/endEdit block
m_ignoreCancel = true;
m_progress.reportStarted();
auto watcher = new QFutureWatcher<ChangeSet>();
QObject::connect(watcher, &QFutureWatcher<Text::Replacements>::canceled, [this]() {
cancelCurrentRequest();
});
watcher->setFuture(m_progress.future());
return watcher;
}
void LanguageClientFormatter::cancelCurrentRequest()
{
if (QTC_GUARD(m_client) && m_currentRequest.has_value()) {
m_progress.reportCanceled();
m_progress.reportFinished();
m_client->cancelRequest(*m_currentRequest);
m_ignoreCancel = false;
m_currentRequest = std::nullopt;
}
}
void LanguageClientFormatter::handleResponse(const DocumentRangeFormattingRequest::Response &response)
{
m_currentRequest = std::nullopt;
const std::optional<DocumentRangeFormattingRequest::Response::Error> &error = response.error();
if (QTC_GUARD(m_client) && error)
m_client->log(*error);
ChangeSet changeSet;
if (std::optional<LanguageClientArray<TextEdit>> result = response.result()) {
if (!result->isNull())
changeSet = editsToChangeSet(result->toList(), m_document->document());
}
m_progress.reportResult(changeSet);
m_progress.reportFinished();
}
} // namespace LanguageClient
|