diff --git a/doc/manual/rl-next/repl-reverse-bt.md b/doc/manual/rl-next/repl-reverse-bt.md new file mode 100644 index 000000000..105e01401 --- /dev/null +++ b/doc/manual/rl-next/repl-reverse-bt.md @@ -0,0 +1,17 @@ +--- +synopsis: "Print REPL backtraces in more convenient order" +issues: [] +cls: [5491] +category: "Improvements" +credits: [blokyk] +--- + +When using the debugger, stack traces printed with the `:bt` command were +previously printed in reverse order compared to most other situations where they +appeared: the current stack frame would be printed at the very top, with the +most outer frame at the bottom, meaning that you'd have to scroll up to get a +sense of where you are. + +With this change, the stack frames are printed such that the most relevant ones +are immediatly visible at the bottom, just like other traces in lix (e.g. +ones caused by errors). diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index 3a006a3c3..6af2c16fb 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -699,17 +699,26 @@ void NixRepl::initDebugBuiltinCommands() addCommand( "backtrace", [](NixRepl & repl, const std::string & _arg) { - auto traces = repl.evaluator.debug->traces(); - for (const auto & [idx, i] : enumerate(traces)) { - std::cout << "\n" << ANSI_BLUE << idx << ANSI_NORMAL << ": "; - showDebugTrace(std::cout, repl.evaluator.positions, *i); + auto tracesGenerator = repl.evaluator.debug->traces(); + + // since we want to print the stack trace in reverse order, + // we have to first traverse all frames and accumulate them + // in a list (in which we store each new trace at the /beginning/) + std::list> reversedTraces; + for (const auto trace : tracesGenerator) { + // because the original traces are indexed from 0 upto N, + // this gives us their original index + auto idx = reversedTraces.size(); + reversedTraces.push_front({idx, trace}); + } + + for (const auto & [traceIdx, trace] : reversedTraces) { + std::cout << "\n" << ANSI_BLUE << traceIdx << ANSI_NORMAL << ": "; + showDebugTrace(std::cout, repl.evaluator.positions, *trace); } return ProcessLineResult::PromptAgain; }, - {.aliases = {"bt"}, - .debugModeOnly = true, - .help = "Show trace stack", - .section = "Debug mode"} + {.aliases = {"bt"}, .debugModeOnly = true, .help = "Show trace stack", .section = "Debug mode"} ); addCommand( diff --git a/tests/functional/repl_characterization/data/debug_frames.test b/tests/functional/repl_characterization/data/debug_frames.test index b97a76f56..fdc603ac2 100644 --- a/tests/functional/repl_characterization/data/debug_frames.test +++ b/tests/functional/repl_characterization/data/debug_frames.test @@ -48,13 +48,14 @@ we can now inspect state nix-repl> :bt - 0: error: undefined variable 'a' + + 1: error: Fake frame for debugging purposes «string»:1:10 1| with {}; a | ^ - 1: error: Fake frame for debugging purposes + 0: error: undefined variable 'a' «string»:1:10 1| with {}; a @@ -92,13 +93,13 @@ leaving the debugger from a toplevel error and entering it again doesn't leave o nix-repl> :bt - 0: error: undefined variable 'b' + 1: error: Fake frame for debugging purposes «string»:1:10 1| with {}; b | ^ - 1: error: Fake frame for debugging purposes + 0: error: undefined variable 'b' «string»:1:10 1| with {}; b diff --git a/tests/functional/repl_characterization/data/regression_9917.test b/tests/functional/repl_characterization/data/regression_9917.test index ec147abd4..b1a88f292 100644 --- a/tests/functional/repl_characterization/data/regression_9917.test +++ b/tests/functional/repl_characterization/data/regression_9917.test @@ -11,37 +11,19 @@ This test ensures that continues don't skip opportunities to enter the debugger. nix-repl> :bt - 0: error: breakpoint reached - $TEST_DATA/regression_9917.nix:3:5 - - 2| a = builtins.trace "before inner break" ( - 3| builtins.break { msg = "hello"; } - | ^ - 4| ); - - 1: while calling a function - $TEST_DATA/regression_9917.nix:3:5 - - 2| a = builtins.trace "before inner break" ( - 3| builtins.break { msg = "hello"; } - | ^ - 4| ); - - 2: while calling a function - $TEST_DATA/regression_9917.nix:2:7 + 6: while evaluating the file '$TEST_DATA/regression_9917.nix': + $TEST_DATA/regression_9917.nix:1:1 1| let + | ^ 2| a = builtins.trace "before inner break" ( - | ^ - 3| builtins.break { msg = "hello"; } - 3: while calling a function - $TEST_DATA/regression_9917.nix:6:5 + 5: while evaluating a 'let' expression + $TEST_DATA/regression_9917.nix:1:1 - 5| b = builtins.trace "before outer break" ( - 6| builtins.break a - | ^ - 7| ); + 1| let + | ^ + 2| a = builtins.trace "before inner break" ( 4: while calling a function $TEST_DATA/regression_9917.nix:5:7 @@ -51,19 +33,37 @@ This test ensures that continues don't skip opportunities to enter the debugger. | ^ 6| builtins.break a - 5: while evaluating a 'let' expression - $TEST_DATA/regression_9917.nix:1:1 + 3: while calling a function + $TEST_DATA/regression_9917.nix:6:5 + + 5| b = builtins.trace "before outer break" ( + 6| builtins.break a + | ^ + 7| ); + + 2: while calling a function + $TEST_DATA/regression_9917.nix:2:7 1| let - | ^ 2| a = builtins.trace "before inner break" ( + | ^ + 3| builtins.break { msg = "hello"; } - 6: while evaluating the file '$TEST_DATA/regression_9917.nix': - $TEST_DATA/regression_9917.nix:1:1 + 1: while calling a function + $TEST_DATA/regression_9917.nix:3:5 - 1| let - | ^ 2| a = builtins.trace "before inner break" ( + 3| builtins.break { msg = "hello"; } + | ^ + 4| ); + + 0: error: breakpoint reached + $TEST_DATA/regression_9917.nix:3:5 + + 2| a = builtins.trace "before inner break" ( + 3| builtins.break { msg = "hello"; } + | ^ + 4| ); nix-repl> :c