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 "qworkspace_p.h"
5#include "qqmllanguageserver_p.h"
6#include "qqmllsutils_p.h"
7
8#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
9#include <QtLanguageServer/private/qlspnotifysignals_p.h>
10
11#include <QtCore/qfile.h>
12#include <variant>
13
14QT_BEGIN_NAMESPACE
15using namespace Qt::StringLiterals;
16using namespace QLspSpecification;
17
18void WorkspaceHandlers::registerHandlers(QLanguageServer *server, QLanguageServerProtocol *)
19{
20 QObject::connect(sender: server->notifySignals(),
21 signal: &QLspNotifySignals::receivedDidChangeWorkspaceFoldersNotification, context: this,
22 slot: [this](const DidChangeWorkspaceFoldersParams &params) {
23 const WorkspaceFoldersChangeEvent &event = params.event;
24
25 const QList<WorkspaceFolder> &removed = event.removed;
26 QList<QByteArray> toRemove;
27 for (const WorkspaceFolder &folder : removed) {
28 toRemove.append(t: QQmlLSUtils::lspUriToQmlUrl(uri: folder.uri));
29 m_codeModel->removeDirectory(path: m_codeModel->url2Path(
30 url: QQmlLSUtils::lspUriToQmlUrl(uri: folder.uri)));
31 }
32 m_codeModel->removeRootUrls(urls: toRemove);
33 const QList<WorkspaceFolder> &added = event.added;
34 QList<QByteArray> toAdd;
35 for (const WorkspaceFolder &folder : added) {
36 toAdd.append(t: QQmlLSUtils::lspUriToQmlUrl(uri: folder.uri));
37 }
38 m_codeModel->addRootUrls(urls: toAdd);
39 });
40
41 QObject::connect(sender: server->notifySignals(),
42 signal: &QLspNotifySignals::receivedDidChangeWatchedFilesNotification, context: this,
43 slot: [this](const DidChangeWatchedFilesParams &params) {
44 const QList<FileEvent> &changes = params.changes;
45 for (const FileEvent &change : changes) {
46 const QString filename =
47 m_codeModel->url2Path(url: QQmlLSUtils::lspUriToQmlUrl(uri: change.uri));
48 switch (FileChangeType(change.type)) {
49 case FileChangeType::Created:
50 // m_codeModel->addFile(filename);
51 break;
52 case FileChangeType::Changed: {
53 QFile file(filename);
54 if (file.open(flags: QIODevice::ReadOnly))
55 // m_modelManager->setFileContents(filename, file.readAll());
56 break;
57 break;
58 }
59 case FileChangeType::Deleted:
60 // m_modelManager->removeFile(filename);
61 break;
62 }
63 }
64 // update due to dep changes...
65 });
66
67 QObject::connect(sender: server, signal: &QLanguageServer::clientInitialized, context: this,
68 slot: &WorkspaceHandlers::clientInitialized);
69}
70
71QString WorkspaceHandlers::name() const
72{
73 return u"Workspace"_s;
74}
75
76void WorkspaceHandlers::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
77 QLspSpecification::InitializeResult &serverInfo)
78{
79 if (!clientInfo.capabilities.workspace
80 || !clientInfo.capabilities.workspace->value(key: u"workspaceFolders"_s).toBool(defaultValue: false))
81 return;
82 WorkspaceFoldersServerCapabilities folders;
83 folders.supported = true;
84 folders.changeNotifications = true;
85 if (!serverInfo.capabilities.workspace)
86 serverInfo.capabilities.workspace = QJsonObject();
87 serverInfo.capabilities.workspace->insert(key: u"workspaceFolders"_s,
88 value: QTypedJson::toJsonValue(params: folders));
89}
90
91void WorkspaceHandlers::clientInitialized(QLanguageServer *server)
92{
93 QLanguageServerProtocol *protocol = server->protocol();
94 const auto clientInfo = server->clientInfo();
95 QList<Registration> registrations;
96 if (clientInfo.capabilities.workspace
97 && clientInfo.capabilities.workspace
98 ->value(key: u"didChangeWatchedFiles"_s)[u"dynamicRegistration"_s]
99 .toBool(defaultValue: false)) {
100 const int watchAll =
101 int(WatchKind::Create) | int(WatchKind::Change) | int(WatchKind::Delete);
102 DidChangeWatchedFilesRegistrationOptions watchedFilesParams;
103 FileSystemWatcher qmlWatcher;
104 qmlWatcher.globPattern = QByteArray("*.{qml,js,mjs}");
105 qmlWatcher.kind = watchAll;
106 FileSystemWatcher qmldirWatcher;
107 qmldirWatcher.globPattern = "qmldir";
108 qmldirWatcher.kind = watchAll;
109 FileSystemWatcher qmltypesWatcher;
110 qmltypesWatcher.globPattern = QByteArray("*.qmltypes");
111 qmltypesWatcher.kind = watchAll;
112 watchedFilesParams.watchers = QList<FileSystemWatcher>({
113 std::move(qmlWatcher),
114 std::move(qmldirWatcher),
115 std::move(qmltypesWatcher)
116 });
117 registrations.append(t: Registration {
118 // use ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles as id too
119 .id: ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
120 .method: ClientCapabilitiesInfo::WorkspaceDidChangeWatchedFiles,
121 .registerOptions: QTypedJson::toJsonValue(params: watchedFilesParams) });
122 }
123
124 if (!registrations.isEmpty()) {
125 RegistrationParams params;
126 params.registrations = registrations;
127 protocol->requestRegistration(
128 params,
129 responseHandler: []() {
130 // successful registration
131 },
132 errorHandler: [protocol](const ResponseError &err) {
133 LogMessageParams msg;
134 msg.message = QByteArray("Registration of file updates failed, will miss file "
135 "changes from outside the editor.");
136 msg.message.append(a: QString::number(err.code).toUtf8());
137 if (!err.message.isEmpty())
138 msg.message.append(s: " ");
139 msg.message.append(a: err.message);
140 msg.type = MessageType::Warning;
141 qCWarning(lspServerLog) << QString::fromUtf8(ba: msg.message);
142 protocol->notifyLogMessage(params: msg);
143 });
144 }
145
146 QSet<QString> rootPaths;
147 if (std::holds_alternative<QByteArray>(v: clientInfo.rootUri)) {
148 QString path = m_codeModel->url2Path(
149 url: QQmlLSUtils::lspUriToQmlUrl(uri: std::get<QByteArray>(v: clientInfo.rootUri)));
150 rootPaths.insert(value: path);
151 } else if (clientInfo.rootPath && std::holds_alternative<QByteArray>(v: *clientInfo.rootPath)) {
152 QString path = QString::fromUtf8(ba: std::get<QByteArray>(v: *clientInfo.rootPath));
153 rootPaths.insert(value: path);
154 }
155
156 if (clientInfo.workspaceFolders
157 && std::holds_alternative<QList<WorkspaceFolder>>(v: *clientInfo.workspaceFolders)) {
158 for (const WorkspaceFolder &workspace :
159 std::as_const(t: std::get<QList<WorkspaceFolder>>(v: *clientInfo.workspaceFolders))) {
160 const QUrl workspaceUrl(QString::fromUtf8(ba: QQmlLSUtils::lspUriToQmlUrl(uri: workspace.uri)));
161 rootPaths.insert(value: workspaceUrl.toLocalFile());
162 }
163 }
164}
165
166QT_END_NAMESPACE
167

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