Skip to content

Commit

Permalink
nixd/declaration: goto declaration support for options
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Jun 8, 2023
1 parent 8c3351e commit 49744c6
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
12 changes: 12 additions & 0 deletions lib/lspserver/src/SourceCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ llvm::Expected<size_t> positionToOffset(llvm::StringRef Code, Position P,
return StartOfLine + ByteInLine;
}

Position offsetToPosition(llvm::StringRef Code, size_t Offset) {
Offset = std::min(Code.size(), Offset);
llvm::StringRef Before = Code.substr(0, Offset);
int Lines = Before.count('\n');
size_t PrevNL = Before.rfind('\n');
size_t StartOfLine = (PrevNL == llvm::StringRef::npos) ? 0 : (PrevNL + 1);
Position Pos;
Pos.line = Lines;
Pos.character = lspLength(Before.substr(StartOfLine));
return Pos;
}

// Workaround for editors that have buggy handling of newlines at end of file.
//
// The editor is supposed to expose document contents over LSP as an exact
Expand Down
6 changes: 6 additions & 0 deletions lib/nixd/include/nixd/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ class Server : public lspserver::LSPServer {

// Controller Methods

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

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

Expand All @@ -283,6 +286,9 @@ class Server : public lspserver::LSPServer {

// Worker

void onOptionDeclaration(const ipc::AttrPathParams &,
lspserver::Callback<lspserver::Location>);

void onWorkerDefinition(const lspserver::TextDocumentPositionParams &,
lspserver::Callback<lspserver::Location>);

Expand Down
57 changes: 57 additions & 0 deletions lib/nixd/src/ServerController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "lspserver/Logger.h"
#include "lspserver/Path.h"
#include "lspserver/Protocol.h"
#include "lspserver/SourceCode.h"
#include "lspserver/URI.h"

#include <llvm/ADT/FunctionExtras.h>
Expand All @@ -29,6 +30,7 @@
#include <memory>
#include <mutex>
#include <optional>
#include <ranges>
#include <sstream>
#include <stdexcept>
#include <string>
Expand Down Expand Up @@ -213,6 +215,7 @@ Server::Server(std::unique_ptr<lspserver::InboundPort> In,
// Language Features
Registry.addMethod("textDocument/hover", this, &Server::onHover);
Registry.addMethod("textDocument/completion", this, &Server::onCompletion);
Registry.addMethod("textDocument/declaration", this, &Server::onDecalration);
Registry.addMethod("textDocument/definition", this, &Server::onDefinition);
Registry.addMethod("textDocument/formatting", this, &Server::onFormat);

Expand All @@ -239,6 +242,9 @@ Server::Server(std::unique_ptr<lspserver::InboundPort> In,
Registry.addMethod("nixd/ipc/textDocument/definition", this,
&Server::onWorkerDefinition);

Registry.addMethod("nixd/ipc/option/textDocument/declaration", this,
&Server::onOptionDeclaration);

readJSONConfig();
}

Expand All @@ -255,6 +261,7 @@ void Server::onInitialize(const lspserver::InitializeParams &InitializeParams,
{"change", (int)lspserver::TextDocumentSyncKind::Incremental},
{"save", true},
}},
{"declarationProvider", true},
{"definitionProvider", true},
{"hoverProvider", true},
{"documentFormattingProvider", true},
Expand Down Expand Up @@ -312,6 +319,56 @@ void Server::onDocumentDidClose(
//-----------------------------------------------------------------------------/
// Language Features

void Server::onDecalration(const lspserver::TextDocumentPositionParams &Params,
lspserver::Callback<llvm::json::Value> Reply) {
auto Thread = std::thread([=, Reply = std::move(Reply), this]() mutable {
ReplyRAII<llvm::json::Value> RR(std::move(Reply));

// Set the default response to "null", instead of errors
RR.Response = nullptr;

ipc::AttrPathParams APParams;

// Try to get current attribute path, expand the position

auto Code = llvm::StringRef(
*DraftMgr.getDraft(Params.textDocument.uri.file())->Contents);
auto ExpectedOffset = positionToOffset(Code, Params.position);

if (!ExpectedOffset)
RR.Response = ExpectedOffset.takeError();

auto Offset = ExpectedOffset.get();
auto From = Offset;
auto To = Offset;

std::string Punc = "\r\n\t ;";

auto IsPunc = [&Punc](char C) {
return std::ranges::any_of(Punc, [C](char Ck) { return Ck == C; });
};

for (; From >= 0 && !IsPunc(Code[From]);)
From--;
for (; To < Code.size() && !IsPunc(Code[To]);)
To++;

APParams.Path = Code.substr(From, To - From).trim(Punc);
lspserver::log("requesting path: {0}", APParams.Path);

auto Responses = askWorkers<ipc::AttrPathParams, lspserver::Location>(
OptionWorkers, "nixd/ipc/option/textDocument/declaration", APParams,
2e4);

if (Responses.empty())
return;

RR.Response = std::move(Responses.back());
});

Thread.detach();
}

void Server::onDefinition(const lspserver::TextDocumentPositionParams &Params,
lspserver::Callback<llvm::json::Value> Reply) {
auto Thread = std::thread([=, Reply = std::move(Reply), this]() mutable {
Expand Down
54 changes: 54 additions & 0 deletions lib/nixd/src/ServerWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,60 @@ void Server::evalInstallable(lspserver::PathRef File, int Depth = 0) {
std::move(Session));
}

void Server::onOptionDeclaration(
const ipc::AttrPathParams &Params,
lspserver::Callback<lspserver::Location> Reply) {
assert(Role == ServerRole::OptionProvider &&
"option declaration should be calculated in option worker!");
using namespace lspserver;
ReplyRAII<Location> RR(std::move(Reply));
if (OptionAttrSet->type() != nix::ValueType::nAttrs)
return;

try {
nix::Value *V = OptionAttrSet;
if (!Params.Path.empty()) {
auto &Bindings(*OptionIES->getState()->allocBindings(0));
V = nix::findAlongAttrPath(*OptionIES->getState(), Params.Path, Bindings,
*OptionAttrSet)
.first;
}

auto &State = *OptionIES->getState();
State.forceValue(*V, nix::noPos);

auto *VDecl = nix::findAlongAttrPath(State, "declarations",
*State.allocBindings(0), *V)
.first;

State.forceValue(*VDecl, nix::noPos);

// declarations should be a list containing file paths
if (!VDecl->isList())
return;

for (auto *VFile : VDecl->listItems()) {
State.forceValue(*VFile, nix::noPos);
if (VFile->type() == nix::ValueType::nString) {
auto File = VFile->str();
Location L;
L.uri = URIForFile::canonicalize(File, File);

// Where is the range?
L.range.start = {0, 0};
L.range.end = {0, 0};

RR.Response = L;
return;
}
}

} catch (std::exception &E) {
RR.Response = error(stripANSI(E.what()));
} catch (...) {
}
}

void Server::onWorkerDefinition(
const lspserver::TextDocumentPositionParams &Params,
lspserver::Callback<lspserver::Location> Reply) {
Expand Down

0 comments on commit 49744c6

Please sign in to comment.