From b3d257d8b3913d8c84db40975611df1574a59dcd Mon Sep 17 00:00:00 2001 From: Grafcube Date: Wed, 19 Jun 2024 21:01:57 +0530 Subject: [PATCH] nixd/hover: Implement hover documentation for options --- nixd/lib/Controller/Hover.cpp | 64 ++++++++++++++++++++- nixd/tools/nixd/test/hover-options.md | 83 +++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 nixd/tools/nixd/test/hover-options.md diff --git a/nixd/lib/Controller/Hover.cpp b/nixd/lib/Controller/Hover.cpp index 597240483..8de022c9d 100644 --- a/nixd/lib/Controller/Hover.cpp +++ b/nixd/lib/Controller/Hover.cpp @@ -22,6 +22,31 @@ using namespace lspserver; namespace { +class OptionsHoverProvider { + AttrSetClient &Client; + +public: + OptionsHoverProvider(AttrSetClient &Client) : Client(Client) {} + std::optional resolveHover(std::vector Scope, + std::string Name) { + std::binary_semaphore Ready(0); + std::optional Desc; + auto OnReply = [&Ready, &Desc](llvm::Expected Resp) { + if (Resp) + Desc = *Resp; + else + elog("options hover: {0}", Resp.takeError()); + Ready.release(); + }; + + Client.optionInfo(Scope, std::move(OnReply)); + Scope.emplace_back(std::move(Name)); + Ready.acquire(); + + return Desc; + } +}; + /// \brief Provide package information, library information ... , from nixpkgs. class NixpkgsHoverProvider { AttrSetClient &NixpkgsClient; @@ -46,8 +71,7 @@ class NixpkgsHoverProvider { } if (Package.Description) { - OS << "## Description" - << "\n\n"; + OS << "## Description" << "\n\n"; OS << *Package.Description; OS << "\n\n"; @@ -122,6 +146,42 @@ void Controller::onHover(const TextDocumentPositionParams &Params, return; } } + + std::vector Scope; + auto R = findAttrPath(*N, PM, Scope); + if (R == FindAttrPathResult::OK) { + std::lock_guard _(OptionsLock); + for (const auto &[_, Client] : Options) { + if (AttrSetClient *C = Client->client()) { + OptionsHoverProvider OHP(*C); + std::optional Desc = + OHP.resolveHover(Scope, Name); + std::string Docs = ""; + if (Desc) { + if (Desc->Type) { + std::string TypeName = Desc->Type->Name.value_or(""); + std::string TypeDesc = Desc->Type->Description.value_or(""); + Docs += llvm::formatv("{0} ({1})", TypeName, TypeDesc); + } else { + Docs += "? (missing type)"; + } + if (Desc->Description) { + Docs += "\n" + Desc->Description.value_or(""); + } + Reply(Hover{ + .contents = + MarkupContent{ + .kind = MarkupKind::Markdown, + .value = std::move(Docs), + }, + .range = toLSPRange(N->range()), + }); + return; + } + } + } + } + // Reply it's kind by static analysis // FIXME: support more. Reply(Hover{ diff --git a/nixd/tools/nixd/test/hover-options.md b/nixd/tools/nixd/test/hover-options.md new file mode 100644 index 000000000..e7abc08d2 --- /dev/null +++ b/nixd/tools/nixd/test/hover-options.md @@ -0,0 +1,83 @@ +# RUN: nixd --lit-test \ +# RUN: --nixos-options-expr="{ foo.bar = { _type = \"option\"; }; }" \ +# 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":"{ foo.bar = 1 }" + } + } +} +``` + +<-- textDocument/hover(2) + + +```json +{ + "jsonrpc":"2.0", + "id":2, + "method":"textDocument/hover", + "params":{ + "textDocument":{ + "uri":"file:///basic.nix" + }, + "position":{ + "line":0, + "character":6 + } + } +} +``` + +``` + CHECK: "id": 2, +CHECK-NEXT: "jsonrpc": "2.0", +CHECK-NEXT: "result": { +CHECK-NEXT: "contents": { +CHECK-NEXT: "kind": "markdown", +CHECK-NEXT: "value": "`Identifer`" +CHECK-NEXT: }, +CHECK-NEXT: "range": { +CHECK-NEXT: "end": { +CHECK-NEXT: "character": 9, +CHECK-NEXT: "line": 0 +CHECK-NEXT: }, +CHECK-NEXT: "start": { +CHECK-NEXT: "character": 6, +CHECK-NEXT: "line": 0 +CHECK-NEXT: } +CHECK-NEXT: } +``` + +```json +{"jsonrpc":"2.0","method":"exit"} +```