nixd
Loading...
Searching...
No Matches
Format.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of [Formatting].
3/// [Formatting]:
4/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_formatting
5///
6/// For now nixd only support "external" formatting. That is, invokes an
7/// external command and then let that process do formatting.
8
11
12#include <boost/asio/post.hpp>
13#include <sys/wait.h>
14
15using namespace nixd;
16using namespace lspserver;
17
18void Controller::onFormat(const DocumentFormattingParams &Params,
19 Callback<std::vector<TextEdit>> Reply) {
20 auto Action = [this, Params, Reply = std::move(Reply)]() mutable {
21 lspserver::PathRef File = Params.textDocument.uri.file();
22 const std::string &Code = *Store.getDraft(File)->Contents;
23 // Invokes another process and then read it's stdout.
24 std::vector<std::string> FormatCommand;
25 {
26 // Read from config, get format options.
27 std::lock_guard G(ConfigLock);
29 }
30
31 if (FormatCommand.empty()) {
32 Reply(error("formating command is empty, please set external formatter"));
33 return;
34 }
35
36 // Convert vectors to syscall form. This should be cheap.
37 std::vector<char *> Syscall;
38 Syscall.reserve(FormatCommand.size());
39 for (const auto &Str : FormatCommand) {
40 // For compatibility with existing C code.
41 Syscall.emplace_back(const_cast<char *>(Str.c_str()));
42 }
43
44 // Null terminator.
45 Syscall.emplace_back(nullptr);
46
47 int In;
48 int Out;
49 int Err;
50
51 pid_t Child = forkPiped(In, Out, Err);
52 if (Child == 0) {
53 execvp(Syscall[0], Syscall.data());
54 exit(-1);
55 }
56 // Firstly, send the document to the process stdin.
57 // Invoke POSIX write(2) to do such thing.
58 const char *Start = Code.c_str();
59 const char *End = Code.c_str() + Code.size();
60 while (Start != End) {
61 if (long Writen = write(In, Start, End - Start); Writen != -1) {
62 Start += Writen;
63 } else {
64 throw std::system_error(errno, std::generic_category());
65 }
66 }
67 close(In);
68
69 // And, wait for the process.
70 int Exit = 0;
71 waitpid(Child, &Exit, 0);
72
73 if (Exit != 0) {
74 Reply(error("formatting {0} command exited with {1}", FormatCommand[0],
75 Exit));
76 return;
77 }
78
79 // Okay, read stdout from it.
80 std::string Response;
81 while (true) {
82 char Buf[1024];
83 auto Read = read(Out, Buf, sizeof(Buf));
84 if (Read == 0)
85 break;
86 if (Read < 0)
87 throw std::system_error(errno, std::generic_category());
88 // Otherwise, append it to "response"
89 Response.append(Buf, Read);
90 }
91
92 TextEdit E{{{0, 0}, {INT_MAX, INT_MAX}}, Response};
93 Reply(std::vector{E});
94 };
95
96 boost::asio::post(Pool, std::move(Action));
97}
std::optional< Draft > getDraft(PathRef File) const
Whether current platform treats paths case insensitively.
Definition Connection.h:11
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&...Vals)
Definition Logger.h:70
llvm::StringRef PathRef
Definition Path.h:27
constexpr std::string_view Exit
Definition AttrSet.h:34
int forkPiped(int &In, int &Out, int &Err)
fork this process and create some pipes connected to the new process.
Definition ForkPiped.cpp:8
bool fromJSON(const llvm::json::Value &Params, Configuration::Diagnostic &R, llvm::json::Path P)
struct nixd::Configuration::Formatting formatting