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{
69 m_server.addServerModule(serverModule: this);
70 m_server.addServerModule(serverModule: &m_textSynchronization);
71 m_server.addServerModule(serverModule: &m_lint);
72 m_server.addServerModule(serverModule: &m_workspace);
73 m_server.addServerModule(serverModule: &m_completionSupport);
74 m_server.addServerModule(serverModule: &m_navigationSupport);
75 m_server.addServerModule(serverModule: &m_definitionSupport);
76 m_server.addServerModule(serverModule: &m_referencesSupport);
77 m_server.addServerModule(serverModule: &m_documentFormatting);
78 m_server.finishSetup();
79 qCWarning(lspServerLog) << "Did Setup";
80}
81
82void QQmlLanguageServer::registerHandlers(QLanguageServer *server,
83 QLanguageServerProtocol *protocol)
84{
85 Q_UNUSED(protocol);
86 QObject::connect(sender: server, signal: &QLanguageServer::lifecycleError, context: this,
87 slot: &QQmlLanguageServer::errorExit);
88 QObject::connect(sender: server, signal: &QLanguageServer::exit, context: this, slot: &QQmlLanguageServer::exit);
89 QObject::connect(sender: server, signal: &QLanguageServer::runStatusChanged, slot: [](QLanguageServer::RunStatus r) {
90 qCDebug(lspServerLog) << "runStatus" << int(r);
91 });
92 protocol->typedRpc()->registerNotificationHandler<Notifications::AddBuildDirsParams>(
93 method: QByteArray(Notifications::AddBuildDirsMethod),
94 handler: [this](const QByteArray &, const Notifications::AddBuildDirsParams &params) {
95 for (const auto &buildDirs : params.buildDirsToSet) {
96 QStringList dirPaths;
97 dirPaths.resize(size: buildDirs.buildDirs.size());
98 std::transform(first: buildDirs.buildDirs.begin(), last: buildDirs.buildDirs.end(),
99 result: dirPaths.begin(), unary_op: [](const QByteArray &utf8Str) {
100 return QString::fromUtf8(ba: utf8Str);
101 });
102 m_codeModel.setBuildPathsForRootUrl(url: buildDirs.baseUri, paths: dirPaths);
103 }
104 });
105}
106
107void QQmlLanguageServer::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
108 QLspSpecification::InitializeResult &serverInfo)
109{
110 Q_UNUSED(clientInfo);
111 QJsonObject expCap;
112 if (serverInfo.capabilities.experimental.has_value() && serverInfo.capabilities.experimental->isObject())
113 expCap = serverInfo.capabilities.experimental->toObject();
114 expCap.insert(key: u"addBuildDirs"_s, value: QJsonObject({ { u"supported"_s, true } }));
115 serverInfo.capabilities.experimental = expCap;
116}
117
118QString QQmlLanguageServer::name() const
119{
120 return u"QQmlLanguageServer"_s;
121}
122
123void QQmlLanguageServer::errorExit()
124{
125 qCWarning(lspServerLog) << "Error exit";
126 fclose(stdin);
127}
128
129void QQmlLanguageServer::exit()
130{
131 m_returnValue = 0;
132 fclose(stdin);
133}
134
135int QQmlLanguageServer::returnValue() const
136{
137 return m_returnValue;
138}
139
140QQmlCodeModel *QQmlLanguageServer::codeModel()
141{
142 return &m_codeModel;
143}
144
145QLanguageServer *QQmlLanguageServer::server()
146{
147 return &m_server;
148}
149
150TextSynchronization *QQmlLanguageServer::textSynchronization()
151{
152 return &m_textSynchronization;
153}
154
155QmlLintSuggestions *QQmlLanguageServer::lint()
156{
157 return &m_lint;
158}
159
160WorkspaceHandlers *QQmlLanguageServer::worspace()
161{
162 return &m_workspace;
163}
164
165} // namespace QmlLsp
166
167QT_END_NAMESPACE
168

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