16#include <boost/asio/post.hpp>
18#include <llvm/Support/Error.h>
19#include <llvm/Support/JSON.h>
51 if (Parent && Parent->
kind() == Node::NK_AttrName)
52 return VLA.
toDef(*Parent);
62 if (
const Node *Up = PMA.
upTo(N, Node::NK_Inherit)) {
63 const Node *UpAn = PMA.
upTo(N, Node::NK_AttrName);
66 const auto &Inh =
static_cast<const Inherit &
>(*Up);
67 const auto &AN =
static_cast<const AttrName &
>(*UpAn);
81 Up = PMA.
upTo(Inh, Node::NK_ExprAttrs);
87 assert(Var->
kind() == Node::NK_ExprVar);
88 return static_cast<const ExprVar *
>(Var);
95 if (
const ExprVar *InVar = findInheritVar(N, PMA, VLA))
98 return static_cast<const ExprVar *
>(PMA.
upTo(N, Node::NK_ExprVar));
105 if (Result.
Kind == ResultKind::Undefined)
108 if (Result.
Kind == ResultKind::NoSuchVar)
123 .uri = std::move(
URI),
128struct NoLocationsFoundInNixpkgsException : std::exception {
129 [[nodiscard]]
const char *what()
const noexcept override {
130 return "no locations found in nixpkgs";
134class WorkerReportedException : std::exception {
138 WorkerReportedException(llvm::Error E) : E(std::move(E)) {};
140 llvm::Error takeError() {
return std::move(E); }
141 [[nodiscard]]
const char *what()
const noexcept override {
142 return "worker reported some error";
151class NixpkgsDefinitionProvider {
157 auto Pos =
Position.find_first_of(
':');
158 if (Pos == std::string_view::npos) {
161 .range = {{0, 0}, {0, 0}},
164 int PosL = std::stoi(std::string(
Position.substr(Pos + 1)));
175 : NixpkgsClient(NixpkgsClient) {}
178 std::binary_semaphore Ready(0);
179 Expected<AttrPathInfoResponse> Desc =
error(
"not replied");
180 auto OnReply = [&Ready, &Desc](llvm::Expected<AttrPathInfoResponse> Resp) {
184 Desc = Resp.takeError();
191 throw WorkerReportedException(Desc.takeError());
194 if (
const std::optional<std::string> &
Position = Desc->PackageDesc.
Position)
198 if (
const auto &Loc = Desc->Meta.Location)
201 throw NoLocationsFoundInNixpkgsException();
206class OptionsDefinitionProvider {
210 OptionsDefinitionProvider(
AttrSetClient &Client) : Client(Client) {}
211 void resolveLocations(
const std::vector<std::string> &Params,
213 std::binary_semaphore Ready(0);
214 Expected<OptionInfoResponse>
Info =
error(
"not replied");
216 auto OnReply = [&Ready, &
Info](llvm::Expected<OptionInfoResponse> Resp) {
217 Info = std::move(Resp);
222 Client.
optionInfo(Params, std::move(OnReply));
226 elog(
"getting locations: {0}",
Info.takeError());
230 for (
const auto &Decl :
Info->Declarations)
231 Locs.emplace_back(Decl);
239 std::mutex &OptionsLock,
242 std::vector<std::string> Scope;
245 if (R == PathResult::OK) {
246 std::lock_guard _(OptionsLock);
248 for (
const auto &[_, Client] : Options) {
250 OptionsDefinitionProvider ODP(*C);
251 ODP.resolveLocations(Scope, Locs);
263 NixpkgsDefinitionProvider NDP(NixpkgsClient);
264 return NDP.resolveSelector(Sel);
265 }
catch (NoLocationsFoundInNixpkgsException &E) {
266 elog(
"definition/idiom: {0}", E.what());
267 }
catch (WorkerReportedException &E) {
268 elog(
"definition/idiom/worker: {0}", E.takeError());
280 return defineNixpkgsSelector(
mkSelector(Sel, VLA, PM), NixpkgsClient);
282 elog(
"defintion/idiom/selector: {0}", E.what());
289 const Definition &Def = findVarDefinition(Var, VLA);
290 return {convertToLocation(Src, Def,
URI)};
294std::vector<T> mergeVec(std::vector<T> A,
const std::vector<T> &B) {
295 A.insert(A.end(), B.begin(), B.end());
299llvm::Expected<Locations>
304 Locations StaticLocs = defineVarStatic(Var, VLA,
URI, Src);
309 Locations NixpkgsLocs = defineNixpkgsSelector(Sel, NixpkgsClient);
310 return mergeVec(std::move(StaticLocs), NixpkgsLocs);
311 }
catch (std::exception &E) {
312 elog(
"definition/idiom/selector: {0}", E.what());
315 }
catch (std::exception &E) {
316 elog(
"definition/static: {0}", E.what());
319 return error(
"unreachable code! Please submit an issue");
323template <
class T> llvm::json::Value squash(std::vector<T> List) {
324 std::size_t Size = List.size();
329 return std::move(List.back());
333 return std::move(List);
337llvm::Expected<llvm::json::Value> squash(llvm::Expected<std::vector<T>> List) {
339 return List.takeError();
340 return squash(std::move(*List));
348 const ExprVar *Var = findVar(N, PMA, VLA);
349 if (!Var) [[unlikely]] {
350 if (
const Definition *Def = findSelfDefinition(N, PMA, VLA))
354 assert(Var->
kind() == Node::NK_ExprVar);
355 return findVarDefinition(*Var, VLA);
363 const auto File =
URI.file().str();
364 return Reply(squash([&]() -> llvm::Expected<Locations> {
367 const auto &VLA = *TU->variableLookup();
368 const auto &PM = *TU->parentMap();
369 const auto &N = *
CheckDefault(AST->descend({Pos, Pos}));
373 if (
const ExprVar *Var = findInheritVar(N, PM, VLA))
374 return defineVar(*Var, VLA, PM, *nixpkgsClient(),
URI, TU->src());
376 switch (UpExpr.kind()) {
377 case Node::NK_ExprVar: {
378 const auto &Var =
static_cast<const ExprVar &
>(UpExpr);
379 return defineVar(Var, VLA, PM, *nixpkgsClient(),
URI, TU->src());
381 case Node::NK_ExprSelect: {
382 const auto &Sel =
static_cast<const ExprSelect &
>(UpExpr);
383 return defineSelect(Sel, VLA, PM, *nixpkgsClient());
385 case Node::NK_ExprAttrs:
386 return defineAttrPath(N, PM, OptionsLock, Options);
390 return error(
"unknown node type for definition");
393 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.
std::vector< Location > Locations
Lookup variable names, from it's parent scope.
void optionInfo(const AttrPathInfoParams &Params, lspserver::Callback< OptionInfoResponse > Reply)
void attrpathInfo(const AttrPathInfoParams &Params, lspserver::Callback< AttrPathInfoResponse > Reply)
std::map< std::string, std::unique_ptr< AttrSetClientProc > > OptionMapTy
const Node * syntax() const
const SemaAttrs & sema() const
LexerCursorRange range() const
const Node * upTo(const Node &N, Node::NodeKind Kind) const
Search up until some kind of node is found.
const Node * query(const Node &N) const
Attribute set after deduplication.
const std::map< std::string, Attribute > & staticAttrs() const
Static attributes, do not require evaluation to get the key.
const Definition * toDef(const Node &N) const
Get definition record for some name.
LookupResult query(const ExprVar &Var) const
Query the which name/with binds to specific varaible.
Whether current platform treats paths case insensitively.
llvm::unique_function< void(llvm::Expected< T >)> Callback
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&...Vals)
@ 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.
FindAttrPathResult findAttrPath(const nixf::Node &N, const nixf::ParentMapAnalysis &PM, std::vector< std::string > &Path)
Heuristically find attrpath suitable for "attrpath" completion.
std::vector< std::string > Selector
A list of strings that "select"s into a attribute set.
nixf::Position toNixfPosition(const lspserver::Position &P)
const nixf::Definition & findDefinition(const nixf::Node &N, const nixf::ParentMapAnalysis &PMA, const nixf::VariableLookupAnalysis &VLA)
Heuristically find definition on some node.
lspserver::Range toLSPRange(llvm::StringRef Code, const nixf::LexerCursorRange &R)
std::vector< OptionField > OptionCompleteResponse
URIForFile uri
The text document's URI.
Position position
The position inside the text document.
TextDocumentIdentifier textDocument
The text document.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
Exceptions scoped in nixd::mkIdiomSelector.
std::shared_ptr< const Definition > Def