nixd
Loading...
Searching...
No Matches
AttrSetProvider.cpp
Go to the documentation of this file.
3
5
6#include <nix/attr-path.hh>
7#include <nix/nixexpr.hh>
8#include <nix/store-api.hh>
9#include <nixt/Value.h>
10
11using namespace nixd;
12using namespace lspserver;
13
14namespace {
15
16constexpr int MaxItems = 30;
17
18void fillString(nix::EvalState &State, nix::Value &V,
19 const std::vector<std::string_view> &AttrPath,
20 std::optional<std::string> &Field) {
21 try {
22 nix::Value &Select = nixt::selectStringViews(State, V, AttrPath);
23 State.forceValue(Select, nix::noPos);
24 if (Select.type() == nix::ValueType::nString)
25 Field = Select.string.c_str;
26 } catch (std::exception &E) {
27 Field = std::nullopt;
28 }
29}
30
31/// Describe the value as if \p Package is actually a nixpkgs package.
32PackageDescription describePackage(nix::EvalState &State, nix::Value &Package) {
34 fillString(State, Package, {"name"}, R.Name);
35 fillString(State, Package, {"pname"}, R.PName);
36 fillString(State, Package, {"version"}, R.Version);
37 fillString(State, Package, {"meta", "description"}, R.Description);
38 fillString(State, Package, {"meta", "longDescription"}, R.LongDescription);
39 fillString(State, Package, {"meta", "position"}, R.Position);
40 fillString(State, Package, {"meta", "homepage"}, R.Homepage);
41 return R;
42}
43
44std::optional<Location> locationOf(nix::PosTable &PTable, nix::Value &V) {
45 nix::PosIdx P = V.determinePos(nix::noPos);
46 if (!P)
47 return std::nullopt;
48
49 nix::Pos NixPos = PTable[P];
50 const auto *SP = std::get_if<nix::SourcePath>(&NixPos.origin);
51
52 if (!SP)
53 return std::nullopt;
54
55 Position LPos = {
56 .line = static_cast<int>(NixPos.line - 1),
57 .character = static_cast<int>(NixPos.column - 1),
58 };
59
60 return Location{
61 .uri = URIForFile::canonicalize(SP->path.abs(), SP->path.abs()),
62 .range = {LPos, LPos},
63 };
64}
65
66ValueMeta metadataOf(nix::EvalState &State, nix::Value &V) {
67 return {
68 .Type = V.type(true),
69 .Location = locationOf(State.positions, V),
70 };
71}
72
73void fillUnsafeGetAttrPosLocation(nix::EvalState &State, nix::Value &V,
75 State.forceValue(V, nix::noPos);
76 nix::Value &File = nixt::selectAttr(State, V, State.symbols.create("file"));
77 nix::Value &Line = nixt::selectAttr(State, V, State.symbols.create("line"));
78 nix::Value &Column =
79 nixt::selectAttr(State, V, State.symbols.create("column"));
80
81 if (File.type() == nix::ValueType::nString)
82 Loc.uri = URIForFile::canonicalize(File.c_str(), File.c_str());
83
84 if (Line.type() == nix::ValueType::nInt &&
85 Column.type() == nix::ValueType::nInt) {
86
87 // Nix position starts from "1" however lsp starts from zero.
88 lspserver::Position Pos = {static_cast<int>(Line.integer) - 1,
89 static_cast<int>(Column.integer) - 1};
90 Loc.range = {Pos, Pos};
91 }
92}
93
94void fillOptionDeclarationPositions(nix::EvalState &State, nix::Value &V,
96 State.forceValue(V, nix::noPos);
97 if (V.type() != nix::ValueType::nList)
98 return;
99 for (nix::Value *Item : V.listItems()) {
100 // Each item should have "column", "line", "file" fields.
103 R.Declarations.emplace_back(std::move(Loc));
104 }
105}
106
107void fillOptionDeclarations(nix::EvalState &State, nix::Value &V,
109 // Eval declarations
110 try {
112 State, V, State.symbols.create("declarationPositions"));
113
114 State.forceValue(DeclarationPositions, nix::noPos);
115 // A list of positions, in unsafeGetAttrPos format.
117 } catch (nix::AttrPathNotFound &E) {
118 // FIXME: fallback to "declarations"
119 return;
120 }
121}
122
123void fillOptionType(nix::EvalState &State, nix::Value &VType, OptionType &R) {
124 fillString(State, VType, {"description"}, R.Description);
125 fillString(State, VType, {"name"}, R.Name);
126}
127
128void fillOptionDescription(nix::EvalState &State, nix::Value &V,
130 fillString(State, V, {"description"}, R.Description);
131 fillOptionDeclarations(State, V, R);
132 // FIXME: add definitions location.
133 if (V.type() == nix::ValueType::nAttrs) [[likely]] {
134 assert(V.attrs);
135 if (auto *It = V.attrs->find(State.symbols.create("type"));
136 It != V.attrs->end()) [[likely]] {
138 fillOptionType(State, *It->value, Type);
139 R.Type = std::move(Type);
140 }
141
142 if (auto *It = V.attrs->find(State.symbols.create("example"));
143 It != V.attrs->end()) {
144 State.forceValue(*It->value, It->pos);
145
146 // In nixpkgs some examples are nested in "literalExpression"
147 if (nixt::checkField(State, *It->value, "_type", "literalExpression")) {
148 R.Example = nixt::getFieldString(State, *It->value, "text");
149 } else {
150 std::ostringstream OS;
151 It->value->print(State.symbols, OS);
152 R.Example = OS.str();
153 }
154 }
155 }
156}
157
158} // namespace
159
174
176 const std::string &Name,
177 lspserver::Callback<std::optional<std::string>> Reply) {
178 try {
179 nix::Expr *AST = state().parseExprFromString(
180 Name, state().rootPath(nix::CanonPath::fromCwd()));
181 state().eval(AST, Nixpkgs);
182 Reply(std::nullopt);
183 return;
184 } catch (const nix::BaseError &Err) {
185 Reply(error(Err.info().msg.str()));
186 return;
187 } catch (const std::exception &Err) {
188 Reply(error(Err.what()));
189 return;
190 }
191}
192
194 const AttrPathInfoParams &AttrPath,
197 Reply([&]() -> llvm::Expected<RespT> {
198 try {
199 if (AttrPath.empty())
200 return error("attrpath is empty!");
201
202 nix::Value &V = nixt::selectStrings(state(), Nixpkgs, AttrPath);
203 state().forceValue(V, nix::noPos);
204 return RespT{
205 .Meta = metadataOf(state(), V),
206 .PackageDesc = describePackage(state(), V),
207 };
208 } catch (const nix::BaseError &Err) {
209 return error(Err.info().msg.str());
210 } catch (const std::exception &Err) {
211 return error(Err.what());
212 }
213 }());
214}
215
219 try {
220 nix::Value &Scope = nixt::selectStrings(state(), Nixpkgs, Params.Scope);
221
222 state().forceValue(Scope, nix::noPos);
223
224 if (Scope.type() != nix::ValueType::nAttrs) {
225 Reply(error("scope is not an attrset"));
226 return;
227 }
228
229 std::vector<std::string> Names;
230 int Num = 0;
231
232 // FIXME: we may want to use "Trie" to speedup the string searching.
233 // However as my (roughtly) profiling the critical in this loop is
234 // evaluating package details.
235 // "Trie"s may not beneficial becausae it cannot speedup eval.
236 for (const auto *AttrPtr :
237 Scope.attrs->lexicographicOrder(state().symbols)) {
238 const nix::Attr &Attr = *AttrPtr;
239 const std::string Name = state().symbols[Attr.name];
240 if (Name.starts_with(Params.Prefix)) {
241 ++Num;
242 Names.emplace_back(Name);
243 // We set this a very limited number as to speedup
244 if (Num > MaxItems)
245 break;
246 }
247 }
248 Reply(std::move(Names));
249 return;
250 } catch (const nix::BaseError &Err) {
251 Reply(error(Err.info().msg.str()));
252 return;
253 } catch (const std::exception &Err) {
254 Reply(error(Err.what()));
255 return;
256 }
257}
258
260 const AttrPathInfoParams &AttrPath,
262 try {
263 if (AttrPath.empty()) {
264 Reply(error("attrpath is empty!"));
265 return;
266 }
267
268 nix::Value Option = nixt::selectOptions(
269 state(), Nixpkgs, nixt::toSymbols(state().symbols, AttrPath));
270
272
273 fillOptionDescription(state(), Option, R);
274
275 Reply(std::move(R));
276 return;
277 } catch (const nix::BaseError &Err) {
278 Reply(error(Err.info().msg.str()));
279 return;
280 } catch (const std::exception &Err) {
281 Reply(error(Err.what()));
282 return;
283 }
284}
285
289 try {
290 nix::Value Scope = nixt::selectOptions(
291 state(), Nixpkgs, nixt::toSymbols(state().symbols, Params.Scope));
292
293 state().forceValue(Scope, nix::noPos);
294
295 if (Scope.type() != nix::ValueType::nAttrs) {
296 Reply(error("scope is not an attrset"));
297 return;
298 }
299
300 if (nixt::isOption(state(), Scope)) {
301 Reply(error("scope is already an option"));
302 return;
303 }
304
305 std::vector<OptionField> Response;
306
307 // FIXME: we may want to use "Trie" to speedup the string searching.
308 // However as my (roughtly) profiling the critical in this loop is
309 // evaluating package details.
310 // "Trie"s may not beneficial becausae it cannot speedup eval.
311 for (const auto *AttrPtr :
312 Scope.attrs->lexicographicOrder(state().symbols)) {
313 const nix::Attr &Attr = *AttrPtr;
314 std::string Name = state().symbols[Attr.name];
315 if (Name.starts_with(Params.Prefix)) {
316 // Add a new "OptionField", see it's type.
317 assert(Attr.value);
319 NewField.Name = Name;
320 if (nixt::isOption(state(), *Attr.value)) {
322 fillOptionDescription(state(), *Attr.value, Desc);
323 NewField.Description = std::move(Desc);
324 }
325 Response.emplace_back(std::move(NewField));
326 // We set this a very limited number as to speedup
327 if (Response.size() >= MaxItems)
328 break;
329 }
330 }
331 Reply(std::move(Response));
332 return;
333 } catch (const nix::BaseError &Err) {
334 Reply(error(Err.info().msg.str()));
335 return;
336 } catch (const std::exception &Err) {
337 Reply(error(Err.what()));
338 return;
339 }
340}
Dedicated worker for evaluating attrset.
Types used in nixpkgs provider.
HandlerRegistry Registry
Definition LSPServer.h:55
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.
Definition Connection.h:11
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&...Vals)
Definition Logger.h:70
Definition Kinds.h:6
constexpr std::string_view EvalExpr
Definition AttrSet.h:29
constexpr std::string_view OptionInfo
Definition AttrSet.h:32
constexpr std::string_view AttrPathInfo
Definition AttrSet.h:30
constexpr std::string_view OptionComplete
Definition AttrSet.h:33
constexpr std::string_view AttrPathComplete
Definition AttrSet.h:31
bool fromJSON(const llvm::json::Value &Params, Configuration::Diagnostic &R, llvm::json::Path P)
Selector AttrPathInfoParams
Definition AttrSet.h:47
std::optional< std::string_view > getFieldString(nix::EvalState &State, nix::Value &V, std::string_view Field)
Definition Value.cpp:23
bool isOption(nix::EvalState &State, nix::Value &V)
Definition Value.cpp:46
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.
Definition Value.cpp:36
nix::Value & selectStrings(nix::EvalState &State, nix::Value &V, const std::vector< std::string > &AttrPath)
Given an attrpath in nix::Value V, select it.
Definition Value.h:61
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".
Definition Value.cpp:158
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.
Definition Value.cpp:63
nix::Value & selectAttr(nix::EvalState &State, nix::Value &V, nix::Symbol Attr)
Select attribute Attr.
Definition Value.cpp:84
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.
Definition Value.h:68
void addMethod(llvm::StringRef Method, ThisT *This, void(ThisT::*Handler)(const Param &, Callback< Result >))
Definition LSPBinder.h:57
URIForFile uri
The text document's URI.
int line
Line position in a document (zero-based).
static URIForFile canonicalize(llvm::StringRef AbsPath, llvm::StringRef TUPath)
std::optional< std::string > Description
Definition AttrSet.h:115
std::optional< std::string > Example
Definition AttrSet.h:118
std::optional< OptionType > Type
Definition AttrSet.h:119
std::vector< lspserver::Location > Declarations
Definition AttrSet.h:116
std::string Name
Definition AttrSet.h:127
std::optional< std::string > Description
Definition AttrSet.h:106
std::optional< std::string > Name
Definition AttrSet.h:107
std::optional< std::string > Name
Definition AttrSet.h:50
std::optional< std::string > Version
Definition AttrSet.h:52
std::optional< std::string > PName
Definition AttrSet.h:51
std::optional< std::string > Description
Definition AttrSet.h:53
std::optional< std::string > LongDescription
Definition AttrSet.h:54
std::optional< std::string > Position
Definition AttrSet.h:55
std::optional< std::string > Homepage
Definition AttrSet.h:56
General metadata of all nix::Values.
Definition AttrSet.h:64
int Type
Type of this value.
Definition AttrSet.h:66