Allow multiple search experssions in nix search

The common use case is to search for packages containing multiple words
like a "git" "frontend". Having only one expressions makes this simple regular
use case very complicated. Instead, search accepts multiple regular epressions
which all need to match.

nix search git 'gui|frontend'

returns a list of all git uis for example
This commit is contained in:
Daniel Poelzleithner 2018-04-18 21:08:35 +02:00
parent b37f5ae31d
commit b712d4674b

View file

@ -25,14 +25,14 @@ std::string hilite(const std::string & s, const std::smatch & m)
struct CmdSearch : SourceExprCommand, MixJSON struct CmdSearch : SourceExprCommand, MixJSON
{ {
std::string re; std::vector<std::string> res;
bool writeCache = true; bool writeCache = true;
bool useCache = true; bool useCache = true;
CmdSearch() CmdSearch()
{ {
expectArg("regex", &re, true); expectArgs("regex", &res);
mkFlag() mkFlag()
.longName("update-cache") .longName("update-cache")
@ -68,9 +68,13 @@ struct CmdSearch : SourceExprCommand, MixJSON
"nix search blender" "nix search blender"
}, },
Example{ Example{
"To search for Firefox and Chromium:", "To search for Firefox or Chromium:",
"nix search 'firefox|chromium'" "nix search 'firefox|chromium'"
}, },
Example{
"To search for git and frontend or gui:",
"nix search git 'frontend|gui'"
},
}; };
} }
@ -81,9 +85,16 @@ struct CmdSearch : SourceExprCommand, MixJSON
// Empty search string should match all packages // Empty search string should match all packages
// Use "^" here instead of ".*" due to differences in resulting highlighting // Use "^" here instead of ".*" due to differences in resulting highlighting
// (see #1893 -- libc++ claims empty search string is not in POSIX grammar) // (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
if (re.empty()) re = "^"; if (res.empty()) {
res.push_back("^");
}
std::regex regex(re, std::regex::extended | std::regex::icase); std::vector<std::regex> 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 state = getEvalState();
@ -102,6 +113,7 @@ struct CmdSearch : SourceExprCommand, MixJSON
debug("at attribute '%s'", attrPath); debug("at attribute '%s'", attrPath);
try { try {
uint found = 0;
state->forceValue(*v); state->forceValue(*v);
@ -115,25 +127,33 @@ struct CmdSearch : SourceExprCommand, MixJSON
if (state->isDerivation(*v)) { if (state->isDerivation(*v)) {
DrvInfo drv(*state, attrPath, v->attrs); DrvInfo drv(*state, attrPath, v->attrs);
std::string description;
std::smatch attrPathMatch;
std::smatch descriptionMatch;
std::smatch nameMatch;
std::string name;
DrvName parsed(drv.queryName()); DrvName parsed(drv.queryName());
std::smatch attrPathMatch; for (auto &regex : regexes) {
std::regex_search(attrPath, attrPathMatch, regex); std::regex_search(attrPath, attrPathMatch, regex);
auto name = parsed.name; name = parsed.name;
std::smatch nameMatch;
std::regex_search(name, nameMatch, regex); std::regex_search(name, nameMatch, regex);
std::string description = drv.queryMetaString("description"); description = drv.queryMetaString("description");
std::replace(description.begin(), description.end(), '\n', ' '); std::replace(description.begin(), description.end(), '\n', ' ');
std::smatch descriptionMatch;
std::regex_search(description, descriptionMatch, regex); std::regex_search(description, descriptionMatch, regex);
if (!attrPathMatch.empty() if (!attrPathMatch.empty()
|| !nameMatch.empty() || !nameMatch.empty()
|| !descriptionMatch.empty()) || !descriptionMatch.empty())
{ {
found++;
}
}
if (found == res.size()) {
if (json) { if (json) {
auto jsonElem = jsonOut->object(attrPath); auto jsonElem = jsonOut->object(attrPath);