libcmd: don't enter debugger immediately on nix repl --debugger

checking whether a repl hook is set does not tell whether it's running,
which in turn means we will enter a debug repl even when not debugging.
this is not very useful when we have no debug frames to inspect at all,
as commonly happens when starting a new repl with --debugger specified.

Change-Id: I7065dc4ec29743bdd53ed99c29d6592e2ceea89c
This commit is contained in:
eldritch horrors 2024-11-27 02:09:08 +01:00
parent 9554a1ae29
commit 89a0ddc108
3 changed files with 82 additions and 8 deletions

View file

@ -285,7 +285,7 @@ ReplExitStatus NixRepl::mainLoop()
{
if (isFirstRepl) {
std::string_view debuggerNotice = "";
if (state.debug.repl) {
if (state.debug.inDebugger) {
debuggerNotice = " debugger";
}
notice("Lix %1%%2%\nType :? for help.", nixVersion, debuggerNotice);
@ -366,7 +366,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
}
}
if (state.debug.repl) {
if (state.debug.inDebugger) {
for (auto const & colonCmd : this->DEBUG_COMMANDS) {
if (colonCmd.starts_with(prefix)) {
completions.insert(std::string(colonCmd));
@ -541,7 +541,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
<< " errors\n"
<< " :?, :help Brings up this help menu\n"
;
if (state.debug.repl) {
if (state.debug.inDebugger) {
std::cout
<< "\n"
<< " Debug mode commands\n"
@ -556,14 +556,14 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
else if (state.debug.repl && (command == ":bt" || command == ":backtrace")) {
else if (state.debug.inDebugger && (command == ":bt" || command == ":backtrace")) {
for (const auto & [idx, i] : enumerate(state.debug.traces)) {
std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": ";
showDebugTrace(std::cout, state.positions, i);
}
}
else if (state.debug.repl && (command == ":env")) {
else if (state.debug.inDebugger && (command == ":env")) {
for (const auto & [idx, i] : enumerate(state.debug.traces)) {
if (idx == debugTraceIndex) {
printEnvBindings(state, i.expr, i.env);
@ -572,7 +572,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
else if (state.debug.repl && (command == ":st")) {
else if (state.debug.inDebugger && (command == ":st")) {
try {
// change the DebugTrace index.
debugTraceIndex = stoi(arg);
@ -590,13 +590,13 @@ ProcessLineResult NixRepl::processLine(std::string line)
}
}
else if (state.debug.repl && (command == ":s" || command == ":step")) {
else if (state.debug.inDebugger && (command == ":s" || command == ":step")) {
// set flag to stop at next DebugTrace; exit repl.
state.debug.stop = true;
return ProcessLineResult::Continue;
}
else if (state.debug.repl && (command == ":c" || command == ":continue")) {
else if (state.debug.inDebugger && (command == ":c" || command == ":continue")) {
// set flag to run to next breakpoint or end of program; exit repl.
state.debug.stop = false;
return ProcessLineResult::Continue;

View file

@ -0,0 +1,73 @@
@args --debugger
:c at the root repl is not allowed since no debugger is running yet
nix-repl> :c
error: unknown command ':c'
:c and other commands become available once a debugger starts
nix-repl> with {}; a
error: undefined variable 'a'
at «string»:1:10:
1| with {}; a
| ^
nix-repl> :?
The following commands are available:
<expr> Evaluate and print expression
<x> = <expr> Bind expression to variable
:a, :add <expr> Add attributes from resulting set to scope
:b <expr> Build a derivation
:bl <expr> Build a derivation, creating GC roots in the
working directory
:e, :edit <expr> Open package or function in $EDITOR
:i <expr> Build derivation, then install result into
current profile
:l, :load <path> Load Nix expression and add it to scope
:lf, :load-flake <ref> Load Nix flake and add it to scope
:p, :print <expr> Evaluate and print expression recursively
Strings are printed directly, without escaping.
:q, :quit Exit nix-repl
:r, :reload Reload all files
:sh <expr> Build dependencies of derivation, then start
nix-shell
:t <expr> Describe result of evaluation
:u <expr> Build derivation, then start nix-shell
:doc <expr> Show documentation for the provided function (experimental lambda support)
:log <expr | .drv path> Show logs for a derivation
:te, :trace-enable [bool] Enable, disable or toggle showing traces for
errors
:?, :help Brings up this help menu
Debug mode commands
:env Show env stack
:bt, :backtrace Show trace stack
:st Show current trace
:st <idx> Change to another trace in the stack
:c, :continue Go until end of program, exception, or builtins.break
:s, :step Go one step
we can now inspect state
nix-repl> :bt
0: error: undefined variable 'a'
«string»:1:10
1| with {}; a
| ^
1: error: Fake frame for debugging purposes
«string»:1:10
1| with {}; a
| ^
and resume execution
nix-repl> :c
error: undefined variable 'a'
at «string»:1:10:
1| with {}; a
| ^
the debugger is once again disabled
nix-repl> :c
error: unknown command ':c'

View file

@ -186,5 +186,6 @@ REPL_TEST(repl_printing);
REPL_TEST(stack_vars);
REPL_TEST(errors);
REPL_TEST(idempotent);
REPL_TEST(debug_frames);
}; // namespace nix