1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-21 09:49:36 +01:00

Support multi-line status

This commit is contained in:
Eelco Dolstra 2020-11-10 15:51:56 +01:00
parent 2a2df85fbd
commit 304715d5f3

View file

@ -61,6 +61,8 @@ private:
uint64_t failed = 0; uint64_t failed = 0;
}; };
typedef unsigned int LineId;
struct State struct State
{ {
std::list<ActInfo> activities; std::list<ActInfo> activities;
@ -76,6 +78,11 @@ private:
bool haveUpdate = true; bool haveUpdate = true;
bool printBuildLogs; bool printBuildLogs;
std::map<LineId, std::string> statusLines;
/* How many lines need to be erased when redrawing. */
size_t prevStatusLines = 0;
}; };
bool isTTY; bool isTTY;
@ -104,6 +111,7 @@ public:
while (state->active) { while (state->active) {
if (!state->haveUpdate) if (!state->haveUpdate)
state.wait(updateCV); state.wait(updateCV);
updateStatusLine(*state);
draw(*state); draw(*state);
state.wait_for(quitCV, std::chrono::milliseconds(50)); state.wait_for(quitCV, std::chrono::milliseconds(50));
} }
@ -116,6 +124,8 @@ public:
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) { if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO)) {
static auto helpLine = ANSI_BOLD "Type 'h' for help.";
struct termios term; struct termios term;
if (tcgetattr(STDIN_FILENO, &term)) if (tcgetattr(STDIN_FILENO, &term))
throw SysError("getting terminal attributes"); throw SysError("getting terminal attributes");
@ -161,12 +171,12 @@ public:
} }
if (c == 'l') { if (c == 'l') {
auto state(state_.lock()); auto state(state_.lock());
log(*state, lvlError,
state->printBuildLogs
? ANSI_BOLD "Disabling build logs."
: ANSI_BOLD "Enabling build logs.");
state->printBuildLogs = !state->printBuildLogs; state->printBuildLogs = !state->printBuildLogs;
draw(*state); updateStatusLine(*state);
draw(*state,
state->printBuildLogs
? ANSI_BOLD "Enabling build logs."
: ANSI_BOLD "Disabling build logs.");
} }
if (c == '+' || c == '=' || c == 'v') { if (c == '+' || c == '=' || c == 'v') {
auto state(state_.lock()); auto state(state_.lock());
@ -179,17 +189,24 @@ public:
log(*state, lvlError, ANSI_BOLD "Decreasing verbosity..."); log(*state, lvlError, ANSI_BOLD "Decreasing verbosity...");
} }
if (c == 'h' || c == '?') { if (c == 'h' || c == '?') {
printError( auto state(state_.lock());
ANSI_BOLD "The following keys are available:\n" if (state->statusLines.count(1)) {
" 'v' to increase verbosity.\n" for (LineId i = 0; i <= 4; i++)
" '-' to decrease verbosity.\n" state->statusLines.erase(i);
" 'l' to show build log output.\n" state->statusLines.insert_or_assign(0, helpLine);
" 'q' to quit." ANSI_NORMAL); } else {
state->statusLines.insert_or_assign(0, ANSI_BOLD "The following keys are available:");
state->statusLines.insert_or_assign(1, ANSI_BOLD " 'v' to increase verbosity.");
state->statusLines.insert_or_assign(2, ANSI_BOLD " '-' to decrease verbosity.");
state->statusLines.insert_or_assign(3, ANSI_BOLD " 'l' to show build log output.");
state->statusLines.insert_or_assign(4, ANSI_BOLD " 'q' to quit.");
}
draw(*state);
} }
} }
}); });
log(lvlError, "Type 'h' for help."); state_.lock()->statusLines.insert_or_assign(0, helpLine);
} }
} }
@ -209,8 +226,9 @@ public:
{ {
auto state(state_.lock()); auto state(state_.lock());
if (!state->active) return; if (!state->active) return;
state->statusLines.clear();
draw(*state);
state->active = false; state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one(); updateCV.notify_one();
quitCV.notify_one(); quitCV.notify_one();
} }
@ -242,8 +260,7 @@ public:
void log(State & state, Verbosity lvl, const std::string & s) void log(State & state, Verbosity lvl, const std::string & s)
{ {
if (state.active) { if (state.active) {
writeToStderr("\r\e[K" + replaceStrings(filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n", "\n", "\r\n")); draw(state, filterANSIEscapes(s, !isTTY));
draw(state);
} else { } else {
auto s2 = s + ANSI_NORMAL "\n"; auto s2 = s + ANSI_NORMAL "\n";
if (!isTTY) s2 = filterANSIEscapes(s2, true); if (!isTTY) s2 = filterANSIEscapes(s2, true);
@ -375,7 +392,7 @@ public:
info.lastLine = lastLine; info.lastLine = lastLine;
state->activities.emplace_back(info); state->activities.emplace_back(info);
i->second = std::prev(state->activities.end()); i->second = std::prev(state->activities.end());
if (state->printBuildLogs) if (!state->printBuildLogs)
update(*state); update(*state);
} }
} }
@ -427,11 +444,8 @@ public:
updateCV.notify_one(); updateCV.notify_one();
} }
void draw(State & state) void updateStatusLine(State & state)
{ {
state.haveUpdate = false;
if (!state.active) return;
std::string line; std::string line;
std::string status = getStatus(state); std::string status = getStatus(state);
@ -462,10 +476,40 @@ public:
} }
} }
if (line.empty())
state.statusLines.erase(100);
else
state.statusLines.insert_or_assign(100, line);
}
void draw(State & state, std::optional<std::string_view> msg = {})
{
state.haveUpdate = false;
if (!state.active) return;
auto width = getWindowSize().second; auto width = getWindowSize().second;
if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); if (width <= 0) width = std::numeric_limits<decltype(width)>::max();
writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); std::string s;
for (size_t i = 1; i < state.prevStatusLines; ++i)
s += "\r\e[K\e[A";
s += "\r";
if (msg) {
s += replaceStrings(*msg, "\n", "\r\n");
s += ANSI_NORMAL "\e[K\n\r";
}
for (const auto & [n, i] : enumerate(state.statusLines)) {
s += filterANSIEscapes(i.second, false, width) + ANSI_NORMAL + "\e[K";
if (n + 1 < state.statusLines.size()) s += "\r\n";
}
writeToStderr(s);
state.prevStatusLines = state.statusLines.size();
} }
std::string getStatus(State & state) std::string getStatus(State & state)
@ -562,9 +606,7 @@ public:
{ {
auto state(state_.lock()); auto state(state_.lock());
if (state->active) { if (state->active) {
std::cerr << "\r\e[K"; draw(*state, s);
Logger::writeToStdout(s);
draw(*state);
} else { } else {
Logger::writeToStdout(s); Logger::writeToStdout(s);
} }