2020-01-08 12:03:54 +01:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2019 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator.
|
|
|
|
|
**
|
|
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
|
**
|
|
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "languageclientformatter.h"
|
|
|
|
|
|
2020-03-26 06:50:19 +01:00
|
|
|
#include "client.h"
|
2020-01-08 12:03:54 +01:00
|
|
|
#include "languageclientutils.h"
|
|
|
|
|
|
|
|
|
|
#include <texteditor/tabsettings.h>
|
|
|
|
|
#include <texteditor/textdocument.h>
|
2022-02-23 17:11:20 +01:00
|
|
|
#include <utils/mimeutils.h>
|
2020-01-08 12:03:54 +01:00
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-29 13:53:20 +01:00
|
|
|
QFutureWatcher<ChangeSet> *LanguageClientFormatter::format(
|
2020-01-08 12:03:54 +01:00
|
|
|
const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings)
|
|
|
|
|
{
|
|
|
|
|
cancelCurrentRequest();
|
2021-01-29 13:53:20 +01:00
|
|
|
m_progress = QFutureInterface<ChangeSet>();
|
2020-01-08 12:03:54 +01:00
|
|
|
|
|
|
|
|
const FilePath &filePath = m_document->filePath();
|
|
|
|
|
const DynamicCapabilities dynamicCapabilities = m_client->dynamicCapabilities();
|
|
|
|
|
const QString method(DocumentRangeFormattingRequest::methodName);
|
2021-01-29 13:53:20 +01:00
|
|
|
if (optional<bool> registered = dynamicCapabilities.isRegistered(method)) {
|
2022-02-24 09:38:59 +01:00
|
|
|
if (!*registered)
|
2020-01-08 12:03:54 +01:00
|
|
|
return nullptr;
|
|
|
|
|
const TextDocumentRegistrationOptions option(dynamicCapabilities.option(method).toObject());
|
2021-02-26 08:29:15 +01:00
|
|
|
if (option.isValid()
|
2020-01-08 12:03:54 +01:00
|
|
|
&& !option.filterApplies(filePath, Utils::mimeTypeForName(m_document->mimeType()))) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2021-02-02 07:34:15 +01:00
|
|
|
} else {
|
|
|
|
|
const Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> &provider
|
|
|
|
|
= m_client->capabilities().documentRangeFormattingProvider();
|
|
|
|
|
if (!provider.has_value())
|
|
|
|
|
return nullptr;
|
|
|
|
|
if (Utils::holds_alternative<bool>(*provider) && !Utils::get<bool>(*provider))
|
|
|
|
|
return nullptr;
|
2020-01-08 12:03:54 +01:00
|
|
|
}
|
|
|
|
|
DocumentRangeFormattingParams params;
|
|
|
|
|
const DocumentUri uri = DocumentUri::fromFilePath(filePath);
|
2020-07-14 14:31:20 +02:00
|
|
|
params.setTextDocument(TextDocumentIdentifier(uri));
|
2020-01-08 12:03:54 +01:00
|
|
|
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();
|
2022-05-12 09:51:39 +02:00
|
|
|
m_client->sendMessage(request);
|
2020-01-08 12:03:54 +01:00
|
|
|
// ignore first contents changed, because this function is called inside a begin/endEdit block
|
|
|
|
|
m_ignoreCancel = true;
|
|
|
|
|
m_progress.reportStarted();
|
2021-01-29 13:53:20 +01:00
|
|
|
auto watcher = new QFutureWatcher<ChangeSet>();
|
2020-01-08 12:03:54 +01:00
|
|
|
QObject::connect(watcher, &QFutureWatcher<Text::Replacements>::canceled, [this]() {
|
|
|
|
|
cancelCurrentRequest();
|
|
|
|
|
});
|
2021-05-12 16:55:32 +02:00
|
|
|
watcher->setFuture(m_progress.future());
|
2020-01-08 12:03:54 +01:00
|
|
|
return watcher;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientFormatter::cancelCurrentRequest()
|
|
|
|
|
{
|
|
|
|
|
if (m_currentRequest.has_value()) {
|
|
|
|
|
m_progress.reportCanceled();
|
|
|
|
|
m_progress.reportFinished();
|
|
|
|
|
m_client->cancelRequest(*m_currentRequest);
|
|
|
|
|
m_ignoreCancel = false;
|
|
|
|
|
m_currentRequest = nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LanguageClientFormatter::handleResponse(const DocumentRangeFormattingRequest::Response &response)
|
|
|
|
|
{
|
|
|
|
|
m_currentRequest = nullopt;
|
|
|
|
|
if (const optional<DocumentRangeFormattingRequest::Response::Error> &error = response.error())
|
|
|
|
|
m_client->log(*error);
|
2021-01-29 13:53:20 +01:00
|
|
|
ChangeSet changeSet;
|
2020-01-08 12:03:54 +01:00
|
|
|
if (optional<LanguageClientArray<TextEdit>> result = response.result()) {
|
2021-01-29 13:53:20 +01:00
|
|
|
if (!result->isNull())
|
|
|
|
|
changeSet = editsToChangeSet(result->toList(), m_document->document());
|
2020-01-08 12:03:54 +01:00
|
|
|
}
|
2021-01-29 13:53:20 +01:00
|
|
|
m_progress.reportResult(changeSet);
|
2020-01-08 12:03:54 +01:00
|
|
|
m_progress.reportFinished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace LanguageClient
|