forked from lix-project/lix
Improve filtering of ANSI escape sequences in build logs
All ANSI sequences except color setting are now filtered out. In particular, terminal resets (such as from NixOS VM tests) are filtered out. Also, fix the completely broken tab character handling.
This commit is contained in:
parent
cfdfad5c34
commit
84989d3af2
|
@ -3428,7 +3428,7 @@ void DerivationGoal::flushLine()
|
||||||
else {
|
else {
|
||||||
if (settings.verboseBuild &&
|
if (settings.verboseBuild &&
|
||||||
(settings.printRepeatedBuilds || curRound == 1))
|
(settings.printRepeatedBuilds || curRound == 1))
|
||||||
printError(filterANSIEscapes(currentLogLine, true));
|
printError(currentLogLine);
|
||||||
else {
|
else {
|
||||||
logTail.push_back(currentLogLine);
|
logTail.push_back(currentLogLine);
|
||||||
if (logTail.size() > settings.logLines) logTail.pop_front();
|
if (logTail.size() > settings.logLines) logTail.pop_front();
|
||||||
|
|
|
@ -44,7 +44,7 @@ public:
|
||||||
prefix = std::string("<") + c + ">";
|
prefix = std::string("<") + c + ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToStderr(prefix + (tty ? fs.s : filterANSIEscapes(fs.s)) + "\n");
|
writeToStderr(prefix + filterANSIEscapes(fs.s) + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
||||||
|
|
|
@ -1178,36 +1178,51 @@ void ignoreException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string filterANSIEscapes(const string & s, bool nixOnly)
|
std::string filterANSIEscapes(const std::string & s, unsigned int width)
|
||||||
{
|
{
|
||||||
string t, r;
|
std::string t, e;
|
||||||
enum { stTop, stEscape, stCSI } state = stTop;
|
size_t w = 0;
|
||||||
for (auto c : s) {
|
auto i = s.begin();
|
||||||
if (state == stTop) {
|
|
||||||
if (c == '\e') {
|
while (w < (size_t) width && i != s.end()) {
|
||||||
state = stEscape;
|
|
||||||
r = c;
|
if (*i == '\e') {
|
||||||
} else
|
std::string e;
|
||||||
t += c;
|
e += *i++;
|
||||||
} else if (state == stEscape) {
|
char last = 0;
|
||||||
r += c;
|
|
||||||
if (c == '[')
|
if (i != s.end() && *i == '[') {
|
||||||
state = stCSI;
|
e += *i++;
|
||||||
else {
|
// eat parameter bytes
|
||||||
t += r;
|
while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++;
|
||||||
state = stTop;
|
// eat intermediate bytes
|
||||||
|
while (i != s.end() && *i >= 0x20 && *i <= 0x2f) e += *i++;
|
||||||
|
// eat final byte
|
||||||
|
if (i != s.end() && *i >= 0x40 && *i <= 0x7e) e += last = *i++;
|
||||||
|
} else {
|
||||||
|
if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
r += c;
|
if (last == 'm')
|
||||||
if (c >= 0x40 && c <= 0x7e) {
|
t += e;
|
||||||
if (nixOnly && (c != 'p' && c != 'q' && c != 's' && c != 'a' && c != 'b'))
|
}
|
||||||
t += r;
|
|
||||||
state = stTop;
|
else if (*i == '\t') {
|
||||||
r.clear();
|
i++; t += ' '; w++;
|
||||||
|
while (w < (size_t) width && w % 8) {
|
||||||
|
t += ' '; w++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (*i == '\r')
|
||||||
|
// do nothing for now
|
||||||
|
;
|
||||||
|
|
||||||
|
else {
|
||||||
|
t += *i++; w++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t += r;
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -388,10 +388,12 @@ void ignoreException();
|
||||||
#define ANSI_BLUE "\e[34;1m"
|
#define ANSI_BLUE "\e[34;1m"
|
||||||
|
|
||||||
|
|
||||||
/* Filter out ANSI escape codes from the given string. If ‘nixOnly’ is
|
/* Truncate a string to 'width' printable characters. Certain ANSI
|
||||||
set, only filter escape codes generated by Nixpkgs' stdenv (used to
|
escape sequences (such as colour setting) are copied but not
|
||||||
denote nesting etc.). */
|
included in the character count. Other ANSI escape sequences are
|
||||||
string filterANSIEscapes(const string & s, bool nixOnly = false);
|
filtered. Also, tabs are expanded to spaces. */
|
||||||
|
std::string filterANSIEscapes(const std::string & s,
|
||||||
|
unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||||
|
|
||||||
|
|
||||||
/* Base64 encoding/decoding. */
|
/* Base64 encoding/decoding. */
|
||||||
|
|
|
@ -23,44 +23,6 @@ static uint64_t getI(const std::vector<Logger::Field> & fields, size_t n)
|
||||||
return fields[n].i;
|
return fields[n].i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Truncate a string to 'width' printable characters. ANSI escape
|
|
||||||
sequences are copied but not included in the character count. Also,
|
|
||||||
tabs are expanded to spaces. */
|
|
||||||
static std::string ansiTruncate(const std::string & s, int width)
|
|
||||||
{
|
|
||||||
if (width <= 0) return s;
|
|
||||||
|
|
||||||
std::string t;
|
|
||||||
size_t w = 0;
|
|
||||||
auto i = s.begin();
|
|
||||||
|
|
||||||
while (w < (size_t) width && i != s.end()) {
|
|
||||||
if (*i == '\e') {
|
|
||||||
t += *i++;
|
|
||||||
if (i != s.end() && *i == '[') {
|
|
||||||
t += *i++;
|
|
||||||
while (i != s.end() && (*i < 0x40 || *i > 0x7e)) {
|
|
||||||
t += *i++;
|
|
||||||
}
|
|
||||||
if (i != s.end()) t += *i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (*i == '\t') {
|
|
||||||
t += ' '; w++;
|
|
||||||
while (w < (size_t) width && w & 8) {
|
|
||||||
t += ' '; w++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
t += *i++; w++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProgressBar : public Logger
|
class ProgressBar : public Logger
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -343,7 +305,10 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToStderr("\r" + ansiTruncate(line, getWindowSize().second) + "\e[K");
|
auto width = getWindowSize().second;
|
||||||
|
if (width <= 0) std::numeric_limits<decltype(width)>::max();
|
||||||
|
|
||||||
|
writeToStderr("\r" + filterANSIEscapes(line, width) + "\e[K");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getStatus(State & state)
|
std::string getStatus(State & state)
|
||||||
|
|
|
@ -16,4 +16,4 @@ nix-env --foo 2>&1 | grep "no operation"
|
||||||
nix-env -q --foo 2>&1 | grep "unknown flag"
|
nix-env -q --foo 2>&1 | grep "unknown flag"
|
||||||
|
|
||||||
# Eval Errors.
|
# Eval Errors.
|
||||||
nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 | grep "infinite recursion encountered, at (string):1:15$"
|
nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 | grep "infinite recursion encountered, at .*(string).*:1:15$"
|
||||||
|
|
Loading…
Reference in a new issue