Skip to content

Commit 2c1447b

Browse files
authored
Merge pull request #12604 from NixOS/issue-12599
Fix chopped up repl output
2 parents a5cf291 + 1e1c587 commit 2c1447b

File tree

8 files changed

+72
-41
lines changed

8 files changed

+72
-41
lines changed

src/libcmd/repl.cc

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ struct NixRepl
102102
unsigned int maxDepth = std::numeric_limits<unsigned int>::max())
103103
{
104104
// Hide the progress bar during printing because it might interfere
105-
logger->pause();
106-
Finally resumeLoggerDefer([]() { logger->resume(); });
105+
auto suspension = logger->suspend();
107106
::nix::printValue(*state, str, v, PrintOptions {
108107
.ansiColors = true,
109108
.force = true,
@@ -180,18 +179,20 @@ ReplExitStatus NixRepl::mainLoop()
180179

181180
while (true) {
182181
// Hide the progress bar while waiting for user input, so that it won't interfere.
183-
logger->pause();
184-
// When continuing input from previous lines, don't print a prompt, just align to the same
185-
// number of chars as the prompt.
186-
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
187-
// Ctrl-D should exit the debugger.
188-
state->debugStop = false;
189-
logger->cout("");
190-
// TODO: Should Ctrl-D exit just the current debugger session or
191-
// the entire program?
192-
return ReplExitStatus::QuitAll;
182+
{
183+
auto suspension = logger->suspend();
184+
// When continuing input from previous lines, don't print a prompt, just align to the same
185+
// number of chars as the prompt.
186+
if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) {
187+
// Ctrl-D should exit the debugger.
188+
state->debugStop = false;
189+
logger->cout("");
190+
// TODO: Should Ctrl-D exit just the current debugger session or
191+
// the entire program?
192+
return ReplExitStatus::QuitAll;
193+
}
194+
// `suspension` resumes the logger
193195
}
194-
logger->resume();
195196
try {
196197
switch (processLine(input)) {
197198
case ProcessLineResult::Quit:
@@ -586,6 +587,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
586587
else if (command == ":p" || command == ":print") {
587588
Value v;
588589
evalString(arg, v);
590+
auto suspension = logger->suspend();
589591
if (v.type() == nString) {
590592
std::cout << v.string_view();
591593
} else {
@@ -694,6 +696,7 @@ ProcessLineResult NixRepl::processLine(std::string line)
694696
} else {
695697
Value v;
696698
evalString(line, v);
699+
auto suspension = logger->suspend();
697700
printValue(std::cout, v, 1);
698701
std::cout << std::endl;
699702
}

src/libfetchers/git.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,7 @@ struct GitInputScheme : InputScheme
351351

352352
if (commitMsg) {
353353
// Pause the logger to allow for user input (such as a gpg passphrase) in `git commit`
354-
logger->pause();
355-
Finally restoreLogger([]() { logger->resume(); });
354+
auto suspension = logger->suspend();
356355
runProgram("git", true,
357356
{ "-C", repoPath->string(), "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" },
358357
*commitMsg);

src/libmain/progress-bar.cc

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,13 @@ class ProgressBar : public Logger
7373
uint64_t corruptedPaths = 0, untrustedPaths = 0;
7474

7575
bool active = true;
76-
bool paused = false;
76+
size_t suspensions = 0;
7777
bool haveUpdate = true;
78+
79+
bool isPaused() const
80+
{
81+
return suspensions > 0;
82+
}
7883
};
7984

8085
/** Helps avoid unnecessary redraws, see `redraw()` */
@@ -130,18 +135,30 @@ class ProgressBar : public Logger
130135

131136
void pause() override {
132137
auto state (state_.lock());
133-
state->paused = true;
138+
state->suspensions++;
139+
if (state->suspensions > 1) {
140+
// already paused
141+
return;
142+
}
143+
134144
if (state->active)
135145
writeToStderr("\r\e[K");
136146
}
137147

138148
void resume() override {
139149
auto state (state_.lock());
140-
state->paused = false;
141-
if (state->active)
142-
writeToStderr("\r\e[K");
143-
state->haveUpdate = true;
144-
updateCV.notify_one();
150+
if (state->suspensions == 0) {
151+
log(lvlError, "nix::ProgressBar: resume() called without a matching preceding pause(). This is a bug.");
152+
return;
153+
} else {
154+
state->suspensions--;
155+
}
156+
if (state->suspensions == 0) {
157+
if (state->active)
158+
writeToStderr("\r\e[K");
159+
state->haveUpdate = true;
160+
updateCV.notify_one();
161+
}
145162
}
146163

147164
bool isVerbose() override
@@ -383,7 +400,7 @@ class ProgressBar : public Logger
383400
auto nextWakeup = std::chrono::milliseconds::max();
384401

385402
state.haveUpdate = false;
386-
if (state.paused || !state.active) return nextWakeup;
403+
if (state.isPaused() || !state.active) return nextWakeup;
387404

388405
std::string line;
389406

src/libstore/ssh.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
117117
ProcessOptions options;
118118
options.dieWithParent = false;
119119

120+
std::unique_ptr<Logger::Suspension> loggerSuspension;
120121
if (!fakeSSH && !useMaster) {
121-
logger->pause();
122+
loggerSuspension = std::make_unique<Logger::Suspension>(logger->suspend());
122123
}
123-
Finally cleanup = [&]() { logger->resume(); };
124124

125125
conn->sshPid = startProcess([&]() {
126126
restoreProcessContext();
@@ -199,8 +199,7 @@ Path SSHMaster::startMaster()
199199
ProcessOptions options;
200200
options.dieWithParent = false;
201201

202-
logger->pause();
203-
Finally cleanup = [&]() { logger->resume(); };
202+
auto suspension = logger->suspend();
204203

205204
if (isMasterRunning())
206205
return state->socketPath;

src/libutil/logging.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ void Logger::writeToStdout(std::string_view s)
4343
writeFull(standard_out, "\n");
4444
}
4545

46+
Logger::Suspension Logger::suspend()
47+
{
48+
pause();
49+
return Suspension { ._finalize = {[this](){this->resume();}} };
50+
}
51+
52+
std::optional<Logger::Suspension> Logger::suspendIf(bool cond)
53+
{
54+
if (cond)
55+
return suspend();
56+
return {};
57+
}
58+
4659
class SimpleLogger : public Logger
4760
{
4861
public:

src/libutil/logging.hh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "error.hh"
55
#include "config.hh"
66
#include "file-descriptor.hh"
7+
#include "finally.hh"
78

89
#include <nlohmann/json_fwd.hpp>
910

@@ -75,6 +76,17 @@ public:
7576

7677
virtual void stop() { };
7778

79+
/**
80+
* Guard object to resume the logger when done.
81+
*/
82+
struct Suspension {
83+
Finally<std::function<void()>> _finalize;
84+
};
85+
86+
Suspension suspend();
87+
88+
std::optional<Suspension> suspendIf(bool cond);
89+
7890
virtual void pause() { };
7991
virtual void resume() { };
8092

src/libutil/unix/processes.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -306,15 +306,7 @@ void runProgram2(const RunOptions & options)
306306
// case), so we can't use it if we alter the environment
307307
processOptions.allowVfork = !options.environment;
308308

309-
std::optional<Finally<std::function<void()>>> resumeLoggerDefer;
310-
if (options.isInteractive) {
311-
logger->pause();
312-
resumeLoggerDefer.emplace(
313-
[]() {
314-
logger->resume();
315-
}
316-
);
317-
}
309+
auto suspension = logger->suspendIf(options.isInteractive);
318310

319311
/* Fork. */
320312
Pid pid = startProcess([&] {

src/libutil/windows/processes.cc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,7 @@ void runProgram2(const RunOptions & options)
312312
// TODO: Implement shebang / program interpreter lookup on Windows
313313
auto interpreter = getProgramInterpreter(realProgram);
314314

315-
std::optional<Finally<std::function<void()>>> resumeLoggerDefer;
316-
if (options.isInteractive) {
317-
logger->pause();
318-
resumeLoggerDefer.emplace([]() { logger->resume(); });
319-
}
315+
auto suspension = logger->suspendIf(options.isInteractive);
320316

321317
Pid pid = spawnProcess(interpreter.has_value() ? *interpreter : realProgram, options, out, in);
322318

0 commit comments

Comments
 (0)