Add a description in the completion outputs

Make nix output completions in the form `completion\tdescription`.
This can't be used by bash (afaik), but other shells like zsh or fish
can display it along the completion choices
This commit is contained in:
regnat 2020-10-09 09:39:51 +02:00
parent 58dadf2954
commit 04e5d0e704
6 changed files with 42 additions and 20 deletions

View file

@ -4,13 +4,14 @@ function _complete_nix {
_get_comp_words_by_ref -n ':=&' words cword cur _get_comp_words_by_ref -n ':=&' words cword cur
local have_type local have_type
while IFS= read -r line; do while IFS= read -r line; do
local completion=${line%% *}
if [[ -z $have_type ]]; then if [[ -z $have_type ]]; then
have_type=1 have_type=1
if [[ $line = filenames ]]; then if [[ $completion = filenames ]]; then
compopt -o filenames compopt -o filenames
fi fi
else else
COMPREPLY+=("$line") COMPREPLY+=("$completion")
fi fi
done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}") done < <(NIX_GET_COMPLETIONS=$cword "${words[@]}")
__ltrim_colon_completions "$cur" __ltrim_colon_completions "$cur"

View file

@ -44,7 +44,7 @@ MixCommonArgs::MixCommonArgs(const string & programName)
globalConfig.getSettings(settings); globalConfig.getSettings(settings);
for (auto & s : settings) for (auto & s : settings)
if (hasPrefix(s.first, prefix)) if (hasPrefix(s.first, prefix))
completions->insert(s.first); completions->add(s.first, s.second.description);
} }
} }
}); });

View file

@ -17,8 +17,19 @@ void Args::addFlag(Flag && flag_)
if (flag->shortName) shortFlags[flag->shortName] = flag; if (flag->shortName) shortFlags[flag->shortName] = flag;
} }
void Completions::add(std::string completion, std::string description)
{
insert(Completion{
.completion = completion,
.description = description
});
}
bool Completion::operator<(const Completion & other) const
{ return completion < other.completion || (completion == other.completion && description < other.description); }
bool pathCompletions = false; bool pathCompletions = false;
std::shared_ptr<std::set<std::string>> completions; std::shared_ptr<Completions> completions;
std::string completionMarker = "___COMPLETE___"; std::string completionMarker = "___COMPLETE___";
@ -148,7 +159,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
for (auto & [name, flag] : longFlags) { for (auto & [name, flag] : longFlags) {
if (!hiddenCategories.count(flag->category) if (!hiddenCategories.count(flag->category)
&& hasPrefix(name, std::string(*prefix, 2))) && hasPrefix(name, std::string(*prefix, 2)))
completions->insert("--" + name); completions->add("--" + name, flag->description);
} }
} }
auto i = longFlags.find(string(*pos, 2)); auto i = longFlags.find(string(*pos, 2));
@ -165,9 +176,9 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
if (auto prefix = needsCompletion(*pos)) { if (auto prefix = needsCompletion(*pos)) {
if (prefix == "-") { if (prefix == "-") {
completions->insert("--"); completions->add("--");
for (auto & [flag, _] : shortFlags) for (auto & [flagName, flag] : shortFlags)
completions->insert(std::string("-") + flag); completions->add(std::string("-") + flagName, flag->description);
} }
} }
@ -244,11 +255,11 @@ nlohmann::json Args::toJSON()
return res; return res;
} }
static void hashTypeCompleter(size_t index, std::string_view prefix) static void hashTypeCompleter(size_t index, std::string_view prefix)
{ {
for (auto & type : hashTypes) for (auto & type : hashTypes)
if (hasPrefix(type, prefix)) if (hasPrefix(type, prefix))
completions->insert(type); completions->add(type);
} }
Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht) Args::Flag Args::Flag::mkHashTypeFlag(std::string && longName, HashType * ht)
@ -292,7 +303,7 @@ static void _completePath(std::string_view prefix, bool onlyDirs)
auto st = lstat(globbuf.gl_pathv[i]); auto st = lstat(globbuf.gl_pathv[i]);
if (!S_ISDIR(st.st_mode)) continue; if (!S_ISDIR(st.st_mode)) continue;
} }
completions->insert(globbuf.gl_pathv[i]); completions->add(globbuf.gl_pathv[i]);
} }
globfree(&globbuf); globfree(&globbuf);
} }
@ -385,7 +396,7 @@ MultiCommand::MultiCommand(const Commands & commands)
if (auto prefix = needsCompletion(s)) { if (auto prefix = needsCompletion(s)) {
for (auto & [name, command] : commands) for (auto & [name, command] : commands)
if (hasPrefix(name, *prefix)) if (hasPrefix(name, *prefix))
completions->insert(name); completions->add(name);
} }
auto i = commands.find(s); auto i = commands.find(s);
if (i == commands.end()) if (i == commands.end())

