1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "common/common.h"
6#include "jsonrpccallproxy.h"
7#include "remotechecker.h"
8
9#include <QCoreApplication>
10#include <QDebug>
11#include <QJsonObject>
12#include <QDir>
13
14#include <iostream>
15
16// setting from clangd trismit
17QProcess *createCxxServ(const newlsp::ProjectKey &key)
18{
19 lspServOut << __FUNCTION__ << qApp->thread() << QThread::currentThread();
20 if (key.language != newlsp::Cxx)
21 return nullptr;
22
23 QString projectCacheDir = ".unioncode";
24 QString compileDB_Path = QString::fromStdString(key.workspace) + QDir::separator() + projectCacheDir;
25 QStringList compileDB_CMD_As;
26 compileDB_CMD_As << "-S" << QString::fromStdString(key.workspace);
27 compileDB_CMD_As << "-B" << compileDB_Path;
28 compileDB_CMD_As << "-DCMAKE_EXPORT_COMPILE_COMMANDS=1";
29 QProcess::execute("/usr/bin/cmake", compileDB_CMD_As);
30
31 QStringList procAs;
32 QString clangd = "clangd-13";
33 if (ProcessUtil::exists(clangd)) {
34 procAs << clangd;
35 } else {
36 QString clangdFileName = "clangd";
37 if (!env::pkg::native::installed()) {
38 RemoteChecker::instance().checkLanguageBackend(QString::fromStdString(key.language));
39 QString runtimePath = CustomPaths::lspRuntimePath(QString::fromStdString(key.language));
40 procAs << runtimePath + QDir::separator() + clangdFileName;
41 } else {
42 procAs << env::pkg::native::path(clangdFileName);
43 }
44 }
45
46 procAs << "--log=verbose";
47 procAs << QString("--compile-commands-dir=%0").arg(compileDB_Path);
48 procAs << "--clang-tidy";
49 procAs << "-j=$(nproc)";
50
51 auto proc = new QProcess();
52 proc->setProgram("/usr/bin/bash");
53 proc->setArguments({"-c", procAs.join(" ")});
54
55 return proc;
56}
57
58// setting from jdtls trismit
59QProcess *createJavaServ(const newlsp::ProjectKey &key)
60{
61 lspServOut << __FUNCTION__ << qApp->thread() << QThread::currentThread();
62 if (key.language != newlsp::Java)
63 return nullptr;
64
65 QString dataDir = CustomPaths::projectCachePath(QString::fromStdString(key.workspace));
66 QString runtimePath = CustomPaths::lspRuntimePath(QString::fromStdString(key.language));
67 bool noRuntimeChilds = QDir(runtimePath).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).isEmpty();
68 if (noRuntimeChilds) {
69 if (!env::pkg::native::installed()) {
70 RemoteChecker::instance().checkLanguageBackend(QString::fromStdString(key.language));
71 } else {
72 lspServOut << "unzip install native package..." << noRuntimeChilds;
73 QString jdtlsNativePkgPath = env::pkg::native::path(env::package::Category::get()->jdtls);
74 ProcessUtil::execute("tar", {"zxvf", jdtlsNativePkgPath, "-C", "."}, runtimePath,
75 [=](const QByteArray &data){
76 lspServOut << QString(data);
77 });
78 }
79 }
80
81 auto proc = new QProcess();
82 QString lanuchLine = "/usr/bin/java "
83 "-Declipse.application=org.eclipse.jdt.ls.core.id1 "
84 "-Dosgi.bundles.defaultStartLevel=4 "
85 "-Declipse.product=org.eclipse.jdt.ls.core.product "
86 "-Dlog.level=ALL "
87 "-noverify "
88 "-Xmx1G "
89 "--add-modules=ALL-SYSTEM "
90 "--add-opens java.base/java.util=ALL-UNNAMED "
91 "--add-opens java.base/java.lang=ALL-UNNAMED "
92 "-jar " + runtimePath + "/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar "
93 "-configuration " + runtimePath + "/config_linux "
94 + QString("-data %0").arg(dataDir);
95 proc->setProgram("/usr/bin/bash");
96 proc->setArguments({"-c", lanuchLine});
97
98 return proc;
99}
100
101// setting from pyls trismit
102QProcess *createPythonServ(const newlsp::ProjectKey &key)
103{
104 lspServOut << __FUNCTION__ << qApp->thread() << QThread::currentThread();
105 if (key.language != newlsp::Python)
106 return nullptr;
107
108 QString jdtlsNativePkgPath = env::pkg::native::path(env::package::Category::get()->jdtls);
109 ProcessUtil::execute("pip3", {"install", "python-language-server[all]"}, [=](const QByteArray &data){
110 lspServOut << QString(data);
111 });
112
113 auto proc = new QProcess();
114 proc->setProgram("/usr/bin/bash");
115 proc->setArguments({"-c","pyls -v"});
116 env::lang::Version pyVer;
117 pyVer.major = 3;
118 if (!ProcessUtil::exists("pyls")) {
119 auto python3Env = env::lang::get(env::lang::Category::User, env::lang::Python, pyVer);
120 proc->setProcessEnvironment(python3Env);
121 }
122
123 return proc;
124}
125
126// setting from pyls trismit
127QProcess *createJSServ(const newlsp::ProjectKey &key)
128{
129 lspServOut << "create js language server," << qApp->thread() << QThread::currentThread();
130 if (key.language != newlsp::JS)
131 return nullptr;
132
133 QString jsServerInstallPath = CustomPaths::lspRuntimePath(QString::fromStdString(key.language));
134 RemoteChecker::instance().checkJSServer(jsServerInstallPath);
135
136 QString nodePath = jsServerInstallPath + "/node_modules/node/bin/node";
137 QString serverPath = jsServerInstallPath + "/node_modules/.bin/typescript-language-server";
138 if (!QFileInfo::exists(nodePath) || !QFileInfo::exists(serverPath)) {
139 return nullptr;
140 }
141
142 auto proc = new QProcess();
143 proc->setProgram(nodePath);
144 proc->setArguments({serverPath, "--stdio"});
145
146 return proc;
147}
148
149
150void selectLspServer(const QJsonObject &params)
151{
152 QString language = params.value(QString::fromStdString(newlsp::language)).toString();
153 QString workspace = params.value(QString::fromStdString(newlsp::workspace)).toString();
154 newlsp::ProjectKey projectKey {language.toStdString(), workspace.toStdString()};
155 JsonRpcCallProxy::ins().setSelect(projectKey);
156 QProcess *proc = JsonRpcCallProxy::ins().value(projectKey);
157
158 if (!proc) {
159 proc = JsonRpcCallProxy::ins().createLspServ(projectKey);
160 if (proc) {
161 proc->setProcessChannelMode(QProcess::ForwardedOutputChannel);
162 QObject::connect(proc, &QProcess::readyReadStandardError, proc, [=]() {
163 lspServErr << "run lspServ error:" << proc->readAllStandardError().toStdString();
164 });
165 proc->start();
166 lspServOut << "save backend process";
167 JsonRpcCallProxy::ins().save(projectKey, proc);
168 lspServOut << "selected ProjectKey{language:" << projectKey.language
169 << ", workspace:" << projectKey.workspace << "}";
170 JsonRpcCallProxy::ins().setSelect(projectKey);
171 }
172 }
173
174 if (!proc)
175 lspServOut << "selected error ProjectKey{language:" << projectKey.language
176 << ",workspace:" << projectKey.workspace << "}";
177}
178
179int main(int argc, char *argv[])
180{
181 QCoreApplication a(argc, argv);
182
183 JsonRpcCallProxy::ins().bindCreateProc(newlsp::Cxx, createCxxServ);
184 JsonRpcCallProxy::ins().bindCreateProc(newlsp::Java, createJavaServ);
185 JsonRpcCallProxy::ins().bindCreateProc(newlsp::Python, createPythonServ);
186 JsonRpcCallProxy::ins().bindCreateProc(newlsp::JS, createJSServ);
187 JsonRpcCallProxy::ins().bindFilter("selectLspServer", selectLspServer);
188
189 newlsp::ServerApplication lspServ(a);
190 QObject::connect(&lspServ, &newlsp::ServerApplication::jsonrpcMethod,
191 &JsonRpcCallProxy::ins(), &JsonRpcCallProxy::methodFilter,
192 Qt::QueuedConnection);
193
194 QObject::connect(&lspServ, &newlsp::ServerApplication::jsonrpcNotification,
195 &JsonRpcCallProxy::ins(), &JsonRpcCallProxy::notificationFilter,
196 Qt::QueuedConnection);
197 lspServ.start();
198 lspServOut << "created ServerApplication";
199
200 return a.exec();
201}
202