#include "config.h" #include "lsp/lsp.h" static bool init_lsp(std::shared_ptr lsp) { int in_pipe[2]; int out_pipe[2]; if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) { perror("pipe"); return false; } pid_t pid = fork(); if (pid == -1) { perror("fork"); return false; } if (pid == 0) { dup2(in_pipe[0], STDIN_FILENO); dup2(out_pipe[1], STDOUT_FILENO); #ifdef __clang__ int devnull = open("/dev/null", O_WRONLY); if (devnull >= 0) { dup2(devnull, STDERR_FILENO); close(devnull); } #else int log = open("/tmp/lsp.log", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (log >= 0) { dup2(log, STDERR_FILENO); close(log); } #endif close(in_pipe[0]); close(in_pipe[1]); close(out_pipe[0]); close(out_pipe[1]); execvp(lsp->lsp->command, (char *const *)(lsp->lsp->args.data())); perror("execvp"); _exit(127); } lsp->pid = pid; lsp->stdin_fd = in_pipe[1]; lsp->stdout_fd = out_pipe[0]; close(in_pipe[0]); close(out_pipe[1]); return true; } std::shared_ptr get_or_init_lsp(uint8_t lsp_id) { std::unique_lock lock(active_lsps_mtx); auto it = active_lsps.find(lsp_id); if (it == active_lsps.end()) { auto map_it = kLsps.find(lsp_id); if (map_it == kLsps.end()) return nullptr; std::shared_ptr lsp = std::make_shared(); lsp->lsp = &map_it->second; if (!init_lsp(lsp)) return nullptr; LSPPending *pending = new LSPPending(); pending->method = "initialize"; pending->editor = nullptr; pending->callback = [lsp](Editor *, std::string, json msg) { if (msg.contains("result") && msg["result"].contains("capabilities")) { auto &caps = msg["result"]["capabilities"]; if (caps.contains("textDocumentSync")) { auto &sync = caps["textDocumentSync"]; if (sync.is_number()) { int change_type = sync.get(); lsp->incremental_sync = (change_type == 2); } else if (sync.is_object() && sync.contains("change")) { int change_type = sync["change"].get(); lsp->incremental_sync = (change_type == 2); } } if (caps.contains("hoverProvider")) lsp->allow_hover = caps["hoverProvider"].get(); else lsp->allow_hover = false; if (caps.contains("completionProvider")) lsp->allow_completion = true; } lsp->initialized = true; json initialized = {{"jsonrpc", "2.0"}, {"method", "initialized"}, {"params", json::object()}}; lsp_send(lsp, initialized, nullptr); }; json init_message = { {"jsonrpc", "2.0"}, {"method", "initialize"}, {"params", {{"processId", getpid()}, {"rootUri", "file://" + percent_encode(path_abs("."))}, {"capabilities", client_capabilities}}}}; lsp_send(lsp, init_message, pending); active_lsps[lsp_id] = lsp; return lsp; } return it->second; } void close_lsp(uint8_t lsp_id) { std::shared_lock active_lsps_lock(active_lsps_mtx); auto it = active_lsps.find(lsp_id); if (it == active_lsps.end()) return; std::shared_ptr lsp = it->second; active_lsps_lock.unlock(); lsp->exited = true; lsp->initialized = false; LSPPending *shutdown_pending = new LSPPending(); shutdown_pending->method = "shutdown"; shutdown_pending->callback = [lsp, lsp_id](Editor *, std::string, json) { json exit = {{"jsonrpc", "2.0"}, {"method", "exit"}}; lsp_send(lsp, exit, nullptr); }; json shutdown = {{"jsonrpc", "2.0"}, {"method", "shutdown"}}; lsp_send(lsp, shutdown, shutdown_pending); std::thread t([lsp, lsp_id] { std::this_thread::sleep_for(100ms); std::unique_lock active_lsps_lock(active_lsps_mtx); std::unique_lock lock(lsp->mtx); if (lsp->pid != -1 && kill(lsp->pid, 0) == 0) kill(lsp->pid, SIGKILL); waitpid(lsp->pid, nullptr, 0); close(lsp->stdin_fd); close(lsp->stdout_fd); for (auto &kv : lsp->pending) delete kv.second; for (auto &editor : lsp->editors) { std::unique_lock editor_lock(editor->lsp_mtx); editor->lsp = nullptr; } active_lsps.erase(lsp_id); }); t.detach(); } void clean_lsp(std::shared_ptr lsp, uint8_t lsp_id) { for (auto &kv : lsp->pending) delete kv.second; lsp->pid = -1; close(lsp->stdin_fd); close(lsp->stdout_fd); for (auto &editor : lsp->editors) { std::unique_lock editor_lock(editor->lsp_mtx); editor->lsp = nullptr; } active_lsps.erase(lsp_id); }