-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
nixd: support
textDocument/inlayHint
(#414)
- Loading branch information
Showing
9 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/// \file | ||
/// \brief Implementation of [Inlay Hints]. | ||
/// [Inlay Hints]: | ||
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint | ||
/// | ||
/// In nixd, "Inlay Hints" are placed after each "package" node, showing it's | ||
/// version. | ||
/// | ||
/// For example | ||
/// | ||
/// nixd[: 1.2.3] | ||
/// nix[: 2.19.3] | ||
/// | ||
/// | ||
#include "AST.h" | ||
#include "Convert.h" | ||
|
||
#include "nixd/CommandLine/Options.h" | ||
#include "nixd/Controller/Controller.h" | ||
|
||
#include <boost/asio/post.hpp> | ||
|
||
#include <llvm/Support/CommandLine.h> | ||
|
||
#include <semaphore> | ||
|
||
using namespace nixd; | ||
using namespace nixf; | ||
using namespace lspserver; | ||
using namespace llvm::cl; | ||
|
||
namespace { | ||
|
||
opt<bool> EnableInlayHints{"inlay-hints", desc("Enable/Disable inlay-hints"), | ||
init(true), cat(NixdCategory)}; | ||
|
||
/// Ask nixpkgs provider to compute package information, to get inlay-hints. | ||
class NixpkgsInlayHintsProvider { | ||
AttrSetClient &NixpkgsProvider; | ||
const VariableLookupAnalysis &VLA; | ||
const ParentMapAnalysis &PMA; | ||
|
||
/// Only positions contained in this range should be computed && added; | ||
std::optional<nixf::PositionRange> Range; | ||
|
||
std::vector<InlayHint> &Hints; | ||
|
||
bool rangeOK(const nixf::PositionRange &R) { | ||
if (!Range) | ||
return true; // Always OK if there is no limitation. | ||
return Range->contains(R); | ||
} | ||
|
||
public: | ||
NixpkgsInlayHintsProvider(AttrSetClient &NixpkgsProvider, | ||
const VariableLookupAnalysis &VLA, | ||
const ParentMapAnalysis &PMA, | ||
std::optional<lspserver::Range> Range, | ||
std::vector<InlayHint> &Hints) | ||
: NixpkgsProvider(NixpkgsProvider), VLA(VLA), PMA(PMA), Hints(Hints) { | ||
if (Range) | ||
this->Range = toNixfRange(*Range); | ||
} | ||
|
||
void dfs(const Node *N) { | ||
if (!N) | ||
return; | ||
if (N->kind() == Node::NK_ExprVar) { | ||
if (havePackageScope(*N, VLA, PMA)) { | ||
if (!rangeOK(N->positionRange())) | ||
return; | ||
// Ask nixpkgs eval to provide it's information. | ||
// This is relatively slow. Maybe better query a set of packages in the | ||
// future? | ||
std::binary_semaphore Ready(0); | ||
const std::string &Name = static_cast<const ExprVar &>(*N).id().name(); | ||
AttrPathInfoResponse R; | ||
auto OnReply = [&Ready, &R](llvm::Expected<AttrPathInfoResponse> Resp) { | ||
if (!Resp) { | ||
Ready.release(); | ||
return; | ||
} | ||
R = *Resp; | ||
Ready.release(); | ||
}; | ||
NixpkgsProvider.attrpathInfo({Name}, std::move(OnReply)); | ||
Ready.acquire(); | ||
|
||
if (R.Version) { | ||
// Construct inlay hints. | ||
InlayHint H{ | ||
.position = toLSPPosition(N->rCur()), | ||
.label = ": " + *R.Version, | ||
.kind = InlayHintKind::Designator, | ||
.range = toLSPRange(N->range()), | ||
}; | ||
Hints.emplace_back(std::move(H)); | ||
} | ||
} | ||
} | ||
// FIXME: process other node kinds. e.g. ExprSelect. | ||
for (const Node *Ch : N->children()) | ||
dfs(Ch); | ||
} | ||
}; | ||
|
||
} // namespace | ||
|
||
void Controller::onInlayHint(const InlayHintsParams &Params, | ||
Callback<std::vector<InlayHint>> Reply) { | ||
// If not enabled, exit early. | ||
if (!EnableInlayHints) | ||
return Reply(std::vector<InlayHint>{}); | ||
|
||
auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri, | ||
Range = Params.range, this]() mutable { | ||
std::string File(URI.file()); | ||
if (std::shared_ptr<NixTU> TU = getTU(File, Reply)) [[likely]] { | ||
if (std::shared_ptr<Node> AST = getAST(*TU, Reply)) [[likely]] { | ||
// Perform inlay hints computation on the range. | ||
std::vector<InlayHint> Response; | ||
NixpkgsInlayHintsProvider NP(*nixpkgsClient(), *TU->variableLookup(), | ||
*TU->parentMap(), Range, Response); | ||
NP.dfs(AST.get()); | ||
Reply(std::move(Response)); | ||
} | ||
} | ||
}; | ||
boost::asio::post(Pool, std::move(Action)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# RUN: nixd --lit-test \ | ||
# RUN: --nixpkgs-expr="{ hello.version = \"0.3.12\"; }" \ | ||
# RUN: < %s | FileCheck %s | ||
|
||
<-- initialize(0) | ||
|
||
```json | ||
{ | ||
"jsonrpc":"2.0", | ||
"id":0, | ||
"method":"initialize", | ||
"params":{ | ||
"processId":123, | ||
"rootPath":"", | ||
"capabilities":{ | ||
}, | ||
"trace":"off" | ||
} | ||
} | ||
``` | ||
|
||
```json | ||
{ | ||
"jsonrpc":"2.0", | ||
"method":"textDocument/didOpen", | ||
"params":{ | ||
"textDocument":{ | ||
"uri":"file:///basic.nix", | ||
"languageId":"nix", | ||
"version":1, | ||
"text":"with pkgs; [ hello ]" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
|
||
|
||
```json | ||
{ | ||
"jsonrpc": "2.0", | ||
"id": 1, | ||
"method": "textDocument/inlayHint", | ||
"params": { | ||
"textDocument":{ | ||
"uri":"file:///basic.nix" | ||
}, | ||
"range": { | ||
"start":{ | ||
"line": 0, | ||
"character":0 | ||
}, | ||
"end":{ | ||
"line":0, | ||
"character":20 | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
``` | ||
CHECK: "id": 1, | ||
CHECK-NEXT: "jsonrpc": "2.0", | ||
CHECK-NEXT: "result": [ | ||
CHECK-NEXT: { | ||
CHECK-NEXT: "label": ": 0.3.12", | ||
CHECK-NEXT: "paddingLeft": false, | ||
CHECK-NEXT: "paddingRight": false, | ||
CHECK-NEXT: "position": { | ||
CHECK-NEXT: "character": 18, | ||
CHECK-NEXT: "line": 0 | ||
CHECK-NEXT: } | ||
CHECK-NEXT: } | ||
``` | ||
|
||
|
||
```json | ||
{"jsonrpc":"2.0","method":"exit"} | ||
``` |