util.{hh,cc}: Split out terminal.{hh,cc}

Change-Id: I9de2296b4012d50f540124001d54d6ca3be4c6da
This commit is contained in:
Tom Hubrecht 2024-05-28 11:19:08 +02:00
parent 6fd6795bc4
commit 81bdf8d2d6
14 changed files with 157 additions and 122 deletions

View file

@ -1,6 +1,7 @@
#include "markdown.hh" #include "markdown.hh"
#include "util.hh" #include "util.hh"
#include "finally.hh" #include "finally.hh"
#include "terminal.hh"
#include <sys/queue.h> #include <sys/queue.h>
#include <lowdown.h> #include <lowdown.h>

View file

@ -8,6 +8,7 @@
#include "fetchers.hh" #include "fetchers.hh"
#include "finally.hh" #include "finally.hh"
#include "fetch-settings.hh" #include "fetch-settings.hh"
#include "terminal.hh"
namespace nix { namespace nix {

View file

@ -9,6 +9,7 @@
#include "english.hh" #include "english.hh"
#include "signals.hh" #include "signals.hh"
#include "eval.hh" #include "eval.hh"
#include "terminal.hh"
namespace nix { namespace nix {

View file

@ -3,6 +3,7 @@
#include "sync.hh" #include "sync.hh"
#include "store-api.hh" #include "store-api.hh"
#include "names.hh" #include "names.hh"
#include "terminal.hh"
#include <atomic> #include <atomic>
#include <map> #include <map>

View file

@ -1,6 +1,7 @@
#include "environment-variables.hh" #include "environment-variables.hh"
#include "error.hh" #include "error.hh"
#include "position.hh" #include "position.hh"
#include "terminal.hh"
#include <iostream> #include <iostream>
#include <optional> #include <optional>

View file

@ -4,6 +4,7 @@
#include "config.hh" #include "config.hh"
#include "source-path.hh" #include "source-path.hh"
#include "position.hh" #include "position.hh"
#include "terminal.hh"
#include <atomic> #include <atomic>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>

View file

@ -30,6 +30,7 @@ libutil_sources = files(
'source-path.cc', 'source-path.cc',
'suggestions.cc', 'suggestions.cc',
'tarfile.cc', 'tarfile.cc',
'terminal.cc',
'thread-pool.cc', 'thread-pool.cc',
'url.cc', 'url.cc',
'url-name.cc', 'url-name.cc',
@ -90,6 +91,7 @@ libutil_headers = files(
'suggestions.hh', 'suggestions.hh',
'sync.hh', 'sync.hh',
'tarfile.hh', 'tarfile.hh',
'terminal.hh',
'thread-pool.hh', 'thread-pool.hh',
'topo-sort.hh', 'topo-sort.hh',
'types.hh', 'types.hh',

View file

@ -2,6 +2,7 @@
#include "util.hh" #include "util.hh"
#include "error.hh" #include "error.hh"
#include "sync.hh" #include "sync.hh"
#include "terminal.hh"
#include <thread> #include <thread>

View file

@ -1,7 +1,9 @@
#include "suggestions.hh" #include "suggestions.hh"
#include "ansicolor.hh" #include "ansicolor.hh"
#include "util.hh" #include "terminal.hh"
#include <algorithm> #include <algorithm>
#include <ostream>
namespace nix { namespace nix {

104
src/libutil/terminal.cc Normal file
View file

@ -0,0 +1,104 @@
#include "terminal.hh"
#include "environment-variables.hh"
#include "sync.hh"
#include <sys/ioctl.h>
#include <unistd.h>
namespace nix {
bool shouldANSI()
{
return isatty(STDERR_FILENO)
&& getEnv("TERM").value_or("dumb") != "dumb"
&& !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value());
}
std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width)
{
std::string t, e;
size_t w = 0;
auto i = s.begin();
while (w < (size_t) width && i != s.end()) {
if (*i == '\e') {
std::string e;
e += *i++;
char last = 0;
if (i != s.end() && *i == '[') {
e += *i++;
// eat parameter bytes
while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++;
// 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++;
}
if (!filterAll && last == 'm')
t += e;
}
else if (*i == '\t') {
i++; t += ' '; w++;
while (w < (size_t) width && w % 8) {
t += ' '; w++;
}
}
else if (*i == '\r' || *i == '\a')
// do nothing for now
i++;
else {
w++;
// Copy one UTF-8 character.
if ((*i & 0xe0) == 0xc0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
} else if ((*i & 0xf0) == 0xe0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
}
} else if ((*i & 0xf8) == 0xf0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
}
}
} else
t += *i++;
}
}
return t;
}
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
void updateWindowSize()
{
struct winsize ws;
if (ioctl(2, TIOCGWINSZ, &ws) == 0) {
auto windowSize_(windowSize.lock());
windowSize_->first = ws.ws_row;
windowSize_->second = ws.ws_col;
}
}
std::pair<unsigned short, unsigned short> getWindowSize()
{
return *windowSize.lock();
}
}

40
src/libutil/terminal.hh Normal file
View file

@ -0,0 +1,40 @@
#pragma once
///@file
#include <limits>
#include <string>
namespace nix {
/**
* Determine whether ANSI escape sequences are appropriate for the
* present output.
*/
bool shouldANSI();
/**
* Truncate a string to 'width' printable characters. If 'filterAll'
* is true, all ANSI escape sequences are filtered out. Otherwise,
* some escape sequences (such as colour setting) are copied but not
* included in the character count. Also, tabs are expanded to
* spaces.
*/
std::string filterANSIEscapes(std::string_view s,
bool filterAll = false,
unsigned int width = std::numeric_limits<unsigned int>::max());
/**
* Recalculate the window size, updating a global variable. Used in the
* `SIGWINCH` signal handler.
*/
void updateWindowSize();
/**
* @return the number of rows and columns of the terminal.
*
* The value is cached so this is quick. The cached result is computed
* by `updateWindowSize()`.
*/
std::pair<unsigned short, unsigned short> getWindowSize();
}

View file

@ -1436,83 +1436,6 @@ void ignoreException(Verbosity lvl)
} catch (...) { } } catch (...) { }
} }
bool shouldANSI()
{
return isatty(STDERR_FILENO)
&& getEnv("TERM").value_or("dumb") != "dumb"
&& !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value());
}
std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width)
{
std::string t, e;
size_t w = 0;
auto i = s.begin();
while (w < (size_t) width && i != s.end()) {
if (*i == '\e') {
std::string e;
e += *i++;
char last = 0;
if (i != s.end() && *i == '[') {
e += *i++;
// eat parameter bytes
while (i != s.end() && *i >= 0x30 && *i <= 0x3f) e += *i++;
// 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++;
}
if (!filterAll && last == 'm')
t += e;
}
else if (*i == '\t') {
i++; t += ' '; w++;
while (w < (size_t) width && w % 8) {
t += ' '; w++;
}
}
else if (*i == '\r' || *i == '\a')
// do nothing for now
i++;
else {
w++;
// Copy one UTF-8 character.
if ((*i & 0xe0) == 0xc0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
} else if ((*i & 0xf0) == 0xe0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
}
} else if ((*i & 0xf8) == 0xf0) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) {
t += *i++;
if (i != s.end() && ((*i & 0xc0) == 0x80)) t += *i++;
}
}
} else
t += *i++;
}
}
return t;
}
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string base64Encode(std::string_view s) std::string base64Encode(std::string_view s)
@ -1630,25 +1553,6 @@ std::pair<std::string_view, std::string_view> getLine(std::string_view s)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
void updateWindowSize()
{
struct winsize ws;
if (ioctl(2, TIOCGWINSZ, &ws) == 0) {
auto windowSize_(windowSize.lock());
windowSize_->first = ws.ws_row;
windowSize_->second = ws.ws_col;
}
}
std::pair<unsigned short, unsigned short> getWindowSize()
{
return *windowSize.lock();
}
rlim_t savedStackSize = 0; rlim_t savedStackSize = 0;

