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: [server, 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 QStringList pathsToAdd;
36 for (const WorkspaceFolder &folder : added) {
37 toAdd.append(t: QQmlLSUtils::lspUriToQmlUrl(uri: folder.uri));
38 pathsToAdd.append(t: m_codeModel->url2Path(
39 url: QQmlLSUtils::lspUriToQmlUrl(uri: folder.uri)));
40 }
41 m_codeModel->addRootUrls(urls: toAdd);
42 m_codeModel->addDirectoriesToIndex(paths: pathsToAdd, server);
43 });
44
45 QObject::connect(sender: server->notifySignals(),
46 signal: &QLspNotifySignals::receivedDidChangeWatchedFilesNotification, context: this,
47 slot: [this](const DidChangeWatchedFilesParams &params) {
48 const QList<FileEvent> &changes = params.changes;
49 for (const FileEvent &change : changes) {
50 const QString filename =
51 m_codeModel->url2Path(url: QQmlLSUtils::lspUriToQmlUrl(uri: change.uri));
52 switch (FileChangeType(change.type)) {
53 case FileChangeType::Created:
54 // m_codeModel->addFile(filename);
55 break;
56 case FileChangeType::Changed: {
57 QFile file(filename);
58 if (file.open(flags: QIODevice::ReadOnly))
59 // m_modelManager->setFileContents(filename, file.readAll());
60 break;
61 }
62 case FileChangeType::Deleted:
63 // m_modelManager->removeFile(filename);
64 break;
65 }
66 }
67 // update due to dep changes...
68 });
69
70 QObject::connect(sender: server, signal: &QLanguageServer::clientInitialized, context: this,
71 slot: &WorkspaceHandlers::clientInitialized);
72}
73
74QString WorkspaceHandlers::name() const
75{
76 return u"Workspace"_s;
77}
78
79void WorkspaceHandlers::setupCapabilities(const QLspSpecification::InitializeParams &clientInfo,
80 QLspSpecification::InitializeResult &serverInfo)
81{
82 if (!clientInfo.capabilities.workspace
83 || !clientInfo.capabilities.workspace->value(key: u"workspaceFolders"_s).toBool(defaultValue: false))
84 return;
85 WorkspaceFoldersServerCapabilities folders;
86 folders.supported = true;
87 folders.changeNotifications = true;
88 if (!serverInfo.capabilities.workspace)
89 serverInfo.capabilities.workspace = QJsonObject();
90 serverInfo.capabilities.workspace->insert(key: u"workspaceFolders"_s,
91 value: QTypedJson::toJsonValue(params: folders));
92}
93
94void WorkspaceHandlers::clientInitialized(QLanguageServer *server)
95{
96 QLanguageServerProtocol *protocol = server->protocol();
97 const auto clientInfo = server->clientInfo();
98 QList<Registration> registrations;
99 if (clientInfo.capabilities.workspace
100 && clientInfo.capabilities.workspace
101 ->value(key: u"didChangeWatchedFiles"_s)[u"dynamicRegistration"_s]
102 .toBool(defaultValue: false)) {
103 const int watchAll =
104 int(WatchKind::Create) | int(WatchKind::Change) | int(WatchKind::Delete);
105 DidChangeWatchedFilesRegistrationOptions watchedFilesParams;
106 FileSystemWatcher qmlWatcher;
107 qmlWatcher.globPattern = QByteArray("*.{qml,js,mjs}");
108 qmlWatcher.kind = watchAll;
109 FileSystemWatcher qmldirWatcher;
110 qmldirWatcher.globPattern = "qmldir";
111 qmldirWatcher.kind = watchAll;
112 FileSystemWatcher qmltypesWatcher;
113 qmltypesWatcher.globPattern = QByteArray("*.qmltypes");
114 qmltypesWatcher.kind = watchAll;
115 watchedFilesParams.watchers =
116 QList<FileSystemWatcher>({ qmlWatcher, qmldirWatcher, qmltypesWatcher });
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 udates failed, will miss file "
135 "changes done outside the editor due to error ");
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 if (m_status == Status::Indexing)
165 m_codeModel->addDirectoriesToIndex(paths: QStringList(rootPaths.begin(), rootPaths.end()), server);
166}
167
168QT_END_NAMESPACE
169

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