Skip to content

Commit

Permalink
nixd: provide package information on hover request (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc authored Apr 19, 2024
1 parent 24c9e40 commit 2e51fe0
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 7 deletions.
102 changes: 95 additions & 7 deletions nixd/lib/Controller/Hover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,90 @@
/// [Hover Request]:
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover

#include "AST.h"
#include "Convert.h"

#include "nixd/Controller/Controller.h"

#include <llvm/Support/Error.h>

#include <boost/asio/post.hpp>

#include <mutex>
#include <llvm/Support/Error.h>

namespace nixd {
#include <semaphore>

using namespace nixd;
using namespace llvm::json;
using namespace nixf;
using namespace lspserver;
using namespace rpc;

namespace {

/// \brief Provide package information, library information ... , from nixpkgs.
class NixpkgsHoverProvider {
AttrSetClient &NixpkgsClient;

/// \brief Make markdown documentation by package description
///
/// FIXME: there are many markdown generation in language server.
/// Maybe we can add structured generating first?
static std::string mkMarkdown(const PackageDescription &Package) {
std::ostringstream OS;
// Make each field a new section

if (Package.Name) {
OS << "`" << *Package.Name << "`";
OS << "\n";
}

// Make links to homepage.
if (Package.Homepage) {
OS << "[homepage](" << *Package.Homepage << ")";
OS << "\n";
}

if (Package.Description) {
OS << "## Description"
<< "\n\n";
OS << *Package.Description;
OS << "\n\n";

if (Package.LongDescription) {
OS << "\n\n";
OS << *Package.LongDescription;
OS << "\n\n";
}
}

return OS.str();
}

public:
NixpkgsHoverProvider(AttrSetClient &NixpkgsClient)
: NixpkgsClient(NixpkgsClient) {}

std::optional<std::string> resolvePackage(std::vector<std::string> Scope,
std::string Name) {
std::binary_semaphore Ready(0);
std::optional<PackageDescription> Desc;
auto OnReply = [&Ready, &Desc](llvm::Expected<AttrPathInfoResponse> Resp) {
if (Resp)
Desc = *Resp;
else
elog("nixpkgs provider: {0}", Resp.takeError());
Ready.release();
};
Scope.emplace_back(std::move(Name));
NixpkgsClient.attrpathInfo(Scope, std::move(OnReply));
Ready.acquire();

if (!Desc)
return std::nullopt;

return mkMarkdown(*Desc);
}
};

} // namespace

void Controller::onHover(const TextDocumentPositionParams &Params,
Callback<std::optional<Hover>> Reply) {
Expand All @@ -33,6 +102,27 @@ void Controller::onHover(const TextDocumentPositionParams &Params,
return;
}
std::string Name = N->name();
const VariableLookupAnalysis &VLA = *TU->variableLookup();
const ParentMapAnalysis &PM = *TU->parentMap();
if (havePackageScope(*N, VLA, PM) && nixpkgsClient()) {
// Ask nixpkgs client what's current package documentation.
NixpkgsHoverProvider NHP(*nixpkgsClient());
auto [Scope, Name] = getScopeAndPrefix(*N, PM);
if (std::optional<std::string> Doc =
NHP.resolvePackage(Scope, Name)) {
Reply(Hover{
.contents =
MarkupContent{
.kind = MarkupKind::Markdown,
.value = std::move(*Doc),
},
.range = toLSPRange(N->range()),
});
return;
}
}
// Reply it's kind by static analysis
// FIXME: support more.
Reply(Hover{
.contents =
MarkupContent{
Expand All @@ -46,5 +136,3 @@ void Controller::onHover(const TextDocumentPositionParams &Params,
};
boost::asio::post(Pool, std::move(Action));
}

} // namespace nixd
83 changes: 83 additions & 0 deletions nixd/tools/nixd/test/hover-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# RUN: nixd --lit-test \
# RUN: --nixpkgs-expr="{ hello.meta.description = \"Very Nice\"; }" \
# RUN: < %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":"with pkgs; hello"
}
}
}
```

<-- textDocument/hover(2)


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

```
CHECK: "id": 2,
CHECK-NEXT: "jsonrpc": "2.0",
CHECK-NEXT: "result": {
CHECK-NEXT: "contents": {
CHECK-NEXT: "kind": "markdown",
CHECK-NEXT: "value": "## Description\n\nVery Nice\n\n"
CHECK-NEXT: },
CHECK-NEXT: "range": {
CHECK-NEXT: "end": {
CHECK-NEXT: "character": 17,
CHECK-NEXT: "line": 0
CHECK-NEXT: },
CHECK-NEXT: "start": {
CHECK-NEXT: "character": 12,
CHECK-NEXT: "line": 0
CHECK-NEXT: }
CHECK-NEXT: }
```

```json
{"jsonrpc":"2.0","method":"exit"}
```

0 comments on commit 2e51fe0

Please sign in to comment.