13#include <boost/asio/post.hpp>
15#include <llvm/Support/Error.h>
21using namespace llvm::json;
27class OptionsHoverProvider {
28 AttrSetClient &Client;
31 OptionsHoverProvider(AttrSetClient &Client) : Client(Client) {}
32 std::optional<OptionDescription>
33 resolveHover(
const std::vector<std::string> &Scope) {
34 std::binary_semaphore Ready(0);
35 std::optional<OptionDescription> Desc;
36 auto OnReply = [&Ready, &Desc](llvm::Expected<OptionInfoResponse> Resp) {
40 elog(
"options hover: {0}", Resp.takeError());
44 Client.optionInfo(Scope, std::move(OnReply));
52class NixpkgsHoverProvider {
53 AttrSetClient &NixpkgsClient;
59 static std::string mkMarkdown(
const PackageDescription &
Package) {
60 std::ostringstream OS;
64 OS <<
"`" << *
Package.Name <<
"`";
70 OS <<
"[homepage](" << *
Package.Homepage <<
")";
75 OS <<
"## Description"
91 static std::string mkMarkdown(
const AttrPathInfoResponse &
Info) {
92 std::ostringstream OS;
94 OS << mkMarkdown(
Info.PackageDesc);
98 const auto &VD = *
Info.ValueDesc;
99 if (!OS.str().empty())
101 if (!VD.Doc.empty()) {
102 OS << VD.Doc <<
"\n\n";
105 OS <<
"**Arity:** " << VD.Arity <<
"\n";
107 if (!VD.Args.empty()) {
109 for (
size_t Idx = 0; Idx < VD.Args.size(); ++Idx) {
110 OS <<
"`" << VD.Args[Idx] <<
"`";
111 if (Idx + 1 < VD.Args.size())
122 NixpkgsHoverProvider(AttrSetClient &NixpkgsClient)
123 : NixpkgsClient(NixpkgsClient) {}
125 std::optional<std::string> resolveSelector(
const nixd::Selector &Sel) {
126 std::binary_semaphore Ready(0);
127 std::optional<AttrPathInfoResponse>
Info;
128 auto OnReply = [&Ready, &
Info](llvm::Expected<AttrPathInfoResponse> Resp) {
132 elog(
"nixpkgs provider: {0}", Resp.takeError());
135 NixpkgsClient.attrpathInfo(Sel, std::move(OnReply));
141 return mkMarkdown(*
Info);
146std::optional<Hover> hoverNixpkgsSelector(
const Selector &Sel,
151 llvm::StringRef Src) {
154 NixpkgsHoverProvider NHP(NixpkgsClient);
155 if (std::optional<std::string> Doc = NHP.resolveSelector(Sel)) {
160 .value = std::move(*Doc),
165 }
catch (std::exception &E) {
166 elog(
"hover/idiom: {0}", E.what());
172std::optional<Hover> hoverVar(
const ExprVar &Var,
176 llvm::StringRef Src) {
179 return hoverNixpkgsSelector(Sel, Var, VLA, PM, NixpkgsClient, Src);
180 }
catch (std::exception &E) {
181 elog(
"hover/idiom/selector: {0}", E.what());
187std::optional<Hover> hoverSelect(
const ExprSelect &Sel,
191 llvm::StringRef Src) {
194 return hoverNixpkgsSelector(S, Sel, VLA, PM, NixpkgsClient, Src);
195 }
catch (std::exception &E) {
196 elog(
"hover/idiom/selector: {0}", E.what());
204 Callback<std::optional<Hover>> Reply) {
205 using CheckTy = std::optional<Hover>;
206 auto Action = [Reply = std::move(Reply),
208 RawPos = Params.
position,
this]()
mutable {
209 return Reply([&]() -> llvm::Expected<CheckTy> {
212 const auto Pos = nixf::Position{RawPos.line, RawPos.character};
213 const auto &N = *
CheckDefault(AST->descend({Pos, Pos}));
215 const auto Name = std::string(N.
name());
216 const auto &VLA = *TU->variableLookup();
217 const auto &PM = *TU->parentMap();
222 if (
auto *Client = nixpkgsClient(); Client) {
223 switch (UpExpr.kind()) {
224 case Node::NK_ExprVar: {
225 const auto &Var =
static_cast<const ExprVar &
>(UpExpr);
226 if (
auto H = hoverVar(Var, VLA, PM, *Client, TU->src()))
230 case Node::NK_ExprSelect: {
231 const auto &Sel =
static_cast<const ExprSelect &
>(UpExpr);
232 if (
auto H = hoverSelect(Sel, VLA, PM, *Client, TU->src()))
236 case Node::NK_ExprAttrs: {
238 auto Scope = std::vector<std::string>();
241 std::lock_guard
_(OptionsLock);
242 for (
const auto &[_, Client] : Options) {
243 if (AttrSetClient *C = Client->client()) {
244 OptionsHoverProvider OHP(*C);
245 std::optional<OptionDescription> Desc = OHP.resolveHover(Scope);
249 std::string TypeName = Desc->Type->Name.value_or(
"");
250 std::string TypeDesc = Desc->Type->Description.value_or(
"");
251 Docs += llvm::formatv(
"{0} ({1})", TypeName, TypeDesc);
253 Docs +=
"? (missing type)";
255 if (Desc->Description) {
256 Docs +=
"\n\n" + Desc->Description.value_or(
"");
261 .kind = MarkupKind::Markdown,
262 .value = std::move(Docs),
280 boost::asio::post(Pool, std::move(Action));
This file declares some common analysis (tree walk) on the AST.
Types used in nixpkgs provider.
#define CheckDefault(x)
Variant of CheckReturn, but returns default constructed CheckTy.
Convert between LSP and nixf types.
static const char * name(NodeKind Kind)
LexerCursorRange range() const
const Node * upExpr(const Node &N) const
Search up until the node becomes a concrete expression. a ^<--— ID -> ExprVar.
Whether current platform treats paths case insensitively.
llvm::unique_function< void(llvm::Expected< T >)> Callback
@ Info
An information message.
void elog(const char *Fmt, Ts &&...Vals)
Selector mkVarSelector(const nixf::ExprVar &Var, const nixf::VariableLookupAnalysis &VLA, const nixf::ParentMapAnalysis &PM)
Construct a nixd::Selector from Var.
Selector mkSelector(const nixf::AttrPath &AP, Selector BaseSelector)
Construct a nixd::Selector from AP.
std::vector< std::string > Selector
A list of strings that "select"s into a attribute set.
FindAttrPathResult findAttrPathForOptions(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, std::vector< std::string > &Path)
Heuristically find attrpath suitable for "attrpath" completion. Strips "config." from the start to su...
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
URIForFile uri
The text document's URI.
Position position
The position inside the text document.
TextDocumentIdentifier textDocument
The text document.
llvm::StringRef file() const
Retrieves absolute path to the file.