nixd
Loading...
Searching...
No Matches
InlayHints.cpp
Go to the documentation of this file.
1/// \file
2/// \brief Implementation of [Inlay Hints].
3/// [Inlay Hints]:
4/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint
5///
6/// In nixd, "Inlay Hints" are placed after each "package" node, showing it's
7/// version.
8///
9/// For example
10///
11/// nixd[: 1.2.3]
12/// nix[: 2.19.3]
13///
14///
15#include "AST.h"
16#include "Convert.h"
17
20
21#include <boost/asio/post.hpp>
22
23#include <llvm/ADT/StringRef.h>
24#include <llvm/Support/CommandLine.h>
25
26#include <semaphore>
27
28using namespace nixd;
29using namespace nixf;
30using namespace lspserver;
31using namespace llvm::cl;
32
33namespace {
34
35opt<bool> EnableInlayHints{"inlay-hints", desc("Enable/Disable inlay-hints"),
36 init(true), cat(NixdCategory)};
37
38/// Ask nixpkgs provider to compute package information, to get inlay-hints.
39class NixpkgsInlayHintsProvider {
40 AttrSetClient &NixpkgsProvider;
41 const VariableLookupAnalysis &VLA;
42 const ParentMapAnalysis &PMA;
43
44 /// Only positions contained in this range should be computed && added;
45 std::optional<nixf::PositionRange> Range;
46
47 std::vector<InlayHint> &Hints;
48
49 bool rangeOK(const nixf::PositionRange &R) {
50 if (!Range)
51 return true; // Always OK if there is no limitation.
52 return Range->contains(R);
53 }
54
55 llvm::StringRef Src;
56
57public:
58 NixpkgsInlayHintsProvider(AttrSetClient &NixpkgsProvider,
59 const VariableLookupAnalysis &VLA,
60 const ParentMapAnalysis &PMA,
61 std::optional<lspserver::Range> Range,
62 std::vector<InlayHint> &Hints, llvm::StringRef Src)
63 : NixpkgsProvider(NixpkgsProvider), VLA(VLA), PMA(PMA), Hints(Hints),
64 Src(Src) {
65 if (Range)
66 this->Range = toNixfRange(*Range);
67 }
68
69 void dfs(const Node *N) {
70 if (!N)
71 return;
72 if (N->kind() == Node::NK_ExprVar) {
73 if (havePackageScope(*N, VLA, PMA)) {
74 if (!rangeOK(N->positionRange()))
75 return;
76 // Ask nixpkgs eval to provide it's information.
77 // This is relatively slow. Maybe better query a set of packages in the
78 // future?
79 std::binary_semaphore Ready(0);
80 const std::string &Name = static_cast<const ExprVar &>(*N).id().name();
82 auto OnReply = [&Ready, &R](llvm::Expected<AttrPathInfoResponse> Resp) {
83 if (!Resp) {
84 Ready.release();
85 return;
86 }
87 R = *Resp;
88 Ready.release();
89 };
90 NixpkgsProvider.attrpathInfo({Name}, std::move(OnReply));
91 Ready.acquire();
92
93 if (const std::optional<std::string> &Version = R.PackageDesc.Version) {
94 // Construct inlay hints.
96 .position = toLSPPosition(Src, N->rCur()),
97 .label = ": " + *Version,
98 .kind = InlayHintKind::Designator,
99 .range = toLSPRange(Src, N->range()),
100 };
101 Hints.emplace_back(std::move(H));
102 }
103 }
104 }
105 // FIXME: process other node kinds. e.g. ExprSelect.
106 for (const Node *Ch : N->children())
107 dfs(Ch);
108 }
109};
110
111} // namespace
112
113void Controller::onInlayHint(const InlayHintsParams &Params,
114 Callback<std::vector<InlayHint>> Reply) {
115 // If not enabled, exit early.
116 if (!EnableInlayHints)
117 return Reply(std::vector<InlayHint>{});
118
119 auto Action = [Reply = std::move(Reply), URI = Params.textDocument.uri,
120 Range = Params.range, this]() mutable {
121 std::string File(URI.file());
122 if (std::shared_ptr<NixTU> TU = getTU(File, Reply)) [[likely]] {
123 if (std::shared_ptr<Node> AST = getAST(*TU, Reply)) [[likely]] {
124 // Perform inlay hints computation on the range.
125 std::vector<InlayHint> Response;
126 NixpkgsInlayHintsProvider NP(*nixpkgsClient(), *TU->variableLookup(),
127 *TU->parentMap(), Range, Response,
128 TU->src());
129 NP.dfs(AST.get());
130 Reply(std::move(Response));
131 }
132 }
133 };
134 boost::asio::post(Pool, std::move(Action));
135}
This file declares some common analysis (tree walk) on the AST.
Convert between LSP and nixf types.
void attrpathInfo(const AttrPathInfoParams &Params, lspserver::Callback< AttrPathInfoResponse > Reply)
const Identifier & id() const
Definition Simple.h:200
const std::string & name() const
Definition Basic.h:120
Whether current platform treats paths case insensitively.
Definition Connection.h:11
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
nixf::PositionRange toNixfRange(const lspserver::Range &P)
Definition Convert.cpp:36
bool fromJSON(const llvm::json::Value &Params, Configuration::Diagnostic &R, llvm::json::Path P)
llvm::cl::OptionCategory NixdCategory
lspserver::Position toLSPPosition(llvm::StringRef Code, const nixf::LexerCursor &P)
Definition Convert.cpp:27
bool havePackageScope(const nixf::Node &N, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM)
Determine whether or not some node has enclosed "with pkgs; [ ]".
Definition AST.cpp:108
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
Definition Convert.cpp:40
Position position
The position of this hint.
A parameter literal used in inlay hint requests.
PackageDescription PackageDesc
Package description of the attribute path, if available.
Definition AttrSet.h:86
std::optional< std::string > Version
Definition AttrSet.h:52