View file

@ -283,7 +283,17 @@ typedef std::vector<std::pair<std::string, std::string>> Table2;
void printTable(std::ostream & out, const Table2 & table); void printTable(std::ostream & out, const Table2 & table);
extern std::shared_ptr<std::set<std::string>> completions; struct Completion {
std::string completion;
std::string description;
bool operator<(const Completion & other) const;
};
class Completions : public std::set<Completion> {
public:
void add(std::string completion, std::string description = "");
};
extern std::shared_ptr<Completions> completions;
extern bool pathCompletions; extern bool pathCompletions;
std::optional<std::string> needsCompletion(std::string_view s); std::optional<std::string> needsCompletion(std::string_view s);

View file

@ -26,7 +26,7 @@ void completeFlakeInputPath(
auto flake = flake::getFlake(*evalState, flakeRef, true); auto flake = flake::getFlake(*evalState, flakeRef, true);
for (auto & input : flake.inputs) for (auto & input : flake.inputs)
if (hasPrefix(input.first, prefix)) if (hasPrefix(input.first, prefix))
completions->insert(input.first); completions->add(input.first);
} }
MixFlakeOptions::MixFlakeOptions() MixFlakeOptions::MixFlakeOptions()
@ -211,7 +211,7 @@ void completeFlakeRefWithFragment(
auto attrPath2 = attr->getAttrPath(attr2); auto attrPath2 = attr->getAttrPath(attr2);
/* Strip the attrpath prefix. */ /* Strip the attrpath prefix. */
attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size());
completions->insert(flakeRefS + "#" + concatStringsSep(".", attrPath2)); completions->add(flakeRefS + "#" + concatStringsSep(".", attrPath2));
} }
} }
} }
@ -222,7 +222,7 @@ void completeFlakeRefWithFragment(
for (auto & attrPath : defaultFlakeAttrPaths) { for (auto & attrPath : defaultFlakeAttrPaths) {
auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath)); auto attr = root->findAlongAttrPath(parseAttrPath(*evalState, attrPath));
if (!attr) continue; if (!attr) continue;
completions->insert(flakeRefS + "#"); completions->add(flakeRefS + "#");
} }
} }
} }
@ -243,7 +243,7 @@ ref<EvalState> EvalCommand::getEvalState()
void completeFlakeRef(ref<Store> store, std::string_view prefix) void completeFlakeRef(ref<Store> store, std::string_view prefix)
{ {
if (prefix == "") if (prefix == "")
completions->insert("."); completions->add(".");
completeDir(0, prefix); completeDir(0, prefix);
@ -254,10 +254,10 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) {
std::string from2(from, 6); std::string from2(from, 6);
if (hasPrefix(from2, prefix)) if (hasPrefix(from2, prefix))
completions->insert(from2); completions->add(from2);
} else { } else {
if (hasPrefix(from, prefix)) if (hasPrefix(from, prefix))
completions->insert(from); completions->add(from);
} }
} }
} }

View file

@ -208,7 +208,7 @@ void mainWrapped(int argc, char * * argv)
if (completions) { if (completions) {
std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n"); std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
for (auto & s : *completions) for (auto & s : *completions)
std::cout << s << "\n"; std::cout << s.completion << "\t" << s.description << "\n";
} }
}); });