2017-07-17 17:02:56 +00:00
|
|
|
#include "command.hh"
|
|
|
|
#include "globals.hh"
|
|
|
|
#include "eval.hh"
|
|
|
|
#include "eval-inline.hh"
|
|
|
|
#include "names.hh"
|
|
|
|
#include "get-drvs.hh"
|
2017-07-18 15:30:09 +00:00
|
|
|
#include "common-args.hh"
|
|
|
|
#include "json.hh"
|
2018-05-12 18:00:59 +00:00
|
|
|
#include "shared.hh"
|
2020-04-20 13:27:09 +00:00
|
|
|
#include "eval-cache.hh"
|
|
|
|
#include "attr-path.hh"
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2022-01-19 18:59:02 +00:00
|
|
|
#include <iterator>
|
2017-07-17 17:02:56 +00:00
|
|
|
#include <regex>
|
2017-07-26 15:21:46 +00:00
|
|
|
#include <fstream>
|
2017-07-17 17:02:56 +00:00
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
2018-05-12 18:00:59 +00:00
|
|
|
std::string wrap(std::string prefix, std::string s)
|
|
|
|
{
|
|
|
|
return prefix + s + ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
2022-01-19 16:46:29 +00:00
|
|
|
#define HILITE_COLOR ANSI_GREEN
|
|
|
|
|
2018-05-12 18:00:59 +00:00
|
|
|
std::string hilite(const std::string & s, const std::smatch & m, std::string postfix)
|
2017-07-17 17:02:56 +00:00
|
|
|
{
|
|
|
|
return
|
|
|
|
m.empty()
|
|
|
|
? s
|
|
|
|
: std::string(m.prefix())
|
2022-01-19 16:46:29 +00:00
|
|
|
+ HILITE_COLOR + std::string(m.str()) + postfix
|
2017-07-17 17:02:56 +00:00
|
|
|
+ std::string(m.suffix());
|
|
|
|
}
|
|
|
|
|
2022-01-20 16:12:15 +00:00
|
|
|
template<class Iter>
|
|
|
|
std::string hilite_all(const std::string &s, Iter matches_first, Iter matches_last, std::string postfix) {
|
|
|
|
// Avoid copy on zero matches
|
|
|
|
if (matches_first == matches_last)
|
2022-01-19 16:46:29 +00:00
|
|
|
return s;
|
|
|
|
|
2022-01-20 16:12:15 +00:00
|
|
|
std::sort(matches_first, matches_last, [](const auto &a, const auto &b) {
|
2022-01-19 16:46:29 +00:00
|
|
|
return a.position() < b.position();
|
|
|
|
});
|
|
|
|
|
|
|
|
std::string out;
|
|
|
|
ssize_t last_end = 0;
|
2022-01-20 16:12:15 +00:00
|
|
|
|
|
|
|
for (Iter it = matches_first; it != matches_last; ++it) {
|
|
|
|
auto m = *it;
|
2022-01-19 16:46:29 +00:00
|
|
|
size_t start = m.position();
|
2022-01-19 18:59:02 +00:00
|
|
|
out.append(s.substr(last_end, m.position() - last_end));
|
2022-01-19 16:46:29 +00:00
|
|
|
// Merge continous matches
|
|
|
|
ssize_t end = start + m.length();
|
2022-01-20 16:12:15 +00:00
|
|
|
while(it + 1 != matches_last && (*(it + 1)).position() <= end) {
|
|
|
|
auto n = *++it;
|
2022-01-19 16:46:29 +00:00
|
|
|
ssize_t nend = start + (n.position() - start + n.length());
|
|
|
|
if(nend > end)
|
|
|
|
end = nend;
|
|
|
|
}
|
|
|
|
out.append(HILITE_COLOR);
|
|
|
|
out.append(s.substr(start, end - start));
|
|
|
|
out.append(postfix);
|
|
|
|
last_end = end;
|
|
|
|
}
|
|
|
|
out.append(s.substr(last_end));
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
struct CmdSearch : InstallableCommand, MixJSON
|
2017-07-17 17:02:56 +00:00
|
|
|
{
|
2018-04-18 19:08:35 +00:00
|
|
|
std::vector<std::string> res;
|
2017-07-17 17:02:56 +00:00
|
|
|
|
|
|
|
CmdSearch()
|
|
|
|
{
|
2018-04-18 19:08:35 +00:00
|
|
|
expectArgs("regex", &res);
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
2020-12-08 17:09:30 +00:00
|
|
|
return "search for packages";
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
|
2020-12-08 17:09:30 +00:00
|
|
|
std::string doc() override
|
2017-09-07 18:42:11 +00:00
|
|
|
{
|
2020-12-08 17:09:30 +00:00
|
|
|
return
|
|
|
|
#include "search.md"
|
|
|
|
;
|
2017-09-07 18:42:11 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
Strings getDefaultFlakeAttrPaths() override
|
|
|
|
{
|
2020-04-20 13:27:09 +00:00
|
|
|
return {
|
|
|
|
"packages." + settings.thisSystem.get() + ".",
|
|
|
|
"legacyPackages." + settings.thisSystem.get() + "."
|
|
|
|
};
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
2017-07-17 17:02:56 +00:00
|
|
|
void run(ref<Store> store) override
|
|
|
|
{
|
|
|
|
settings.readOnlyMode = true;
|
2021-09-22 15:15:07 +00:00
|
|
|
evalSettings.enableImportFromDerivation.setDefault(false);
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2018-02-25 22:38:13 +00:00
|
|
|
// Empty search string should match all packages
|
|
|
|
// Use "^" here instead of ".*" due to differences in resulting highlighting
|
|
|
|
// (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
|
2020-04-20 13:27:09 +00:00
|
|
|
if (res.empty())
|
2018-04-18 19:08:35 +00:00
|
|
|
res.push_back("^");
|
|
|
|
|
|
|
|
std::vector<std::regex> regexes;
|
|
|
|
regexes.reserve(res.size());
|
2018-02-25 22:38:13 +00:00
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
for (auto & re : res)
|
2018-04-18 19:08:35 +00:00
|
|
|
regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase));
|
2017-07-17 17:02:56 +00:00
|
|
|
|
|
|
|
auto state = getEvalState();
|
|
|
|
|
2017-11-14 13:27:01 +00:00
|
|
|
auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;
|
2017-07-18 15:30:09 +00:00
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
uint64_t results = 0;
|
2017-07-19 14:06:10 +00:00
|
|
|
|
2021-02-04 03:22:11 +00:00
|
|
|
std::function<void(eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)> visit;
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2021-02-04 03:22:11 +00:00
|
|
|
visit = [&](eval_cache::AttrCursor & cursor, const std::vector<Symbol> & attrPath, bool initialRecurse)
|
2020-04-20 13:27:09 +00:00
|
|
|
{
|
|
|
|
Activity act(*logger, lvlInfo, actUnknown,
|
|
|
|
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
2017-07-17 17:02:56 +00:00
|
|
|
try {
|
2020-04-20 13:27:09 +00:00
|
|
|
auto recurse = [&]()
|
|
|
|
{
|
|
|
|
for (const auto & attr : cursor.getAttrs()) {
|
|
|
|
auto cursor2 = cursor.getAttr(attr);
|
|
|
|
auto attrPath2(attrPath);
|
|
|
|
attrPath2.push_back(attr);
|
2021-02-04 03:22:11 +00:00
|
|
|
visit(*cursor2, attrPath2, false);
|
2020-04-20 13:27:09 +00:00
|
|
|
}
|
|
|
|
};
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
if (cursor.isDerivation()) {
|
|
|
|
DrvName name(cursor.getAttr("name")->getString());
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
auto aMeta = cursor.maybeGetAttr("meta");
|
|
|
|
auto aDescription = aMeta ? aMeta->maybeGetAttr("description") : nullptr;
|
|
|
|
auto description = aDescription ? aDescription->getString() : "";
|
|
|
|
std::replace(description.begin(), description.end(), '\n', ' ');
|
|
|
|
auto attrPath2 = concatStringsSep(".", attrPath);
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2022-01-19 16:46:29 +00:00
|
|
|
std::vector<std::smatch> attrPathMatches;
|
|
|
|
std::vector<std::smatch> descriptionMatches;
|
|
|
|
std::vector<std::smatch> nameMatches;
|
|
|
|
bool found = false;
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
for (auto & regex : regexes) {
|
2022-01-19 16:46:29 +00:00
|
|
|
found = false;
|
2022-01-19 18:59:02 +00:00
|
|
|
auto add_all = [&found](std::sregex_iterator it, std::vector<std::smatch>& vec){
|
|
|
|
const auto end = std::sregex_iterator();
|
|
|
|
while(it != end) {
|
|
|
|
vec.push_back(*it++);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
add_all(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches);
|
|
|
|
add_all(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches);
|
|
|
|
add_all(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches);
|
2022-01-19 16:46:29 +00:00
|
|
|
|
|
|
|
if(!found)
|
|
|
|
break;
|
2018-04-18 19:08:35 +00:00
|
|
|
}
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2022-01-19 16:46:29 +00:00
|
|
|
if (found)
|
|
|
|
{
|
2020-04-20 13:27:09 +00:00
|
|
|
results++;
|
2017-07-18 15:30:09 +00:00
|
|
|
if (json) {
|
2020-04-20 13:27:09 +00:00
|
|
|
auto jsonElem = jsonOut->object(attrPath2);
|
2020-04-24 12:42:17 +00:00
|
|
|
jsonElem.attr("pname", name.name);
|
2020-04-20 13:27:09 +00:00
|
|
|
jsonElem.attr("version", name.version);
|
2017-07-18 15:30:09 +00:00
|
|
|
jsonElem.attr("description", description);
|
|
|
|
} else {
|
2022-01-20 16:12:15 +00:00
|
|
|
auto name2 = hilite_all(name.name, nameMatches.begin(), nameMatches.end(), "\e[0;2m");
|
2020-09-25 15:30:04 +00:00
|
|
|
if (results > 1) logger->cout("");
|
|
|
|
logger->cout(
|
2020-04-24 12:42:17 +00:00
|
|
|
"* %s%s",
|
2022-01-20 16:12:15 +00:00
|
|
|
wrap("\e[0;1m", hilite_all(attrPath2, attrPathMatches.begin(), attrPathMatches.end(), "\e[0;1m")),
|
2020-04-24 12:42:17 +00:00
|
|
|
name.version != "" ? " (" + name.version + ")" : "");
|
2020-04-20 13:27:09 +00:00
|
|
|
if (description != "")
|
2020-09-25 15:30:04 +00:00
|
|
|
logger->cout(
|
2022-01-20 16:12:15 +00:00
|
|
|
" %s", hilite_all(description, descriptionMatches.begin(), descriptionMatches.end(), ANSI_NORMAL));
|
2017-07-26 15:21:46 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
else if (
|
|
|
|
attrPath.size() == 0
|
|
|
|
|| (attrPath[0] == "legacyPackages" && attrPath.size() <= 2)
|
|
|
|
|| (attrPath[0] == "packages" && attrPath.size() <= 2))
|
|
|
|
recurse();
|
2017-07-17 17:02:56 +00:00
|
|
|
|
2021-02-04 03:22:11 +00:00
|
|
|
else if (initialRecurse)
|
|
|
|
recurse();
|
|
|
|
|
2020-04-27 14:29:26 +00:00
|
|
|
else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) {
|
|
|
|
auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations);
|
|
|
|
if (attr && attr->getBool())
|
|
|
|
recurse();
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-20 13:27:09 +00:00
|
|
|
} catch (EvalError & e) {
|
|
|
|
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
2017-07-19 14:06:10 +00:00
|
|
|
throw;
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-07 12:13:24 +00:00
|
|
|
for (auto & [cursor, prefix] : installable->getCursors(*state))
|
2021-02-04 03:22:11 +00:00
|
|
|
visit(*cursor, parseAttrPath(*state, prefix), true);
|
2017-07-26 15:21:46 +00:00
|
|
|
|
2020-05-07 10:13:16 +00:00
|
|
|
if (!json && !results)
|
2018-05-12 18:00:59 +00:00
|
|
|
throw Error("no results for the given search term(s)!");
|
2017-07-17 17:02:56 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-10-06 11:36:55 +00:00
|
|
|
static auto rCmdSearch = registerCommand<CmdSearch>("search");
|