Skip to content

Commit

Permalink
nixd: goto definition(declaration) for nixos options (#433)
Browse files Browse the repository at this point in the history
Fixes: #265
  • Loading branch information
inclyc authored Apr 19, 2024
1 parent 14bb8d8 commit 51f7ce3
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 22 deletions.
2 changes: 1 addition & 1 deletion nixd/include/nixd/Controller/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Controller : public lspserver::LSPServer {
lspserver::Callback<lspserver::CompletionItem> Reply);

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

void
onReferences(const lspserver::TextDocumentPositionParams &Params,
Expand Down
4 changes: 3 additions & 1 deletion nixd/lib/Controller/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,9 @@ nixd::findAttrPath(const nixf::Node &N, const nixf::ParentMapAnalysis &PM) {
AttrPath = getValueAttrPath(*Expr, PM);
auto Select = getSelectAttrPath(static_cast<const AttrName &>(*Name), PM);
AttrPath.insert(AttrPath.end(), Select.begin(), Select.end());
assert(!AttrPath.empty());
// Select "AttrPath" might come from "inherit", thus it can be empty.
if (AttrPath.empty())
return std::nullopt;
return AttrPath;
}

Expand Down
81 changes: 71 additions & 10 deletions nixd/lib/Controller/Definition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ using namespace llvm;

using LookupResult = VariableLookupAnalysis::LookupResult;
using ResultKind = VariableLookupAnalysis::LookupResultKind;
using Locations = std::vector<Location>;

namespace {

Expand Down Expand Up @@ -165,6 +166,36 @@ class NixpkgsDefinitionProvider {
}
};

/// \brief Try to get "location" by invoking options worker
class OptionsDefinitionProvider {
AttrSetClient &Client;

public:
OptionsDefinitionProvider(AttrSetClient &Client) : Client(Client) {}
void resolveLocations(const std::vector<std::string> &Params,
Locations &Locs) {
std::binary_semaphore Ready(0);
Expected<OptionInfoResponse> Info = error("not replied");
OptionCompleteResponse Names;
auto OnReply = [&Ready, &Info](llvm::Expected<OptionInfoResponse> Resp) {
Info = std::move(Resp);
Ready.release();
};
// Send request.

Client.optionInfo(Params, std::move(OnReply));
Ready.acquire();

if (!Info) {
elog("getting locations: {0}", Info.takeError());
return;
}

for (const auto &Decl : Info->Declarations)
Locs.emplace_back(Decl);
}
};

} // namespace

Expected<const Definition &>
Expand All @@ -191,7 +222,7 @@ nixd::findDefinition(const Node &N, const ParentMapAnalysis &PMA,
}

void Controller::onDefinition(const TextDocumentPositionParams &Params,
Callback<Location> Reply) {
Callback<llvm::json::Value> Reply) {
auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri,
Pos = toNixfPosition(Params.position), this]() mutable {
std::string File(URI.file());
Expand All @@ -200,24 +231,54 @@ void Controller::onDefinition(const TextDocumentPositionParams &Params,
const VariableLookupAnalysis &VLA = *TU->variableLookup();
const ParentMapAnalysis &PM = *TU->parentMap();
const Node *N = AST->descend({Pos, Pos});
Locations Locs;
if (!N) [[unlikely]] {
Reply(error("cannot find AST node on given position"));
return;
}
if (std::optional<std::vector<std::string_view>> Path =
findAttrPath(*N, PM)) {
std::lock_guard _(OptionsLock);
// For each option worker, try to get it's decl position.
for (const auto &[_, Client] : Options) {
if (AttrSetClient *C = Client->client()) {
OptionsDefinitionProvider ODP(*C);
std::vector<std::string> Params;
Params.reserve(Path->size());
for (const auto &P : *Path)
Params.emplace_back(P);
ODP.resolveLocations(Params, Locs);
}
}
}
if (havePackageScope(*N, VLA, PM) && nixpkgsClient()) {
// Ask nixpkgs client what's current package documentation.
NixpkgsDefinitionProvider NDP(*nixpkgsClient());
auto [Scope, Name] = getScopeAndPrefix(*N, PM);
Expected<Location> Loc =
NDP.resolvePackage(std::move(Scope), std::move(Name));
if (Loc) {
Reply(std::move(*Loc));
return;
}
elog("cannot get nixpkgs definition for this package: {0}",
Loc.takeError());
if (Expected<Location> Loc =
NDP.resolvePackage(std::move(Scope), std::move(Name)))
Locs.emplace_back(*Loc);
else
elog("cannot get nixpkgs definition for package: {0}",
Loc.takeError());
}

if (Expected<Location> StaticLoc = staticDef(URI, *N, PM, VLA))
Locs.emplace_back(*StaticLoc);
else
elog("cannot get static def location: {0}", StaticLoc.takeError());

if (Locs.empty()) {
Reply(nullptr);
return;
}

if (Locs.size() == 1) {
Reply(Locs.back());
return;
}
Reply(staticDef(URI, *N, PM, VLA));

Reply(std::move(Locs));
return;
}
}
Expand Down
80 changes: 80 additions & 0 deletions nixd/tools/nixd/test/definition-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# RUN: nixd \
# RUN: --nixos-options-expr="{ foo.declarationPositions = [ { file = \"/foo\"; line = 8; column = 7; } ]; }" \
# RUN: --lit-test < %s | FileCheck %s

<-- initialize(0)

```json
{
"jsonrpc":"2.0",
"id":0,
"method":"initialize",
"params":{
"processId":123,
"rootPath":"",
"capabilities":{
},
"trace":"off"
}
}
```


<-- textDocument/didOpen

```json
{
"jsonrpc":"2.0",
"method":"textDocument/didOpen",
"params":{
"textDocument":{
"uri":"file:///basic.nix",
"languageId":"nix",
"version":1,
"text":"{ foo = 1; }"
}
}
}
```

<-- textDocument/definition(2)


```json
{
"jsonrpc":"2.0",
"id":2,
"method":"textDocument/definition",
"params":{
"textDocument":{
"uri":"file:///basic.nix"
},
"position":{
"line": 0,
"character":3
}
}
}
```

```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": {
CHECK-NEXT: "range": {
CHECK-NEXT: "end": {
CHECK-NEXT: "character": 7,
CHECK-NEXT: "line": 8
CHECK-NEXT: },
CHECK-NEXT: "start": {
CHECK-NEXT: "character": 7,
CHECK-NEXT: "line": 8
CHECK-NEXT: }
CHECK-NEXT: },
CHECK-NEXT: "uri": "file:///foo"
```


```json
{"jsonrpc":"2.0","method":"exit"}
```
35 changes: 25 additions & 10 deletions nixd/tools/nixd/test/definition-package.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,33 @@
```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": {
CHECK-NEXT: "range": {
CHECK-NEXT: "end": {
CHECK-NEXT: "character": 33,
CHECK-NEXT: "line": 33
CHECK-NEXT: "result": [
CHECK-NEXT: {
CHECK-NEXT: "range": {
CHECK-NEXT: "end": {
CHECK-NEXT: "character": 33,
CHECK-NEXT: "line": 33
CHECK-NEXT: },
CHECK-NEXT: "start": {
CHECK-NEXT: "character": 33,
CHECK-NEXT: "line": 33
CHECK-NEXT: }
CHECK-NEXT: },
CHECK-NEXT: "start": {
CHECK-NEXT: "character": 33,
CHECK-NEXT: "line": 33
CHECK-NEXT: }
CHECK-NEXT: "uri": "file:///foo"
CHECK-NEXT: },
CHECK-NEXT: "uri": "file:///foo"
CHECK-NEXT: {
CHECK-NEXT: "range": {
CHECK-NEXT: "end": {
CHECK-NEXT: "character": 4,
CHECK-NEXT: "line": 0
CHECK-NEXT: },
CHECK-NEXT: "start": {
CHECK-NEXT: "character": 0,
CHECK-NEXT: "line": 0
CHECK-NEXT: }
CHECK-NEXT: },
CHECK-NEXT: "uri": "file:///basic.nix"
CHECK-NEXT: }
```


Expand Down

0 comments on commit 51f7ce3

Please sign in to comment.