Skip to content

Commit

Permalink
nixd/fork: completion regression
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Jun 2, 2023
1 parent 689dfc0 commit 0084324
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 121 deletions.
3 changes: 1 addition & 2 deletions lib/lspserver/include/lspserver/Connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class MessageHandler {
};

class InboundPort {
private:
public:
int In;

JSONStreamStyle StreamStyle = JSONStreamStyle::Standard;
Expand All @@ -40,7 +40,6 @@ class InboundPort {

bool readDelimitedMessage(std::string &JSONString);

public:
InboundPort(int In = STDIN_FILENO,
JSONStreamStyle StreamStyle = JSONStreamStyle::Standard)
: In(In), StreamStyle(StreamStyle){};
Expand Down
36 changes: 22 additions & 14 deletions lib/lspserver/include/lspserver/LSPServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,50 @@ class LSPServer : public MessageHandler {
int bindReply(Callback<llvm::json::Value>);

void callMethod(llvm::StringRef Method, llvm::json::Value Params,
Callback<llvm::json::Value> CB) {
Callback<llvm::json::Value> CB, OutboundPort *O) {
llvm::json::Value ID(bindReply(std::move(CB)));
log("--> call {0}({1})", Method, ID.getAsInteger());
Out->call(Method, Params, ID);
O->call(Method, Params, ID);
}

protected:
HandlerRegistry Registry;
template <class T>
llvm::unique_function<void(const T &)>
mkOutNotifiction(llvm::StringRef Method) {
return [Method = Method, this](const T &Params) {
mkOutNotifiction(llvm::StringRef Method, OutboundPort *O = nullptr) {
if (!O)
O = Out.get();
return [=, this](const T &Params) {
log("--> notify {0}", Method);
Out->notify(Method, Params);
O->notify(Method, Params);
};
}

template <class ParamTy, class ResponseTy>
llvm::unique_function<void(const ParamTy &, Callback<ResponseTy>)>
mkOutMethod(llvm::StringRef Method) {
mkOutMethod(llvm::StringRef Method, OutboundPort *O = nullptr) {
if (!O)
O = Out.get();
return [=, this](const ParamTy &Params, Callback<ResponseTy> Reply) {
callMethod(Method, Params,
[=, Reply = std::move(Reply)](
llvm::Expected<llvm::json::Value> Response) mutable {
if (!Response)
return Reply(Response.takeError());
Reply(parseParam<ResponseTy>(std::move(*Response), Method,
"reply"));
});
callMethod(
Method, Params,
[=, Reply = std::move(Reply)](
llvm::Expected<llvm::json::Value> Response) mutable {
if (!Response)
return Reply(Response.takeError());
Reply(
parseParam<ResponseTy>(std::move(*Response), Method, "reply"));
},
O);
};
}

public:
LSPServer(std::unique_ptr<InboundPort> In, std::unique_ptr<OutboundPort> Out)
: In(std::move(In)), Out(std::move(Out)){};
void run();

void switchStreamStyle(JSONStreamStyle Style) { In->StreamStyle = Style; }
};

} // namespace lspserver
5 changes: 3 additions & 2 deletions lib/lspserver/src/Connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,13 @@ bool InboundPort::readStandardMessage(std::string &JSONString) {
JSONString.resize(ContentLength);
for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {

Read = read(In, &JSONString[Pos], ContentLength - Pos);
Read = read(In, JSONString.data() + Pos, ContentLength - Pos);

if (Read == 0) {
elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
ContentLength);
return false;
}
Pos += Read;
}
return true;
}
Expand Down Expand Up @@ -200,6 +199,8 @@ void InboundPort::loop(MessageHandler &Handler) {
if (!dispatch(*ExpectedParsedJSON, Handler))
return;
} else {
auto Err = ExpectedParsedJSON.takeError();
elog("The received json cannot be parsed, reason: {0}", Err);
return;
}
} else {
Expand Down
2 changes: 2 additions & 0 deletions lib/nixd/include/nixd/Diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace nixd {

std::string stripANSI(std::string Msg);

std::vector<lspserver::Diagnostic> mkDiagnostics(const nix::BaseError &);

lspserver::Position translatePosition(const nix::AbstractPos &P);
Expand Down
36 changes: 2 additions & 34 deletions lib/nixd/include/nixd/EvalDraftStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,40 +93,8 @@ struct IValueEvalResult {
EvalASTForest Forest;
std::unique_ptr<IValueEvalSession> Session;

static const IValueEvalResult *
search(const std::string &Path,
const std::vector<const IValueEvalResult *> &Order) {
for (const auto &Result : Order) {
try {
Result->Forest.at(Path);
return Result;
} catch (...) {
}
}
return nullptr;
}
IValueEvalResult(decltype(Forest) Forest, decltype(Session) Session)
: Forest(std::move(Forest)), Session(std::move(Session)) {}
};

/// Ownes `EvalASTForest`s, mark it is evaluated or not, clients query on this
/// cache to find a suitable AST Tree
struct ForestCache {

IValueEvalResult EvaluatedResult;

IValueEvalResult NonEmptyResult;

enum class ASTPreference { Evaluated, NonEmpty };

[[nodiscard]] const IValueEvalResult *
searchAST(const std::string &Path, ASTPreference Preference) const {
switch (Preference) {
case ASTPreference::Evaluated:
return IValueEvalResult::search(Path,
{&EvaluatedResult, &NonEmptyResult});
case ASTPreference::NonEmpty:
return IValueEvalResult::search(Path,
{&NonEmptyResult, &EvaluatedResult});
}
};
};
} // namespace nixd
49 changes: 42 additions & 7 deletions lib/nixd/include/nixd/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,29 @@

#include <llvm/ADT/FunctionExtras.h>
#include <llvm/Support/JSON.h>
#include <llvm/Support/raw_ostream.h>

#include <cstdint>
#include <memory>

namespace nixd {
// Extension to `lspserver`
namespace lspserver {

llvm::json::Value toJSON(const CompletionContext &R);

llvm::json::Value toJSON(const TextDocumentPositionParams &R);

llvm::json::Value toJSON(const CompletionParams &R);

bool fromJSON(const llvm::json::Value &Params, CompletionItem &R,
llvm::json::Path P);

bool fromJSON(const llvm::json::Value &Params, CompletionList &R,
llvm::json::Path P);

} // namespace lspserver

namespace nixd { // namespace nixd

namespace configuration {

Expand Down Expand Up @@ -75,6 +94,10 @@ bool fromJSON(const llvm::json::Value &, lspserver::PublishDiagnosticsParams &,
bool fromJSON(const llvm::json::Value &, Diagnostics &, llvm::json::Path);
llvm::json::Value toJSON(const Diagnostics &);

struct Completion : WorkerMessage {
lspserver::CompletionList List;
};

} // namespace ipc

/// The server instance, nix-related language features goes here
Expand All @@ -94,15 +117,20 @@ class Server : public lspserver::LSPServer {
struct Proc {
std::unique_ptr<nix::Pipe> ToPipe;
std::unique_ptr<nix::Pipe> FromPipe;
std::unique_ptr<lspserver::OutboundPort> OutPort;
std::unique_ptr<llvm::raw_ostream> OwnedStream;

nix::Pid Pid;
WorkspaceVersionTy WorkspaceVersion;
std::thread InputDispatcher;

Proc(decltype(ToPipe) ToPipe, decltype(FromPipe) FromPipe, pid_t Pid,
decltype(WorkspaceVersion) WorkspaceVersion,
Proc(decltype(ToPipe) ToPipe, decltype(FromPipe) FromPipe,
decltype(OutPort) OutPort, decltype(OwnedStream) OwnedStream,
pid_t Pid, decltype(WorkspaceVersion) WorkspaceVersion,
decltype(InputDispatcher) InputDispatcher)
: ToPipe(std::move(ToPipe)), FromPipe(std::move(FromPipe)), Pid(Pid),
WorkspaceVersion(WorkspaceVersion),
: ToPipe(std::move(ToPipe)), FromPipe(std::move(FromPipe)),
OutPort(std::move(OutPort)), OwnedStream(std::move(OwnedStream)),
Pid(Pid), WorkspaceVersion(WorkspaceVersion),
InputDispatcher(std::move(InputDispatcher)) {}

[[nodiscard]] nix::AutoCloseFD to() const {
Expand Down Expand Up @@ -155,7 +183,8 @@ class Server : public lspserver::LSPServer {
//---------------------------------------------------------------------------/
// Worker members
llvm::unique_function<void(const ipc::Diagnostics &)> WorkerDiagnostic;
ForestCache FCache;

std::unique_ptr<IValueEvalResult> IER;

public:
Server(std::unique_ptr<lspserver::InboundPort> In,
Expand Down Expand Up @@ -199,11 +228,17 @@ class Server : public lspserver::LSPServer {
const lspserver::DidChangeConfigurationParams &) {
fetchConfig();
}

void onHover(const lspserver::TextDocumentPositionParams &,
lspserver::Callback<llvm::json::Value>);
void onWorkerHover(const lspserver::TextDocumentPositionParams &,
lspserver::Callback<llvm::json::Value>);

void onCompletion(const lspserver::CompletionParams &,
lspserver::Callback<llvm::json::Value>);
lspserver::Callback<lspserver::CompletionList>);

void onWorkerCompletion(const lspserver::CompletionParams &,
lspserver::Callback<llvm::json::Value>);
};

}; // namespace nixd
5 changes: 2 additions & 3 deletions lib/nixd/src/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
FUNC(ANSI_BLUE) \
FUNC(ANSI_MAGENTA) \
FUNC(ANSI_CYAN)
namespace nixd {

static std::string stripANSI(std::string Msg){
std::string stripANSI(std::string Msg) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#define REMOVE_ANSI_STR_FUNC(ANSI_STR) \
Expand All @@ -32,8 +33,6 @@ static std::string stripANSI(std::string Msg){
return Msg;
}

namespace nixd {

std::vector<lspserver::Diagnostic> mkDiagnostics(const nix::BaseError &Err) {
std::vector<lspserver::Diagnostic> Ret;
auto ErrPos = Err.info().errPos;
Expand Down
84 changes: 81 additions & 3 deletions lib/nixd/src/ServerController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,17 @@ void Server::updateWorkspaceVersion() {
IPort->loop(*this);
});
WorkerInputDispatcher.detach();
auto WorkerProc = std::make_unique<Proc>(std::move(To), std::move(From),
ForkPID, WorkspaceVersion,
std::move(WorkerInputDispatcher));

auto ProcFdStream =
std::make_unique<llvm::raw_fd_ostream>(To->writeSide.get(), false);

auto OutPort =
std::make_unique<lspserver::OutboundPort>(*ProcFdStream, false);

auto WorkerProc = std::make_unique<Proc>(
std::move(To), std::move(From), std::move(OutPort),
std::move(ProcFdStream), ForkPID, WorkspaceVersion,
std::move(WorkerInputDispatcher));

Workers.emplace_back(std::move(WorkerProc));
if (Workers.size() > 5) {
Expand Down Expand Up @@ -183,6 +191,9 @@ Server::Server(std::unique_ptr<lspserver::InboundPort> In,
/// IPC
Registry.addNotification("nixd/worker/diagnostic", this,
&Server::onWorkerDiagnostic);

Registry.addMethod("nixd/worker/textDocument/completion", this,
&Server::onWorkerCompletion);
}

void Server::onDocumentDidOpen(
Expand Down Expand Up @@ -256,4 +267,71 @@ void Server::onWorkerDiagnostic(const ipc::Diagnostics &Diag) {
}
}

//-----------------------------------------------------------------------------/
// Completion

void Server::onCompletion(
const lspserver::CompletionParams &Params,
lspserver::Callback<lspserver::CompletionList> Reply) {
auto Thread = std::thread([=, Reply = std::move(Reply), this]() mutable {
// For all active workers, send the completion request

std::vector<lspserver::CompletionList> ListStore(Workers.size());

auto *StorePtr = &ListStore;

size_t I = 0;
for (const auto &Worker : Workers) {
auto ComplectionRequest =
mkOutMethod<lspserver::CompletionParams, lspserver::CompletionList>(
"nixd/worker/textDocument/completion", Worker->OutPort.get());

ComplectionRequest(
Params,
[I, &StorePtr](llvm::Expected<lspserver::CompletionList> Result) {
// The worker answered our request, fill the completion
// lists then.
if (Result) {
lspserver::log(
"received result from our client, which has {0} item(s)",
Result.get().items.size());
if (StorePtr) {
(*StorePtr)[I] = Result.get();
} else if (!Result.get().items.empty()) {
lspserver::elog(
"ignored non-empty response, because it's to late.");
}
}
});
I++;
}
// Wait for our client, this is currently hardcoded
usleep(5e5);

// Reset the store ptr in event handling module, so that client reply after
// 'usleep' will not write the store (to avoid data race)
StorePtr = nullptr;

// brute-force iterating over the result, and choose the biggest item
size_t BestIdx = 0;
size_t BestSize = 0;
for (size_t I = 0; I < ListStore.size(); I++) {
auto LSize = ListStore[I].items.size();
if (LSize >= BestSize) {
BestIdx = I;
BestSize = LSize;
}
}
// And finally, reply our client
lspserver::log("chosed {0} completion lists.", BestSize);
Reply(ListStore.at(BestIdx));
ListStore.resize(0);
});

Thread.detach();
}

void Server::onHover(const lspserver::TextDocumentPositionParams &,
lspserver::Callback<llvm::json::Value>) {}

} // namespace nixd

0 comments on commit 0084324

Please sign in to comment.