1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qqmlformatting_p.h>
5#include <qqmlcodemodel_p.h>
6#include <qqmllsutils_p.h>
7
8#include <QtQmlDom/private/qqmldomitem_p.h>
9#include <QtQmlDom/private/qqmldomindentinglinewriter_p.h>
10#include <QtQmlDom/private/qqmldomoutwriter_p.h>
11
12QT_BEGIN_NAMESPACE
13
14Q_LOGGING_CATEGORY(formatLog, "qt.languageserver.formatting")
15
16QQmlDocumentFormatting::QQmlDocumentFormatting(QmlLsp::QQmlCodeModel *codeModel)
17 : QQmlBaseModule(codeModel)
18{
19}
20
21QString QQmlDocumentFormatting::name() const
22{
23 return u"QQmlDocumentFormatting"_s;
24}
25
26void QQmlDocumentFormatting::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
27{
28 protocol->registerDocumentFormattingRequestHandler(handler: getRequestHandler());
29}
30
31void QQmlDocumentFormatting::setupCapabilities(
32 const QLspSpecification::InitializeParams &,
33 QLspSpecification::InitializeResult &serverCapabilities)
34{
35 // TODO: Allow customized formatting in future
36 serverCapabilities.capabilities.documentFormattingProvider = true;
37}
38
39void QQmlDocumentFormatting::process(RequestPointerArgument request)
40{
41 using namespace QQmlJS::Dom;
42 QmlLsp::OpenDocument doc = m_codeModel->openDocumentByUrl(
43 url: QQmlLSUtils::lspUriToQmlUrl(uri: request->m_parameters.textDocument.uri));
44
45 DomItem file = doc.snapshot.doc.fileObject(option: GoTo::MostLikely);
46 if (!file) {
47 qWarning() << u"Could not find the file"_s << doc.snapshot.doc.toString();
48 return;
49 }
50 if (!file.field(name: Fields::isValid).value().toBool(defaultValue: false)) {
51 qWarning() << u"Invalid document will not be formatted"_s;
52 return;
53 }
54 if (auto envPtr = file.environment().ownerAs<DomEnvironment>())
55 envPtr->clearReferenceCache();
56
57 auto qmlFile = file.ownerAs<QmlFile>();
58 if (!qmlFile || !qmlFile->isValid()) {
59 file.iterateErrors(
60 visitor: [](DomItem, ErrorMessage msg) {
61 errorToQDebug(msg);
62 return true;
63 },
64 iterate: true);
65 qWarning().noquote() << "Failed to parse" << file;
66 return;
67 }
68
69 // TODO: implement formatting options
70 // For now, qmlformat's default options.
71 LineWriterOptions options;
72 options.updateOptions = LineWriterOptions::Update::None;
73 options.attributesSequence = LineWriterOptions::AttributesSequence::Preserve;
74
75 QLspSpecification::TextEdit formattedText;
76 LineWriter lw([&formattedText](QStringView s) {formattedText.newText += s.toUtf8(); }, QString(), options);
77 OutWriter ow(lw);
78 MutableDomItem result = file.writeOutForFile(ow, extraChecks: WriteOutCheck::Default);
79 ow.flush();
80
81 const auto &code = qmlFile->code();
82 const auto [endLine, endColumn] = QQmlLSUtils::textRowAndColumnFrom(code, offset: code.length());
83
84 Q_UNUSED(endColumn);
85 formattedText.range = QLspSpecification::Range{ .start: QLspSpecification::Position{ .line: 0, .character: 0 },
86 .end: QLspSpecification::Position{ .line: endLine + 1, .character: 0 } };
87
88 request->m_response.sendResponse(r: QList<QLspSpecification::TextEdit>{ formattedText });
89}
90
91QT_END_NAMESPACE
92

source code of qtdeclarative/src/qmlls/qqmlformatting.cpp