View file

@ -726,23 +726,6 @@ constexpr char treeLast[] = "└───";
constexpr char treeLine[] = ""; constexpr char treeLine[] = "";
constexpr char treeNull[] = " "; constexpr char treeNull[] = " ";
/**
* Determine whether ANSI escape sequences are appropriate for the
* present output.
*/
bool shouldANSI();
/**
* Truncate a string to 'width' printable characters. If 'filterAll'
* is true, all ANSI escape sequences are filtered out. Otherwise,
* some escape sequences (such as colour setting) are copied but not
* included in the character count. Also, tabs are expanded to
* spaces.
*/
std::string filterANSIEscapes(std::string_view s,
bool filterAll = false,
unsigned int width = std::numeric_limits<unsigned int>::max());
/** /**
* Base64 encoding/decoding. * Base64 encoding/decoding.
@ -840,14 +823,6 @@ struct MaintainCount
}; };
/**
* @return the number of rows and columns of the terminal.
*/
std::pair<unsigned short, unsigned short> getWindowSize();
void updateWindowSize();
/** /**
* Used in various places. * Used in various places.
*/ */

View file

@ -1,5 +1,6 @@
#include "util.hh" #include "util.hh"
#include "types.hh" #include "types.hh"
#include "terminal.hh"
#include <limits.h> #include <limits.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>