#include "command.hh" #include "globals.hh" #include "eval.hh" #include "eval-inline.hh" #include "names.hh" #include "get-drvs.hh" #include "common-args.hh" #include "json.hh" #include "shared.hh" #include "eval-cache.hh" #include "attr-path.hh" #include "hilite.hh" #include #include using namespace nix; std::string wrap(std::string prefix, std::string s) { return prefix + s + ANSI_NORMAL; } struct CmdSearch : InstallableCommand, MixJSON { std::vector res; CmdSearch() { expectArgs("regex", &res); } std::string description() override { return "search for packages"; } std::string doc() override { return #include "search.md" ; } Strings getDefaultFlakeAttrPaths() override { return { "packages." + settings.thisSystem.get() + ".", "legacyPackages." + settings.thisSystem.get() + "." }; } void run(ref store) override { settings.readOnlyMode = true; evalSettings.enableImportFromDerivation.setDefault(false); // 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) if (res.empty()) res.push_back("^"); std::vector regexes; regexes.reserve(res.size()); for (auto & re : res) regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); auto state = getEvalState(); auto jsonOut = json ? std::make_unique(std::cout) : nullptr; uint64_t results = 0; std::function & attrPath, bool initialRecurse)> visit; visit = [&](eval_cache::AttrCursor & cursor, const std::vector & attrPath, bool initialRecurse) { Activity act(*logger, lvlInfo, actUnknown, fmt("evaluating '%s'", concatStringsSep(".", attrPath))); try { auto recurse = [&]() { for (const auto & attr : cursor.getAttrs()) { auto cursor2 = cursor.getAttr(attr); auto attrPath2(attrPath); attrPath2.push_back(attr); visit(*cursor2, attrPath2, false); } }; if (cursor.isDerivation()) { DrvName name(cursor.getAttr("name")->getString()); 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); std::vector attrPathMatches; std::vector descriptionMatches; std::vector nameMatches; bool found = false; for (auto & regex : regexes) { found = false; auto addAll = [&found](std::sregex_iterator it, std::vector & vec) { const auto end = std::sregex_iterator(); while (it != end) { vec.push_back(*it++); found = true; } }; addAll(std::sregex_iterator(attrPath2.begin(), attrPath2.end(), regex), attrPathMatches); addAll(std::sregex_iterator(name.name.begin(), name.name.end(), regex), nameMatches); addAll(std::sregex_iterator(description.begin(), description.end(), regex), descriptionMatches); if (!found) break; } if (found) { results++; if (json) { auto jsonElem = jsonOut->object(attrPath2); jsonElem.attr("pname", name.name); jsonElem.attr("version", name.version); jsonElem.attr("description", description); } else { auto name2 = hiliteMatches(name.name, std::move(nameMatches), ANSI_GREEN, "\e[0;2m"); if (results > 1) logger->cout(""); logger->cout( "* %s%s", wrap("\e[0;1m", hiliteMatches(attrPath2, std::move(attrPathMatches), ANSI_GREEN, "\e[0;1m")), name.version != "" ? " (" + name.version + ")" : ""); if (description != "") logger->cout( " %s", hiliteMatches(description, std::move(descriptionMatches), ANSI_GREEN, ANSI_NORMAL)); } } } else if ( attrPath.size() == 0 || (attrPath[0] == "legacyPackages" && attrPath.size() <= 2) || (attrPath[0] == "packages" && attrPath.size() <= 2)) recurse(); else if (initialRecurse) recurse(); else if (attrPath[0] == "legacyPackages" && attrPath.size() > 2) { auto attr = cursor.maybeGetAttr(state->sRecurseForDerivations); if (attr && attr->getBool()) recurse(); } } catch (EvalError & e) { if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages")) throw; } }; for (auto & cursor : installable->getCursors(*state)) visit(*cursor, cursor->getAttrPath(), true); if (!json && !results) throw Error("no results for the given search term(s)!"); } }; static auto rCmdSearch = registerCommand("search");