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:
parent
2a2df85fbd
commit
304715d5f3
1 changed files with 66 additions and 24 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue