forked from lix-project/lix
Merge pull request #6693 from ncfavier/complete-flake-inputs
Improve shell completion of flake inputs
This commit is contained in:
commit
f071eb3702
|
@ -78,10 +78,16 @@ struct MixFlakeOptions : virtual Args, EvalCommand
|
||||||
{
|
{
|
||||||
flake::LockFlags lockFlags;
|
flake::LockFlags lockFlags;
|
||||||
|
|
||||||
|
std::optional<std::string> needsFlakeInputCompletion = {};
|
||||||
|
|
||||||
MixFlakeOptions();
|
MixFlakeOptions();
|
||||||
|
|
||||||
virtual std::optional<FlakeRef> getFlakeRefForCompletion()
|
virtual std::vector<std::string> getFlakesForCompletion()
|
||||||
{ return {}; }
|
{ return {}; }
|
||||||
|
|
||||||
|
void completeFlakeInput(std::string_view prefix);
|
||||||
|
|
||||||
|
void completionHook() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
struct SourceExprCommand : virtual Args, MixFlakeOptions
|
||||||
|
@ -121,7 +127,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand
|
||||||
|
|
||||||
virtual bool useDefaultInstallables() { return true; }
|
virtual bool useDefaultInstallables() { return true; }
|
||||||
|
|
||||||
std::optional<FlakeRef> getFlakeRefForCompletion() override;
|
std::vector<std::string> getFlakesForCompletion() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -137,9 +143,9 @@ struct InstallableCommand : virtual Args, SourceExprCommand
|
||||||
|
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
|
||||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
std::vector<std::string> getFlakesForCompletion() override
|
||||||
{
|
{
|
||||||
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
|
return {_installable};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -23,17 +23,6 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
void completeFlakeInputPath(
|
|
||||||
ref<EvalState> evalState,
|
|
||||||
const FlakeRef & flakeRef,
|
|
||||||
std::string_view prefix)
|
|
||||||
{
|
|
||||||
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
|
||||||
for (auto & input : flake.inputs)
|
|
||||||
if (hasPrefix(input.first, prefix))
|
|
||||||
completions->add(input.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
MixFlakeOptions::MixFlakeOptions()
|
MixFlakeOptions::MixFlakeOptions()
|
||||||
{
|
{
|
||||||
auto category = "Common flake-related options";
|
auto category = "Common flake-related options";
|
||||||
|
@ -86,8 +75,7 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
|
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
|
||||||
}},
|
}},
|
||||||
.completer = {[&](size_t, std::string_view prefix) {
|
.completer = {[&](size_t, std::string_view prefix) {
|
||||||
if (auto flakeRef = getFlakeRefForCompletion())
|
needsFlakeInputCompletion = {std::string(prefix)};
|
||||||
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -103,12 +91,10 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
parseFlakeRef(flakeRef, absPath("."), true));
|
parseFlakeRef(flakeRef, absPath("."), true));
|
||||||
}},
|
}},
|
||||||
.completer = {[&](size_t n, std::string_view prefix) {
|
.completer = {[&](size_t n, std::string_view prefix) {
|
||||||
if (n == 0) {
|
if (n == 0)
|
||||||
if (auto flakeRef = getFlakeRefForCompletion())
|
needsFlakeInputCompletion = {std::string(prefix)};
|
||||||
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
|
else if (n == 1)
|
||||||
} else if (n == 1) {
|
|
||||||
completeFlakeRef(getEvalState()->store, prefix);
|
completeFlakeRef(getEvalState()->store, prefix);
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -139,6 +125,24 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MixFlakeOptions::completeFlakeInput(std::string_view prefix)
|
||||||
|
{
|
||||||
|
auto evalState = getEvalState();
|
||||||
|
for (auto & flakeRefS : getFlakesForCompletion()) {
|
||||||
|
auto flakeRef = parseFlakeRefWithFragment(expandTilde(flakeRefS), absPath(".")).first;
|
||||||
|
auto flake = flake::getFlake(*evalState, flakeRef, true);
|
||||||
|
for (auto & input : flake.inputs)
|
||||||
|
if (hasPrefix(input.first, prefix))
|
||||||
|
completions->add(input.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MixFlakeOptions::completionHook()
|
||||||
|
{
|
||||||
|
if (auto & prefix = needsFlakeInputCompletion)
|
||||||
|
completeFlakeInput(*prefix);
|
||||||
|
}
|
||||||
|
|
||||||
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
|
||||||
{
|
{
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -1048,14 +1052,14 @@ Installables InstallablesCommand::load() {
|
||||||
return parseInstallables(getStore(), _installables);
|
return parseInstallables(getStore(), _installables);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
|
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
|
||||||
{
|
{
|
||||||
if (_installables.empty()) {
|
if (_installables.empty()) {
|
||||||
if (useDefaultInstallables())
|
if (useDefaultInstallables())
|
||||||
return parseFlakeRefWithFragment(".", absPath(".")).first;
|
return {"."};
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
|
return _installables;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
|
||||||
|
|
|
@ -124,7 +124,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
bool anyCompleted = false;
|
bool anyCompleted = false;
|
||||||
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
||||||
if (pos == end) {
|
if (pos == end) {
|
||||||
if (flag.handler.arity == ArityAny) break;
|
if (flag.handler.arity == ArityAny || anyCompleted) break;
|
||||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
||||||
}
|
}
|
||||||
if (auto prefix = needsCompletion(*pos)) {
|
if (auto prefix = needsCompletion(*pos)) {
|
||||||
|
@ -362,6 +362,14 @@ bool MultiCommand::processArgs(const Strings & args, bool finish)
|
||||||
return Args::processArgs(args, finish);
|
return Args::processArgs(args, finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiCommand::completionHook()
|
||||||
|
{
|
||||||
|
if (command)
|
||||||
|
return command->second->completionHook();
|
||||||
|
else
|
||||||
|
return Args::completionHook();
|
||||||
|
}
|
||||||
|
|
||||||
nlohmann::json MultiCommand::toJSON()
|
nlohmann::json MultiCommand::toJSON()
|
||||||
{
|
{
|
||||||
auto cmds = nlohmann::json::object();
|
auto cmds = nlohmann::json::object();
|
||||||
|
|
|
@ -148,6 +148,11 @@ protected:
|
||||||
argument (if any) have been processed. */
|
argument (if any) have been processed. */
|
||||||
virtual void initialFlagsProcessed() {}
|
virtual void initialFlagsProcessed() {}
|
||||||
|
|
||||||
|
/* Called after the command line has been processed if we need to generate
|
||||||
|
completions. Useful for commands that need to know the whole command line
|
||||||
|
in order to know what completions to generate. */
|
||||||
|
virtual void completionHook() { }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void addFlag(Flag && flag);
|
void addFlag(Flag && flag);
|
||||||
|
@ -223,6 +228,8 @@ public:
|
||||||
|
|
||||||
bool processArgs(const Strings & args, bool finish) override;
|
bool processArgs(const Strings & args, bool finish) override;
|
||||||
|
|
||||||
|
void completionHook() override;
|
||||||
|
|
||||||
nlohmann::json toJSON() override;
|
nlohmann::json toJSON() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -50,9 +50,9 @@ public:
|
||||||
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
|
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<FlakeRef> getFlakeRefForCompletion() override
|
std::vector<std::string> getFlakesForCompletion() override
|
||||||
{
|
{
|
||||||
return getFlakeRef();
|
return {flakeUrl};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,10 @@ void mainWrapped(int argc, char * * argv)
|
||||||
if (!completions) throw;
|
if (!completions) throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (completions) return;
|
if (completions) {
|
||||||
|
args.completionHook();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (args.showVersion) {
|
if (args.showVersion) {
|
||||||
printVersion(programName);
|
printVersion(programName);
|
||||||
|
|
Loading…
Reference in a new issue