diff options
Diffstat (limited to 'src/libmain/progress-bar.cc')
-rw-r--r-- | src/libmain/progress-bar.cc | 178 |
1 files changed, 123 insertions, 55 deletions
diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index b3466a860..28bb14863 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -95,8 +95,7 @@ void ProgressBar::logEI(const ErrorInfo & ei) void ProgressBar::log(State & state, Verbosity lvl, std::string_view s) { if (state.active) { - writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); - draw(state); + draw(state, s); } else { auto s2 = s + ANSI_NORMAL "\n"; if (!isTTY) s2 = filterANSIEscapes(s2, true); @@ -234,10 +233,14 @@ void ProgressBar::result(ActivityId act, ResultType type, const std::vector<Fiel } log(*state, lvlInfo, ANSI_FAINT + info.name.value_or("unnamed") + suffix + ANSI_NORMAL + lastLine); } else { - state->activities.erase(i->second); - info.lastLine = lastLine; - state->activities.emplace_back(info); - i->second = std::prev(state->activities.end()); + if (!printMultiline) { + state->activities.erase(i->second); + info.lastLine = lastLine; + state->activities.emplace_back(info); + i->second = std::prev(state->activities.end()); + } else { + i->second->lastLine = lastLine; + } update(*state); } } @@ -290,60 +293,93 @@ void ProgressBar::update(State & state) updateCV.notify_one(); } -std::chrono::milliseconds ProgressBar::draw(State & state) +std::chrono::milliseconds ProgressBar::draw(State & state, const std::optional<std::string_view> & s) { auto nextWakeup = A_LONG_TIME; state.haveUpdate = false; if (state.paused || !state.active) return nextWakeup; - std::string line; + auto windowSize = getWindowSize(); + auto width = windowSize.second; + if (width <= 0) { + width = std::numeric_limits<decltype(width)>::max(); + } + + if (printMultiline && (state.lastLines >= 1)) { + // FIXME: make sure this works on windows + writeToStderr(fmt("\e[G\e[%dF\e[J", state.lastLines)); + } + + state.lastLines = 0; + if (s != std::nullopt) + writeToStderr("\r\e[K" + filterANSIEscapes(s.value(), !isTTY) + ANSI_NORMAL "\n"); + + std::string line; std::string status = getStatus(state); if (!status.empty()) { line += '['; line += status; line += "]"; } + if (printMultiline && !line.empty()) { + writeToStderr(filterANSIEscapes(line, false, width) + "\n"); + state.lastLines++; + } + auto height = windowSize.first > 0 ? windowSize.first : 25; + auto moreActivities = 0; auto now = std::chrono::steady_clock::now(); + std::string activity_line; if (!state.activities.empty()) { - if (!status.empty()) line += " "; - auto i = state.activities.rbegin(); - - while (i != state.activities.rend()) { - if (i->visible && (!i->s.empty() || !i->lastLine.empty())) { - /* Don't show activities until some time has - passed, to avoid displaying very short - activities. */ - auto delay = std::chrono::milliseconds(10); - if (i->startTime + delay < now) - break; - else - nextWakeup = std::min(nextWakeup, std::chrono::duration_cast<std::chrono::milliseconds>(delay - (now - i->startTime))); + for (auto i = state.activities.begin(); i != state.activities.end(); ++i) { + if (!(i->visible && (!i->s.empty() || !i->lastLine.empty()))) { + continue; } - ++i; - } + /* Don't show activities until some time has + passed, to avoid displaying very short + activities. */ + auto delay = std::chrono::milliseconds(10); + if (i->startTime + delay >= now) { + nextWakeup = std::min( + nextWakeup, + std::chrono::duration_cast<std::chrono::milliseconds>( + delay - (now - i->startTime) + ) + ); + } + + activity_line = i->s; - if (i != state.activities.rend()) { - line += i->s; if (!i->phase.empty()) { - line += " ("; - line += i->phase; - line += ")"; + activity_line += " ("; + activity_line += i->phase; + activity_line += ")"; } if (!i->lastLine.empty()) { - if (!i->s.empty()) line += ": "; - line += i->lastLine; + if (!i->s.empty()) + activity_line += ": "; + activity_line += i->lastLine; + } + + if (printMultiline) { + if (state.lastLines < (height -1)) { + writeToStderr(filterANSIEscapes(activity_line, false, width) + "\n"); + state.lastLines++; + } else moreActivities++; } } } - auto width = getWindowSize().second; - if (width <= 0) width = std::numeric_limits<decltype(width)>::max(); + if (printMultiline && moreActivities) + writeToStderr(fmt("And %d more...", moreActivities)); - writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + if (!printMultiline) { + line += " " + activity_line; + writeToStderr("\r" + filterANSIEscapes(line, false, width) + ANSI_NORMAL + "\e[K"); + } return nextWakeup; } @@ -366,31 +402,59 @@ std::string ProgressBar::getStatus(State & state) expected = std::max(expected, act.expected); - std::string s; + std::string rendered; if (running || done || expected || failed) { - if (running) - if (expected != 0) - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - running / unit, done / unit, expected / unit); - else - s = fmt(ANSI_BLUE + numberFmt + ANSI_NORMAL "/" ANSI_GREEN + numberFmt + ANSI_NORMAL, - running / unit, done / unit); - else if (expected != done) - if (expected != 0) - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL "/" + numberFmt, - done / unit, expected / unit); - else - s = fmt(ANSI_GREEN + numberFmt + ANSI_NORMAL, done / unit); - else - s = fmt(done ? ANSI_GREEN + numberFmt + ANSI_NORMAL : numberFmt, done / unit); - s = fmt(itemFmt, s); + if (running) { + if (expected != 0) { + auto const runningPart = fmt(numberFmt, running / unit); + auto const donePart = fmt(numberFmt, done / unit); + auto const expectedPart = fmt(numberFmt, expected / unit); + rendered = fmt( + ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL "/%s", + runningPart, + donePart, + expectedPart + ); + } else { + auto const runningPart = fmt(numberFmt, running / unit); + auto const donePart = fmt(numberFmt, done / unit); + rendered = fmt( + ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL, + runningPart, + donePart + ); + } + } else if (expected != done) { + if (expected != 0) { + auto const donePart = fmt(numberFmt, done / unit); + auto const expectedPart = fmt(numberFmt, expected / unit); + rendered = fmt( + ANSI_GREEN "%s" ANSI_NORMAL "/%s", + donePart, + expectedPart + ); + } else { + auto const donePart = fmt(numberFmt, done / unit); + rendered = fmt(ANSI_GREEN "%s" ANSI_NORMAL, donePart); + } + } else { + auto const donePart = fmt(numberFmt, done / unit); + + // We only color if `done` is non-zero. + if (done) { + rendered = concatStrings(ANSI_GREEN, donePart, ANSI_NORMAL); + } else { + rendered = donePart; + } + } + rendered = fmt(itemFmt, rendered); if (failed) - s += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); + rendered += fmt(" (" ANSI_RED "%d failed" ANSI_NORMAL ")", failed / unit); } - return s; + return rendered; }; auto showActivity = [&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) { @@ -442,9 +506,8 @@ void ProgressBar::writeToStdout(std::string_view s) { auto state(state_.lock()); if (state->active) { - std::cerr << "\r\e[K"; Logger::writeToStdout(s); - draw(*state); + draw(*state, {}); } else { Logger::writeToStdout(s); } @@ -457,7 +520,7 @@ std::optional<char> ProgressBar::ask(std::string_view msg) std::cerr << fmt("\r\e[K%s ", msg); auto s = trim(readLine(STDIN_FILENO)); if (s.size() != 1) return {}; - draw(*state); + draw(*state, {}); return s[0]; } @@ -466,6 +529,11 @@ void ProgressBar::setPrintBuildLogs(bool printBuildLogs) this->printBuildLogs = printBuildLogs; } +void ProgressBar::setPrintMultiline(bool printMultiline) +{ + this->printMultiline = printMultiline; +} + Logger * makeProgressBar() { return new ProgressBar(shouldANSI()); |