6#include <nix/cmd/common-eval-args.hh>
7#include <nix/expr/attr-path.hh>
8#include <nix/expr/nixexpr.hh>
9#include <nix/store/store-open.hh>
17constexpr int MaxItems = 30;
19void fillString(nix::EvalState &State, nix::Value &V,
20 const std::vector<std::string_view> &AttrPath,
21 std::optional<std::string> &
Field) {
24 State.forceValue(Select, nix::noPos);
25 if (Select.type() == nix::ValueType::nString)
26 Field = Select.string_view();
27 }
catch (std::exception &E) {
45std::optional<Location> locationOf(nix::PosTable &PTable, nix::Value &V) {
46 nix::PosIdx P = V.determinePos(nix::noPos);
50 nix::Pos NixPos = PTable[P];
51 const auto *SP = std::get_if<nix::SourcePath>(&NixPos.origin);
57 .line =
static_cast<int64_t
>(NixPos.line - 1),
58 .character =
static_cast<int64_t
>(NixPos.column - 1),
63 .range = {LPos, LPos},
67ValueMeta metadataOf(nix::EvalState &State, nix::Value &V) {
70 .Location = locationOf(State.positions, V),
74void fillUnsafeGetAttrPosLocation(nix::EvalState &State, nix::Value &V,
76 State.forceValue(V, nix::noPos);
82 State.forceValue(
File, nix::noPos);
83 State.forceValue(Line, nix::noPos);
84 State.forceValue(Column, nix::noPos);
86 if (
File.type() == nix::ValueType::nString)
89 if (Line.type() == nix::ValueType::nInt &&
90 Column.type() == nix::ValueType::nInt) {
94 static_cast<int64_t
>(Column.integer()) - 1};
95 Loc.
range = {Pos, Pos};
99void fillOptionDeclarationPositions(nix::EvalState &State, nix::Value &V,
101 State.forceValue(V, nix::noPos);
102 if (V.type() != nix::ValueType::nList)
104 for (nix::Value *Item : V.listView()) {
107 fillUnsafeGetAttrPosLocation(State, *Item, Loc);
112void fillOptionDeclarations(nix::EvalState &State, nix::Value &V,
117 State, V, State.symbols.create(
"declarationPositions"));
119 State.forceValue(DeclarationPositions, nix::noPos);
121 fillOptionDeclarationPositions(State, DeclarationPositions, R);
122 }
catch (nix::AttrPathNotFound &E) {
128void fillOptionType(nix::EvalState &State, nix::Value &VType,
OptionType &R) {
129 fillString(State, VType, {
"description"}, R.
Description);
130 fillString(State, VType, {
"name"}, R.
Name);
133void fillOptionDescription(nix::EvalState &State, nix::Value &V,
135 fillString(State, V, {
"description"}, R.
Description);
136 fillOptionDeclarations(State, V, R);
138 if (V.type() == nix::ValueType::nAttrs) [[likely]] {
140 if (
auto *It = V.attrs()->get(State.symbols.create(
"type"))) [[likely]] {
142 fillOptionType(State, *It->value,
Type);
146 if (
auto *It = V.attrs()->get(State.symbols.create(
"example"))) {
147 State.forceValue(*It->value, It->pos);
153 std::ostringstream OS;
154 It->value->print(State, OS);
161std::vector<std::string> completeNames(nix::Value &Scope,
162 const nix::EvalState &State,
163 std::string_view Prefix) {
165 std::vector<std::string> Names;
171 for (
const auto *AttrPtr : Scope.attrs()->lexicographicOrder(State.symbols)) {
172 const nix::Attr &Attr = *AttrPtr;
173 const std::string_view Name = State.symbols[Attr.name];
174 if (Name.starts_with(Prefix)) {
176 Names.emplace_back(Name);
185std::optional<ValueDescription> describeValue(nix::EvalState &State,
188 const auto *PrimOp = V.primOp();
191 .Doc = PrimOp->doc.value_or(
""),
192 .Arity =
static_cast<int>(PrimOp->arity),
193 .Args = PrimOp->args,
195 }
else if (V.isLambda()) {
196 auto *Lambda = V.lambda().fun;
198 const auto DocComment = Lambda->docComment;
208 .Doc = DocComment.getInnerText(State.positions),
220 std::unique_ptr<OutboundPort> Out)
221 :
LSPServer(std::move(In), std::move(Out)),
222 State(new nix::EvalState({}, nix::openStore(), nix::fetchSettings,
223 nix::evalSettings)) {
236 const std::string &Name,
239 nix::Expr *AST = state().parseExprFromString(Name, state().rootPath(
"."));
240 state().eval(AST, Nixpkgs);
243 }
catch (
const nix::BaseError &Err) {
244 Reply(
error(Err.info().msg.str()));
246 }
catch (
const std::exception &Err) {
247 Reply(
error(Err.what()));
256 Reply([&]() -> llvm::Expected<RespT> {
258 if (AttrPath.empty())
259 return error(
"attrpath is empty!");
262 state().forceValue(V, nix::noPos);
264 .Meta = metadataOf(state(), V),
265 .PackageDesc = describePackage(state(), V),
266 .ValueDesc = describeValue(state(), V),
268 }
catch (
const nix::BaseError &Err) {
269 return error(Err.info().msg.str());
270 }
catch (
const std::exception &Err) {
271 return error(Err.what());
282 state().forceValue(Scope, nix::noPos);
284 if (Scope.type() != nix::ValueType::nAttrs) {
285 Reply(
error(
"scope is not an attrset"));
289 return Reply(completeNames(Scope, state(), Params.
Prefix));
290 }
catch (
const nix::BaseError &Err) {
291 return Reply(
error(Err.info().msg.str()));
292 }
catch (
const std::exception &Err) {
293 return Reply(
error(Err.what()));
301 if (AttrPath.empty()) {
302 Reply(
error(
"attrpath is empty!"));
311 fillOptionDescription(state(), Option, R);
315 }
catch (
const nix::BaseError &Err) {
316 Reply(
error(Err.info().msg.str()));
318 }
catch (
const std::exception &Err) {
319 Reply(
error(Err.what()));
331 state().forceValue(Scope, nix::noPos);
333 if (Scope.type() != nix::ValueType::nAttrs) {
334 Reply(
error(
"scope is not an attrset"));
339 Reply(
error(
"scope is already an option"));
343 std::vector<OptionField> Response;
349 for (
const auto *AttrPtr :
350 Scope.attrs()->lexicographicOrder(state().symbols)) {
351 const nix::Attr &Attr = *AttrPtr;
352 std::string_view Name = state().symbols[Attr.name];
353 if (Name.starts_with(Params.
Prefix)) {
357 NewField.
Name = Name;
360 fillOptionDescription(state(), *Attr.value, Desc);
363 Response.emplace_back(std::move(NewField));
365 if (Response.size() >= MaxItems)
369 Reply(std::move(Response));
371 }
catch (
const nix::BaseError &Err) {
372 Reply(
error(Err.info().msg.str()));
374 }
catch (
const std::exception &Err) {
375 Reply(
error(Err.what()));
Dedicated worker for evaluating attrset.
Types used in nixpkgs provider.
LSPServer(std::unique_ptr< InboundPort > In, std::unique_ptr< OutboundPort > Out)
void onOptionInfo(const AttrPathInfoParams &AttrPath, lspserver::Callback< OptionInfoResponse > Reply)
Provide option information on given attrpath.
void onAttrPathInfo(const AttrPathInfoParams &AttrPath, lspserver::Callback< AttrPathInfoResponse > Reply)
Query attrpath information.
void onEvalExpr(const EvalExprParams &Name, lspserver::Callback< EvalExprResponse > Reply)
Eval an expression, use it for furthur requests.
AttrSetProvider(std::unique_ptr< lspserver::InboundPort > In, std::unique_ptr< lspserver::OutboundPort > Out)
void onAttrPathComplete(const AttrPathCompleteParams &Params, lspserver::Callback< AttrPathCompleteResponse > Reply)
Complete attrpath entries.
void onOptionComplete(const AttrPathCompleteParams &Params, lspserver::Callback< OptionCompleteResponse > Reply)
Complete attrpath entries. However dive into submodules while selecting.
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)
constexpr std::string_view EvalExpr
constexpr std::string_view OptionInfo
constexpr std::string_view AttrPathInfo
constexpr std::string_view OptionComplete
constexpr std::string_view AttrPathComplete
OptionDescription OptionInfoResponse
Selector AttrPathInfoParams
std::optional< std::string_view > getFieldString(nix::EvalState &State, nix::Value &V, std::string_view Field)
bool isOption(nix::EvalState &State, nix::Value &V)
bool checkField(nix::EvalState &State, nix::Value &V, std::string_view Field, std::string_view Pred)
Check if value V is an attrset, has the field, and equals to Pred.
nix::Value & selectStrings(nix::EvalState &State, nix::Value &V, const std::vector< std::string > &AttrPath)
Given an attrpath in nix::Value V, select it.
nix::Value selectOptions(nix::EvalState &State, nix::Value &V, std::vector< nix::Symbol >::const_iterator Begin, std::vector< nix::Symbol >::const_iterator End)
Select the option declaration list, V, dive into "submodules".
std::vector< nix::Symbol > toSymbols(nix::SymbolTable &STable, const std::vector< std::string > &Names)
Transform a vector of string into a vector of nix symbols.
nix::Value & selectAttr(nix::EvalState &State, nix::Value &V, nix::Symbol Attr)
Select attribute Attr.
nix::Value & selectStringViews(nix::EvalState &State, nix::Value &V, const std::vector< std::string_view > &AttrPath)
Given an attrpath in nix::Value V, select it.
URIForFile uri
The text document's URI.
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
std::string Prefix
Search for packages prefixed with this "prefix".
std::optional< std::string > Description
std::optional< std::string > Example
std::optional< OptionType > Type
std::vector< lspserver::Location > Declarations
std::optional< OptionDescription > Description
std::optional< std::string > Description
std::optional< std::string > Name
std::optional< std::string > Name
std::optional< std::string > Version
std::optional< std::string > PName
std::optional< std::string > Description
std::optional< std::string > LongDescription
std::optional< std::string > Position
std::optional< std::string > Homepage
Using nix's ":doc" method to retrieve value's additional information.