Skip to content

Commit

Permalink
Merge pull request #6693 from ncfavier/complete-flake-inputs
Browse files Browse the repository at this point in the history
Improve shell completion of flake inputs
  • Loading branch information
thufschmitt committed Jul 12, 2022
2 parents 7e23039 + 711b2e1 commit f071eb3
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 29 deletions.
14 changes: 10 additions & 4 deletions src/libcmd/command.hh
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,16 @@ struct MixFlakeOptions : virtual Args, EvalCommand
{
flake::LockFlags lockFlags;

std::optional<std::string> needsFlakeInputCompletion = {};

MixFlakeOptions();

virtual std::optional<FlakeRef> getFlakeRefForCompletion()
virtual std::vector<std::string> getFlakesForCompletion()
{ return {}; }

void completeFlakeInput(std::string_view prefix);

void completionHook() override;
};

struct SourceExprCommand : virtual Args, MixFlakeOptions
Expand Down Expand Up @@ -121,7 +127,7 @@ struct InstallablesCommand : virtual Args, SourceExprCommand

virtual bool useDefaultInstallables() { return true; }

std::optional<FlakeRef> getFlakeRefForCompletion() override;
std::vector<std::string> getFlakesForCompletion() override;

protected:

Expand All @@ -137,9 +143,9 @@ struct InstallableCommand : virtual Args, SourceExprCommand

void prepare() override;

std::optional<FlakeRef> getFlakeRefForCompletion() override
std::vector<std::string> getFlakesForCompletion() override
{
return parseFlakeRefWithFragment(_installable, absPath(".")).first;
return {_installable};
}

private:
Expand Down
46 changes: 25 additions & 21 deletions src/libcmd/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,6 @@

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()
{
auto category = "Common flake-related options";
Expand Down Expand Up @@ -86,8 +75,7 @@ MixFlakeOptions::MixFlakeOptions()
lockFlags.inputUpdates.insert(flake::parseInputPath(s));
}},
.completer = {[&](size_t, std::string_view prefix) {
if (auto flakeRef = getFlakeRefForCompletion())
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
needsFlakeInputCompletion = {std::string(prefix)};
}}
});

Expand All @@ -103,12 +91,10 @@ MixFlakeOptions::MixFlakeOptions()
parseFlakeRef(flakeRef, absPath("."), true));
}},
.completer = {[&](size_t n, std::string_view prefix) {
if (n == 0) {
if (auto flakeRef = getFlakeRefForCompletion())
completeFlakeInputPath(getEvalState(), *flakeRef, prefix);
} else if (n == 1) {
if (n == 0)
needsFlakeInputCompletion = {std::string(prefix)};
else if (n == 1)
completeFlakeRef(getEvalState()->store, prefix);
}
}}
});

Expand Down Expand Up @@ -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)
{
addFlag({
Expand Down Expand Up @@ -1048,14 +1052,14 @@ Installables InstallablesCommand::load() {
return parseInstallables(getStore(), _installables);
}

std::optional<FlakeRef> InstallablesCommand::getFlakeRefForCompletion()
std::vector<std::string> InstallablesCommand::getFlakesForCompletion()
{
if (_installables.empty()) {
if (useDefaultInstallables())
return parseFlakeRefWithFragment(".", absPath(".")).first;
return {"."};
return {};
}
return parseFlakeRefWithFragment(_installables.front(), absPath(".")).first;
return _installables;
}

InstallableCommand::InstallableCommand(bool supportReadOnlyMode)
Expand Down
10 changes: 9 additions & 1 deletion src/libutil/args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
bool anyCompleted = false;
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
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);
}
if (auto prefix = needsCompletion(*pos)) {
Expand Down Expand Up @@ -362,6 +362,14 @@ bool MultiCommand::processArgs(const Strings & args, bool finish)
return Args::processArgs(args, finish);
}

void MultiCommand::completionHook()
{
if (command)
return command->second->completionHook();
else
return Args::completionHook();
}

nlohmann::json MultiCommand::toJSON()
{
auto cmds = nlohmann::json::object();
Expand Down
7 changes: 7 additions & 0 deletions src/libutil/args.hh
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ protected:
argument (if any) have been processed. */
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:

void addFlag(Flag && flag);
Expand Down Expand Up @@ -223,6 +228,8 @@ public:

bool processArgs(const Strings & args, bool finish) override;

void completionHook() override;

nlohmann::json toJSON() override;
};

Expand Down
4 changes: 2 additions & 2 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ class FlakeCommand : virtual Args, public MixFlakeOptions
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
}

std::optional<FlakeRef> getFlakeRefForCompletion() override
std::vector<std::string> getFlakesForCompletion() override
{
return getFlakeRef();
return {flakeUrl};
}
};

Expand Down
5 changes: 4 additions & 1 deletion src/nix/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,10 @@ void mainWrapped(int argc, char * * argv)
if (!completions) throw;
}

if (completions) return;
if (completions) {
args.completionHook();
return;
}

if (args.showVersion) {
printVersion(programName);
Expand Down

0 comments on commit f071eb3

Please sign in to comment.