1// Copyright (C) 2021 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 "qqmllanguageserver_p.h"
5#include "qtextsynchronization_p.h"
6#include "qlanguageserver_p.h"
7#include "qlspcustomtypes_p.h"
8
9#include <QtCore/qdir.h>
10
11#include <iostream>
12#include <algorithm>
13
14QT_BEGIN_NAMESPACE
15
16namespace QmlLsp {
17
18using namespace QLspSpecification;
19using namespace Qt::StringLiterals;
20/*!
21\internal
22\class QmlLsp::QQmlLanguageServer
23\brief Sets up a QmlLanguageServer.
24
25This class sets up a QML language server.
26
27Use the following function to send replies:
28
29\code
30std::function<void(const QByteArray &)> sendData
31\endcode
32
33And, feed the data that the function receives to the \c {server()->receive()}
34method.
35
36Call this method only from a single thread, and do not block. To achieve this,
37avoid direct calls, and connect the method as a slot, while reading from another
38thread.
39
40The various tasks of the language server are divided between
41QLanguageServerModule instances. Each instance is responsible for handling a
42certain subset of client requests. For example, one instance handles completion
43requests, another one updates the code in the code model when the client sends a
44new file version, and so on. The QLanguageServerModule instances are
45constructed and registered with QLanguageServer in the constructor of
46this class.
47
48Generally, do all operations in the object thread and always call handlers from
49it. However, the operations can delegate the response to another thread, as the
50response handler is thread safe. All the methods of the \c server() object are
51also thread safe.
52
53The code model starts other threads to update its state. See its documentation
54for more information.
55*/
56QQmlLanguageServer::QQmlLanguageServer(std::function<void(const QByteArray &)> sendData,
57 QQmlToolingSettings *settings)
58 : m_codeModel(nullptr, settings),
59 m_server(sendData),
60 m_textSynchronization(&m_codeModel),
61 m_lint(&m_server, &m_codeModel),
62 m_workspace(&m_codeModel),
63 m_completionSupport(&m_codeModel),
64 m_navigationSupport(&m_codeModel),
65 m_definitionSupport(&m_codeModel),
66 m_referencesSupport(&m_codeModel),
67 m_documentFormatting(&m_codeModel),
68 m_renameSupport(&m_codeModel),
69 m_rangeFormatting(&m_codeModel),
70 m_hover(&m_codeModel),
71 m_highlightSupport(&m_codeModel),
72 m_documentSymbolSupport(&m_codeModel)
73{
74 m_server.addServerModule(serverModule: this);
75 m_server.addServerModule(serverModule: &m_textSynchronization);
76 m_server.addServerModule(serverModule: &m_lint);
77 m_server.addServerModule(serverModule: &m_workspace);
78 m_server.addServerModule(serverModule: &m_completionSupport);
79 m_server.addServerModule(serverModule: &m_navigationSupport);
80 m_server.addServerModule(serverModule: &m_definitionSupport);
81 m_server.addServerModule(serverModule: &m_referencesSupport);
82 m_server.addServerModule(serverModule: &m_documentFormatting);
83 m_server.addServerModule(serverModule: &m_renameSupport);
84 m_server.addServerModule(serverModule: &m_rangeFormatting);
85 m_server.addServerModule(serverModule: &m_hover);
86 m_server.addServerModule(serverModule: &m_highlightSupport);
87 m_server.addServerModule(serverModule: &m_documentSymbolSupport);
88 m_server.finishSetup();
89 qCWarning(lspServerLog) << "Did Setup";
90}
91
92void QQmlLanguageServer::registerHandlers(QLanguageServer *server,
93 QLanguageServerProtocol *protocol)
94{
95 Q_UNUSED(protocol);
96 QObject::connect(sender: server, signal: &QLanguageServer::lifecycleError, context: this,
97 slot: &QQmlLanguageServer::errorExit);
98 QObject::connect(sender: server, signal: &QLanguageServer::exit, context: this, slot: &QQmlLanguageServer::exit);
99 QObject::connect(sender: server, signal: &QLanguageServer::runStatusChanged, context: this, slot: [](QLanguageServer::RunStatus r) {
100 qCDebug(lspServerLog) << "runStatus" << int(r);
101 });
102 protocol->typedRpc()->registerNotificationHandler<Notifications::AddBuildDirsParams>(
103 method: QByteArray(Notifications::AddBuildDirsMethod),
104 handler: [this](const QByteArray &, const Notifications::AddBuildDirsParams &params) {
105 for (const auto &buildDirs : params.buildDirsToSet) {
106 QStringList dirPaths;
107 dirPaths.resize(size: buildDirs.buildDirs.size());
108 std::transform(first: buildDirs.buildDirs.begin(), last: buildDirs.buildDirs.end(),
109 result: dirPaths.begin(), unary_op: [](const QByteArray &utf8Str) {
110 return QString::fromUtf8(ba: utf8Str);
111 });
112 m_codeModel.setBuildPathsForRootUrl(url: buildDirs.baseUri, paths: dirPaths);
113 }
114 });
115}
116
117void QQmlLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
118 QLspSpecification::InitializeResult &serverInfo)
119{
120 QJsonObject expCap;
121 if (serverInfo.capabilities.experimental.has_value() && serverInfo.capabilities.experimental->isObject())
122 expCap = serverInfo.capabilities.experimental->toObject();
123 expCap.insert(key: u"addBuildDirs"_s, value: QJsonObject({ { u"supported"_s, true } }));
124 serverInfo.capabilities.experimental = expCap;
125
126 if (clientInfo.workspaceFolders) {
127 if (auto workspaceList =
128 std::get_if<QList<WorkspaceFolder>>(ptr: &*clientInfo.workspaceFolders)) {
129 QList<QByteArray> workspaceUris;
130 std::transform(first: workspaceList->cbegin(), last: workspaceList->cend(),
131 result: std::back_inserter(x&: workspaceUris),
132 unary_op: [](const auto &workspaceFolder) { return workspaceFolder.uri; });
133 m_codeModel.setRootUrls(workspaceUris);
134 }
135 }
136}
137
138QString QQmlLanguageServer::name() const
139{
140 return u"QQmlLanguageServer"_s;
141}
142
143void QQmlLanguageServer::errorExit()
144{
145 qCWarning(lspServerLog) << "Error exit";
146 fclose(stdin);
147}
148
149void QQmlLanguageServer::exit()
150{
151 m_returnValue = 0;
152 fclose(stdin);
153}
154
155int QQmlLanguageServer::returnValue() const
156{
157 return m_returnValue;
158}
159
160QQmlCodeModel *QQmlLanguageServer::codeModel()
161{
162 return &m_codeModel;
163}
164
165QLanguageServer *QQmlLanguageServer::server()
166{
167 return &m_server;
168}
169
170TextSynchronization *QQmlLanguageServer::textSynchronization()
171{
172 return &m_textSynchronization;
173}
174
175QmlLintSuggestions *QQmlLanguageServer::lint()
176{
177 return &m_lint;
178}
179
180WorkspaceHandlers *QQmlLanguageServer::worspace()
181{
182 return &m_workspace;
183}
184
185} // namespace QmlLsp
186
187QT_END_NAMESPACE
188

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