mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 05:13:10 +00:00
Factor color and terminal sequence outputting into outputter_t
Removes some static variables and simplifies the behavior of the tputs singletone receiver.
This commit is contained in:
parent
9715db9434
commit
5134949a14
8 changed files with 268 additions and 276 deletions
|
@ -42,14 +42,6 @@ static void print_colors(io_streams_t &streams) {
|
|||
}
|
||||
}
|
||||
|
||||
static std::string builtin_set_color_output;
|
||||
/// Function we set as the output writer.
|
||||
static int set_color_builtin_outputter(char c) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
builtin_set_color_output.push_back(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const wchar_t *const short_options = L":b:hvoidrcu";
|
||||
static const struct woption long_options[] = {{L"background", required_argument, NULL, 'b'},
|
||||
{L"help", no_argument, NULL, 'h'},
|
||||
|
@ -185,62 +177,53 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|||
if (cur_term == NULL || !exit_attribute_mode) {
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
|
||||
// Save old output function so we can restore it.
|
||||
int (*const saved_writer_func)(char) = output_get_writer();
|
||||
|
||||
// Set our output function, which writes to a std::string.
|
||||
builtin_set_color_output.clear();
|
||||
output_set_writer(set_color_builtin_outputter);
|
||||
outputter_t outp;
|
||||
|
||||
if (bold && enter_bold_mode) {
|
||||
writembs_nofail(tparm((char *)enter_bold_mode));
|
||||
writembs_nofail(outp, tparm((char *)enter_bold_mode));
|
||||
}
|
||||
|
||||
if (underline && enter_underline_mode) {
|
||||
writembs_nofail(enter_underline_mode);
|
||||
writembs_nofail(outp, enter_underline_mode);
|
||||
}
|
||||
|
||||
if (italics && enter_italics_mode) {
|
||||
writembs_nofail(enter_italics_mode);
|
||||
writembs_nofail(outp, enter_italics_mode);
|
||||
}
|
||||
|
||||
if (dim && enter_dim_mode) {
|
||||
writembs_nofail(enter_dim_mode);
|
||||
writembs_nofail(outp, enter_dim_mode);
|
||||
}
|
||||
|
||||
if (reverse && enter_reverse_mode) {
|
||||
writembs_nofail(enter_reverse_mode);
|
||||
writembs_nofail(outp, enter_reverse_mode);
|
||||
} else if (reverse && enter_standout_mode) {
|
||||
writembs_nofail(enter_standout_mode);
|
||||
writembs_nofail(outp, enter_standout_mode);
|
||||
}
|
||||
|
||||
if (bgcolor != NULL && bg.is_normal()) {
|
||||
writembs_nofail(tparm((char *)exit_attribute_mode));
|
||||
writembs_nofail(outp, tparm((char *)exit_attribute_mode));
|
||||
}
|
||||
|
||||
if (!fg.is_none()) {
|
||||
if (fg.is_normal() || fg.is_reset()) {
|
||||
writembs_nofail(tparm((char *)exit_attribute_mode));
|
||||
} else if (!write_color(fg, true /* is_fg */)) {
|
||||
// We need to do *something* or the lack of any output
|
||||
// with a cartesian product here would make "foo" disappear
|
||||
// on lame terminals:
|
||||
// $ env TERM=vt100 fish -c 'echo (set_color red)"foo"'
|
||||
set_color(rgb_color_t::reset(), rgb_color_t::none());
|
||||
writembs_nofail(outp, tparm((char *)exit_attribute_mode));
|
||||
} else {
|
||||
if (!outp.write_color(fg, true /* is_fg */)) {
|
||||
// We need to do *something* or the lack of any output messes up
|
||||
// when the cartesian product here would make "foo" disappear:
|
||||
// $ echo (set_color foo)bar
|
||||
outp.set_color(rgb_color_t::reset(), rgb_color_t::none());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bgcolor != NULL && !bg.is_normal() && !bg.is_reset()) {
|
||||
write_color(bg, false /* not is_fg */);
|
||||
outp.write_color(bg, false /* not is_fg */);
|
||||
}
|
||||
|
||||
// Restore saved writer function.
|
||||
output_set_writer(saved_writer_func);
|
||||
|
||||
// Output the collected string.
|
||||
streams.out.append(str2wcstring(builtin_set_color_output));
|
||||
builtin_set_color_output.clear();
|
||||
streams.out.append(str2wcstring(outp.contents()));
|
||||
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
|
|
@ -252,37 +252,25 @@ static wcstring prettify(const wcstring &src, bool do_indent) {
|
|||
return std::move(prettifier.output);
|
||||
}
|
||||
|
||||
// Helper for output_set_writer
|
||||
static std::string output_receiver;
|
||||
static int write_to_output_receiver(char c) {
|
||||
output_receiver.push_back(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Given a string and list of colors of the same size, return the string with ANSI escape sequences
|
||||
/// representing the colors.
|
||||
static std::string ansi_colorize(const wcstring &text,
|
||||
const std::vector<highlight_spec_t> &colors) {
|
||||
assert(colors.size() == text.size());
|
||||
assert(output_receiver.empty());
|
||||
|
||||
int (*saved)(char) = output_get_writer();
|
||||
output_set_writer(write_to_output_receiver);
|
||||
outputter_t outp;
|
||||
const auto &vars = env_stack_t::globals();
|
||||
|
||||
highlight_spec_t last_color = highlight_spec_normal;
|
||||
for (size_t i = 0; i < text.size(); i++) {
|
||||
highlight_spec_t color = colors.at(i);
|
||||
if (color != last_color) {
|
||||
set_color(highlight_get_color(color, false), rgb_color_t::normal());
|
||||
outp.set_color(highlight_get_color(color, false), rgb_color_t::normal());
|
||||
last_color = color;
|
||||
}
|
||||
writech(text.at(i));
|
||||
outp.writech(text.at(i));
|
||||
}
|
||||
set_color(rgb_color_t::normal(), rgb_color_t::normal());
|
||||
output_set_writer(saved);
|
||||
std::string result;
|
||||
result.swap(output_receiver);
|
||||
return result;
|
||||
outp.set_color(rgb_color_t::normal(), rgb_color_t::normal());
|
||||
return outp.contents();
|
||||
}
|
||||
|
||||
/// Given a string and list of colors of the same size, return the string with HTML span elements
|
||||
|
|
182
src/output.cpp
182
src/output.cpp
|
@ -30,25 +30,9 @@
|
|||
#include "output.h"
|
||||
#include "wutil.h" // IWYU pragma: keep
|
||||
|
||||
static int writeb_internal(char c);
|
||||
|
||||
/// The function used for output.
|
||||
static int (*out)(char c) = writeb_internal; //!OCLINT(unused param)
|
||||
|
||||
/// Whether term256 and term24bit are supported.
|
||||
static color_support_t color_support = 0;
|
||||
|
||||
/// Set the function used for writing in move_cursor, writespace and set_color and all other output
|
||||
/// functions in this library. By default, the write call is used to give completely unbuffered
|
||||
/// output to stdout.
|
||||
void output_set_writer(int (*writer)(char)) {
|
||||
CHECK(writer, );
|
||||
out = writer;
|
||||
}
|
||||
|
||||
/// Return the current output writer.
|
||||
int (*output_get_writer())(char) { return out; }
|
||||
|
||||
/// Returns true if we think tparm can handle outputting a color index
|
||||
static bool term_supports_color_natively(unsigned int c) { return (unsigned)max_colors >= c + 1; }
|
||||
|
||||
|
@ -63,10 +47,10 @@ unsigned char index_for_color(rgb_color_t c) {
|
|||
return c.to_term256_index();
|
||||
}
|
||||
|
||||
static bool write_color_escape(const char *todo, unsigned char idx, bool is_fg) {
|
||||
static bool write_color_escape(outputter_t &outp, const char *todo, unsigned char idx, bool is_fg) {
|
||||
if (term_supports_color_natively(idx)) {
|
||||
// Use tparm to emit color escape.
|
||||
writembs(tparm((char *)todo, idx));
|
||||
writembs(outp, tparm((char *)todo, idx));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -85,45 +69,46 @@ static bool write_color_escape(const char *todo, unsigned char idx, bool is_fg)
|
|||
snprintf(buff, sizeof buff, "\x1B[%d;5;%dm", is_fg ? 38 : 48, idx);
|
||||
}
|
||||
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
outp.writestr(buff);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write_foreground_color(unsigned char idx) {
|
||||
static bool write_foreground_color(outputter_t &outp, unsigned char idx) {
|
||||
if (!cur_term) return false;
|
||||
if (set_a_foreground && set_a_foreground[0]) {
|
||||
return write_color_escape(set_a_foreground, idx, true);
|
||||
return write_color_escape(outp, set_a_foreground, idx, true);
|
||||
} else if (set_foreground && set_foreground[0]) {
|
||||
return write_color_escape(set_foreground, idx, true);
|
||||
return write_color_escape(outp, set_foreground, idx, true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool write_background_color(unsigned char idx) {
|
||||
static bool write_background_color(outputter_t &outp, unsigned char idx) {
|
||||
if (!cur_term) return false;
|
||||
if (set_a_background && set_a_background[0]) {
|
||||
return write_color_escape(set_a_background, idx, false);
|
||||
return write_color_escape(outp, set_a_background, idx, false);
|
||||
} else if (set_background && set_background[0]) {
|
||||
return write_color_escape(set_background, idx, false);
|
||||
return write_color_escape(outp, set_background, idx, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void outputter_t::flush_to(int fd) {
|
||||
if (fd >= 0 && !contents_.empty()) {
|
||||
write_loop(fd, contents_.data(), contents_.size());
|
||||
contents_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Exported for builtin_set_color's usage only.
|
||||
bool write_color(rgb_color_t color, bool is_fg) {
|
||||
bool outputter_t::write_color(rgb_color_t color, bool is_fg) {
|
||||
if (!cur_term) return false;
|
||||
bool supports_term24bit =
|
||||
static_cast<bool>(output_get_color_support() & color_support_term24bit);
|
||||
if (!supports_term24bit || !color.is_rgb()) {
|
||||
// Indexed or non-24 bit color.
|
||||
unsigned char idx = index_for_color(color);
|
||||
return (is_fg ? write_foreground_color : write_background_color)(idx);
|
||||
return (is_fg ? write_foreground_color : write_background_color)(*this, idx);
|
||||
}
|
||||
|
||||
// 24 bit! No tparm here, just ANSI escape sequences.
|
||||
|
@ -133,13 +118,7 @@ bool write_color(rgb_color_t color, bool is_fg) {
|
|||
char buff[128];
|
||||
snprintf(buff, sizeof buff, "\x1B[%d;2;%u;%u;%um", is_fg ? 38 : 48, rgb.rgb[0], rgb.rgb[1],
|
||||
rgb.rgb[2]);
|
||||
int (*writer)(char) = output_get_writer();
|
||||
if (writer) {
|
||||
for (size_t i = 0; buff[i]; i++) {
|
||||
writer(buff[i]);
|
||||
}
|
||||
}
|
||||
|
||||
writestr(buff);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -162,23 +141,10 @@ bool write_color(rgb_color_t color, bool is_fg) {
|
|||
///
|
||||
/// \param c Foreground color.
|
||||
/// \param c2 Background color.
|
||||
void set_color(rgb_color_t c, rgb_color_t c2) {
|
||||
#if 0
|
||||
wcstring tmp = c.description();
|
||||
wcstring tmp2 = c2.description();
|
||||
debug(3, "set_color %ls : %ls", tmp.c_str(), tmp2.c_str());
|
||||
#endif
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
void outputter_t::set_color(rgb_color_t c, rgb_color_t c2) {
|
||||
if (!cur_term) return;
|
||||
|
||||
const rgb_color_t normal = rgb_color_t::normal();
|
||||
static rgb_color_t last_color = rgb_color_t::normal();
|
||||
static rgb_color_t last_color2 = rgb_color_t::normal();
|
||||
static bool was_bold = false;
|
||||
static bool was_underline = false;
|
||||
static bool was_italics = false;
|
||||
static bool was_dim = false;
|
||||
static bool was_reverse = false;
|
||||
bool bg_set = false, last_bg_set = false;
|
||||
bool is_bold = false;
|
||||
bool is_underline = false;
|
||||
|
@ -216,14 +182,14 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
was_reverse = false;
|
||||
// If we exit attibute mode, we must first set a color, or previously coloured text might
|
||||
// lose it's color. Terminals are weird...
|
||||
write_foreground_color(0);
|
||||
writembs(exit_attribute_mode);
|
||||
write_foreground_color(*this, 0);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (was_bold && !is_bold) {
|
||||
// Only way to exit bold mode is a reset of all attributes.
|
||||
writembs(exit_attribute_mode);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
last_color = normal;
|
||||
last_color2 = normal;
|
||||
was_bold = false;
|
||||
|
@ -235,7 +201,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
|
||||
if (was_dim && !is_dim) {
|
||||
// Only way to exit dim mode is a reset of all attributes.
|
||||
writembs(exit_attribute_mode);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
last_color = normal;
|
||||
last_color2 = normal;
|
||||
was_bold = false;
|
||||
|
@ -247,7 +213,7 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
|
||||
if (was_reverse && !is_reverse) {
|
||||
// Only way to exit reverse mode is a reset of all attributes.
|
||||
writembs(exit_attribute_mode);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
last_color = normal;
|
||||
last_color2 = normal;
|
||||
was_bold = false;
|
||||
|
@ -272,18 +238,18 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
if (bg_set && !last_bg_set) {
|
||||
// Background color changed and is set, so we enter bold mode to make reading easier.
|
||||
// This means bold mode is _always_ on when the background color is set.
|
||||
writembs_nofail(enter_bold_mode);
|
||||
writembs_nofail(*this, enter_bold_mode);
|
||||
}
|
||||
if (!bg_set && last_bg_set) {
|
||||
// Background color changed and is no longer set, so we exit bold mode.
|
||||
writembs(exit_attribute_mode);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
was_bold = false;
|
||||
was_underline = false;
|
||||
was_italics = false;
|
||||
was_dim = false;
|
||||
was_reverse = false;
|
||||
// We don't know if exit_attribute_mode resets colors, so we set it to something known.
|
||||
if (write_foreground_color(0)) {
|
||||
if (write_foreground_color(*this, 0)) {
|
||||
last_color = rgb_color_t::black();
|
||||
}
|
||||
}
|
||||
|
@ -291,8 +257,8 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
|
||||
if (last_color != c) {
|
||||
if (c.is_normal()) {
|
||||
write_foreground_color(0);
|
||||
writembs(exit_attribute_mode);
|
||||
write_foreground_color(*this, 0);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
|
||||
last_color2 = rgb_color_t::normal();
|
||||
was_bold = false;
|
||||
|
@ -309,9 +275,9 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
|
||||
if (last_color2 != c2) {
|
||||
if (c2.is_normal()) {
|
||||
write_background_color(0);
|
||||
write_background_color(*this, 0);
|
||||
|
||||
writembs(exit_attribute_mode);
|
||||
writembs(*this, exit_attribute_mode);
|
||||
if (!last_color.is_normal()) {
|
||||
write_color(last_color, true /* foreground */);
|
||||
}
|
||||
|
@ -330,66 +296,75 @@ void set_color(rgb_color_t c, rgb_color_t c2) {
|
|||
|
||||
// Lastly, we set bold, underline, italics, dim, and reverse modes correctly.
|
||||
if (is_bold && !was_bold && enter_bold_mode && strlen(enter_bold_mode) > 0 && !bg_set) {
|
||||
writembs_nofail(tparm((char *)enter_bold_mode));
|
||||
writembs_nofail(*this, tparm(enter_bold_mode));
|
||||
was_bold = is_bold;
|
||||
}
|
||||
|
||||
if (was_underline && !is_underline) {
|
||||
writembs_nofail(exit_underline_mode);
|
||||
writembs_nofail(*this, exit_underline_mode);
|
||||
}
|
||||
|
||||
if (!was_underline && is_underline) {
|
||||
writembs_nofail(enter_underline_mode);
|
||||
writembs_nofail(*this, enter_underline_mode);
|
||||
}
|
||||
was_underline = is_underline;
|
||||
|
||||
if (was_italics && !is_italics && enter_italics_mode && strlen(enter_italics_mode) > 0) {
|
||||
writembs_nofail(exit_italics_mode);
|
||||
writembs_nofail(*this, exit_italics_mode);
|
||||
was_italics = is_italics;
|
||||
}
|
||||
|
||||
if (!was_italics && is_italics && enter_italics_mode && strlen(enter_italics_mode) > 0) {
|
||||
writembs_nofail(enter_italics_mode);
|
||||
writembs_nofail(*this, enter_italics_mode);
|
||||
was_italics = is_italics;
|
||||
}
|
||||
|
||||
if (is_dim && !was_dim && enter_dim_mode && strlen(enter_dim_mode) > 0) {
|
||||
writembs_nofail(enter_dim_mode);
|
||||
writembs_nofail(*this, enter_dim_mode);
|
||||
was_dim = is_dim;
|
||||
}
|
||||
|
||||
if (is_reverse && !was_reverse) {
|
||||
// Some terms do not have a reverse mode set, so standout mode is a fallback.
|
||||
if (enter_reverse_mode && strlen(enter_reverse_mode) > 0) {
|
||||
writembs_nofail(enter_reverse_mode);
|
||||
writembs_nofail(*this, enter_reverse_mode);
|
||||
was_reverse = is_reverse;
|
||||
} else if (enter_standout_mode && strlen(enter_standout_mode) > 0) {
|
||||
writembs_nofail(enter_standout_mode);
|
||||
writembs_nofail(*this, enter_standout_mode);
|
||||
was_reverse = is_reverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Default output method, simply calls write() on stdout.
|
||||
static int writeb_internal(char c) { // cppcheck
|
||||
write_loop(1, &c, 1);
|
||||
// tputs accepts a function pointer that receives an int only.
|
||||
// Use the following lock to redirect it to the proper outputter.
|
||||
// Note we can't use owning_lock because the tputs_writer must access it and owning_lock is not
|
||||
// recursive.
|
||||
static std::mutex s_tputs_receiver_lock;
|
||||
static outputter_t *s_tputs_receiver{nullptr};
|
||||
|
||||
static int tputs_writer(tputs_arg_t b) {
|
||||
ASSERT_IS_LOCKED(s_tputs_receiver_lock);
|
||||
assert(s_tputs_receiver && "null s_tputs_receiver");
|
||||
char c = static_cast<char>(b);
|
||||
s_tputs_receiver->writestr(&c, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// This is for writing process notification messages. Has to write to stdout, so clr_eol and such
|
||||
/// functions will work correctly. Not an issue since this function is only used in interactive mode
|
||||
/// anyway.
|
||||
int writeb(tputs_arg_t b) {
|
||||
out(b);
|
||||
return 0;
|
||||
int outputter_t::term_puts(const char *str, int affcnt) {
|
||||
// Acquire the lock, use a scoped_push to substitute in our receiver, then call tputs. The
|
||||
// scoped_push will restore it.
|
||||
scoped_lock locker{s_tputs_receiver_lock};
|
||||
scoped_push<outputter_t *> push(&s_tputs_receiver, this);
|
||||
return tputs(str, affcnt, tputs_writer);
|
||||
}
|
||||
|
||||
/// Write a wide character using the output method specified using output_set_writer(). This should
|
||||
/// only be used when writing characters from user supplied strings. This is needed due to our use
|
||||
/// of the ENCODE_DIRECT_BASE mechanism to allow the user to specify arbitrary byte values to be
|
||||
/// output. Such as in a `printf` invocation that includes literal byte values such as `\x1B`.
|
||||
/// This should not be used for writing non-user supplied characters.
|
||||
int writech(wint_t ch) {
|
||||
/// Write a wide character to the outputter. This should only be used when writing characters from
|
||||
/// user supplied strings. This is needed due to our use of the ENCODE_DIRECT_BASE mechanism to
|
||||
/// allow the user to specify arbitrary byte values to be output. Such as in a `printf` invocation
|
||||
/// that includes literal byte values such as `\x1B`. This should not be used for writing non-user
|
||||
/// supplied characters.
|
||||
int outputter_t::writech(wint_t ch) {
|
||||
char buff[MB_LEN_MAX + 1];
|
||||
size_t len;
|
||||
|
||||
|
@ -408,10 +383,7 @@ int writech(wint_t ch) {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
out(buff[i]);
|
||||
}
|
||||
this->writestr(buff, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -419,12 +391,12 @@ int writech(wint_t ch) {
|
|||
/// messages; just use debug() or fwprintf() for that. It should only be used to output user
|
||||
/// supplied strings that might contain literal bytes; e.g., "\342\224\214" from issue #1894. This
|
||||
/// is needed because those strings may contain chars specially encoded using ENCODE_DIRECT_BASE.
|
||||
void writestr(const wchar_t *str) {
|
||||
CHECK(str, );
|
||||
void outputter_t::writestr(const wchar_t *str) {
|
||||
assert(str && "Empty input string");
|
||||
|
||||
if (MB_CUR_MAX == 1) {
|
||||
// Single-byte locale (C/POSIX/ISO-8859).
|
||||
while (*str) writech(*str++);
|
||||
this->writestr(str);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -443,15 +415,16 @@ void writestr(const wchar_t *str) {
|
|||
buffer = new char[len];
|
||||
}
|
||||
wcstombs(buffer, str, len);
|
||||
|
||||
// Write the string.
|
||||
for (char *pos = buffer; *pos; pos++) {
|
||||
out(*pos);
|
||||
}
|
||||
|
||||
this->writestr(buffer);
|
||||
if (buffer != static_buffer) delete[] buffer;
|
||||
}
|
||||
|
||||
outputter_t &outputter_t::stdoutput() {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
static outputter_t res(STDOUT_FILENO);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns
|
||||
/// rgb_color_t::none() if empty.
|
||||
rgb_color_t best_color(const std::vector<rgb_color_t> &candidates, color_support_t support) {
|
||||
|
@ -548,9 +521,10 @@ rgb_color_t parse_color(const env_var_t &var, bool is_background) {
|
|||
}
|
||||
|
||||
/// Write specified multibyte string.
|
||||
void writembs_check(const char *mbs, const char *mbs_name, bool critical, const char *file, long line) {
|
||||
void writembs_check(outputter_t &outp, const char *mbs, const char *mbs_name, bool critical,
|
||||
const char *file, long line) {
|
||||
if (mbs != NULL) {
|
||||
tputs((char *)mbs, 1, &writeb);
|
||||
outp.term_puts(mbs, 1);
|
||||
} else if (critical) {
|
||||
auto term = env_stack_t::globals().get(L"TERM");
|
||||
const wchar_t *fmt =
|
||||
|
|
103
src/output.h
103
src/output.h
|
@ -14,24 +14,102 @@
|
|||
|
||||
class env_var_t;
|
||||
|
||||
void set_color(rgb_color_t c, rgb_color_t c2);
|
||||
class outputter_t {
|
||||
/// Storage for buffered contents.
|
||||
std::string contents_;
|
||||
|
||||
void writembs_check(const char *mbs, const char *mbs_name, bool critical, const char *file, long line);
|
||||
#define writembs(mbs) writembs_check((mbs), #mbs, true, __FILE__, __LINE__)
|
||||
#define writembs_nofail(mbs) writembs_check((mbs), #mbs, false, __FILE__, __LINE__)
|
||||
/// Count of how many outstanding beginBuffering() calls there are.
|
||||
uint32_t bufferCount_{0};
|
||||
|
||||
int writech(wint_t ch);
|
||||
/// fd to output to.
|
||||
int fd_{-1};
|
||||
|
||||
void writestr(const wchar_t *str);
|
||||
rgb_color_t last_color = rgb_color_t::normal();
|
||||
rgb_color_t last_color2 = rgb_color_t::normal();
|
||||
bool was_bold = false;
|
||||
bool was_underline = false;
|
||||
bool was_italics = false;
|
||||
bool was_dim = false;
|
||||
bool was_reverse = false;
|
||||
|
||||
/// Construct an outputter which outputs to a given fd.
|
||||
explicit outputter_t(int fd) : fd_(fd) {}
|
||||
|
||||
/// Flush output, if we have a set fd and our buffering count is 0.
|
||||
void maybe_flush() {
|
||||
if (fd_ >= 0 && bufferCount_ == 0) flush_to(fd_);
|
||||
}
|
||||
|
||||
public:
|
||||
/// Construct an outputter which outputs to its string.
|
||||
outputter_t() = default;
|
||||
|
||||
/// Unconditionally write the color string to the output.
|
||||
bool write_color(rgb_color_t color, bool is_fg);
|
||||
|
||||
/// Set the foreground and background color.
|
||||
void set_color(rgb_color_t c, rgb_color_t c2);
|
||||
|
||||
/// Write a wide character to the receiver.
|
||||
int writech(wint_t ch);
|
||||
|
||||
/// Write a NUL-terminated wide character string to the receiver.
|
||||
void writestr(const wchar_t *str);
|
||||
|
||||
/// Write a wide character string to the receiver.
|
||||
void writestr(const wcstring &str) { writestr(str.c_str()); }
|
||||
|
||||
/// Write the given terminfo string to the receiver, like tputs().
|
||||
int term_puts(const char *str, int affcnt);
|
||||
|
||||
/// Write a narrow string of the given length.
|
||||
void writestr(const char *str, size_t len) {
|
||||
contents_.append(str, len);
|
||||
maybe_flush();
|
||||
}
|
||||
|
||||
/// Write a narrow NUL-terminated string.
|
||||
void writestr(const char *str) { writestr(str, strlen(str)); }
|
||||
|
||||
/// Write a narrow character.
|
||||
void push_back(char c) {
|
||||
contents_.push_back(c);
|
||||
maybe_flush();
|
||||
}
|
||||
|
||||
/// \return the "output" contents.
|
||||
const std::string &contents() const { return contents_; }
|
||||
|
||||
/// Output any buffered data to the given \p fd.
|
||||
void flush_to(int fd);
|
||||
|
||||
/// Begins buffering. Output will not be automatically flushed until a corresponding
|
||||
/// endBuffering() call.
|
||||
void beginBuffering() {
|
||||
bufferCount_++;
|
||||
assert(bufferCount_ > 0 && "bufferCount_ overflow");
|
||||
}
|
||||
|
||||
/// Balance a beginBuffering() call.
|
||||
void endBuffering() {
|
||||
assert(bufferCount_ > 0 && "bufferCount_ underflow");
|
||||
bufferCount_--;
|
||||
maybe_flush();
|
||||
}
|
||||
|
||||
/// Accesses the singleton stdout outputter.
|
||||
/// This can only be used from the main thread.
|
||||
/// This outputter flushes its buffer after every write operation.
|
||||
static outputter_t &stdoutput();
|
||||
};
|
||||
|
||||
void writembs_check(outputter_t &outp, const char *mbs, const char *mbs_name, bool critical,
|
||||
const char *file, long line);
|
||||
#define writembs(outp, mbs) writembs_check((outp), (mbs), #mbs, true, __FILE__, __LINE__)
|
||||
#define writembs_nofail(outp, mbs) writembs_check((outp), (mbs), #mbs, false, __FILE__, __LINE__)
|
||||
|
||||
rgb_color_t parse_color(const env_var_t &val, bool is_background);
|
||||
|
||||
int writeb(tputs_arg_t b);
|
||||
|
||||
void output_set_writer(int (*writer)(char));
|
||||
|
||||
int (*output_get_writer())(char);
|
||||
|
||||
/// Sets what colors are supported.
|
||||
enum { color_support_term256 = 1 << 0, color_support_term24bit = 1 << 1 };
|
||||
typedef unsigned int color_support_t;
|
||||
|
@ -40,7 +118,6 @@ void output_set_color_support(color_support_t support);
|
|||
|
||||
rgb_color_t best_color(const std::vector<rgb_color_t> &colors, color_support_t support);
|
||||
|
||||
bool write_color(rgb_color_t color, bool is_fg);
|
||||
unsigned char index_for_color(rgb_color_t c);
|
||||
|
||||
#endif
|
||||
|
|
13
src/proc.cpp
13
src/proc.cpp
|
@ -475,12 +475,13 @@ typedef enum { JOB_STOPPED, JOB_ENDED } job_status_t;
|
|||
static void print_job_status(const job_t *j, job_status_t status) {
|
||||
const wchar_t *msg = L"Job %d, '%ls' has ended"; // this is the most common status msg
|
||||
if (status == JOB_STOPPED) msg = L"Job %d, '%ls' has stopped";
|
||||
|
||||
fwprintf(stdout, L"\r");
|
||||
fwprintf(stdout, _(msg), j->job_id, truncate_command(j->command()).c_str());
|
||||
outputter_t outp;
|
||||
outp.writestr("\r");
|
||||
outp.writestr(format_string(_(msg), j->job_id, truncate_command(j->command()).c_str()));
|
||||
if (clr_eol) outp.term_puts(clr_eol, 1);
|
||||
outp.writestr(L"\n");
|
||||
fflush(stdout);
|
||||
if (clr_eol) tputs(clr_eol, 1, &writeb);
|
||||
fwprintf(stdout, L"\n");
|
||||
outp.flush_to(STDOUT_FILENO);
|
||||
}
|
||||
|
||||
void proc_fire_event(const wchar_t *msg, event_type_t type, pid_t pid, int status) {
|
||||
|
@ -578,7 +579,7 @@ static bool process_clean_after_marking(bool allow_interactive) {
|
|||
signal_get_desc(WTERMSIG(p->status)));
|
||||
}
|
||||
|
||||
if (clr_eol) tputs(clr_eol, 1, &writeb);
|
||||
if (clr_eol) outputter_t::stdoutput().term_puts(clr_eol, 1);
|
||||
fwprintf(stdout, L"\n");
|
||||
}
|
||||
found = false;
|
||||
|
|
|
@ -458,8 +458,8 @@ static void reader_super_highlight_me_plenty(int highlight_pos_adjust = 0, bool
|
|||
static bool exit_forced;
|
||||
|
||||
/// Give up control of terminal.
|
||||
static void term_donate() {
|
||||
set_color(rgb_color_t::normal(), rgb_color_t::normal());
|
||||
static void term_donate(outputter_t &outp) {
|
||||
outp.set_color(rgb_color_t::normal(), rgb_color_t::normal());
|
||||
|
||||
while (1) {
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &tty_modes_for_external_cmds) == -1) {
|
||||
|
@ -844,7 +844,7 @@ void reader_write_title(const wcstring &cmd, bool reset_cursor_position) {
|
|||
}
|
||||
|
||||
proc_pop_interactive();
|
||||
set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
outputter_t::stdoutput().set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
if (reset_cursor_position && !lst.empty()) {
|
||||
// Put the cursor back at the beginning of the line (issue #2453).
|
||||
ignore_result(write(STDOUT_FILENO, "\r", 1));
|
||||
|
@ -1844,7 +1844,7 @@ static void reader_interactive_init() {
|
|||
/// Destroy data for interactive use.
|
||||
static void reader_interactive_destroy() {
|
||||
kill_destroy();
|
||||
set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
outputter_t::stdoutput().set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
input_destroy();
|
||||
}
|
||||
|
||||
|
@ -2031,9 +2031,9 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) {
|
|||
// For compatibility with fish 2.0's $_, now replaced with `status current-command`
|
||||
if (!ft.empty()) parser.vars().set_one(L"_", ENV_GLOBAL, ft);
|
||||
|
||||
outputter_t &outp = outputter_t::stdoutput();
|
||||
reader_write_title(cmd);
|
||||
|
||||
term_donate();
|
||||
term_donate(outp);
|
||||
|
||||
gettimeofday(&time_before, NULL);
|
||||
|
||||
|
@ -3358,7 +3358,7 @@ const wchar_t *reader_readline(int nchars) {
|
|||
if (errno == EIO) redirect_tty_output();
|
||||
wperror(L"tcsetattr"); // return to previous mode
|
||||
}
|
||||
set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
outputter_t::stdoutput().set_color(rgb_color_t::reset(), rgb_color_t::reset());
|
||||
}
|
||||
|
||||
return finished ? data->command_line.text.c_str() : NULL;
|
||||
|
|
142
src/screen.cpp
142
src/screen.cpp
|
@ -52,29 +52,14 @@
|
|||
|
||||
static void invalidate_soft_wrap(screen_t *scr);
|
||||
|
||||
/// Ugly kludge. The internal buffer used to store output of tputs. Since tputs external function
|
||||
/// can only take an integer and not a pointer as parameter we need a static storage buffer.
|
||||
typedef std::vector<char> data_buffer_t;
|
||||
static data_buffer_t *s_writeb_buffer = 0;
|
||||
|
||||
static int s_writeb(char character);
|
||||
|
||||
/// Class to temporarily set s_writeb_buffer and the writer function in a scoped way.
|
||||
/// RAII class to begin and end buffering around stdoutput().
|
||||
class scoped_buffer_t {
|
||||
data_buffer_t *const old_buff;
|
||||
int (*const old_writer)(char);
|
||||
screen_t &screen_;
|
||||
|
||||
public:
|
||||
explicit scoped_buffer_t(data_buffer_t *buff)
|
||||
: old_buff(s_writeb_buffer), old_writer(output_get_writer()) {
|
||||
s_writeb_buffer = buff;
|
||||
output_set_writer(s_writeb);
|
||||
}
|
||||
scoped_buffer_t(screen_t &s) : screen_(s) { screen_.outp().beginBuffering(); }
|
||||
|
||||
~scoped_buffer_t() {
|
||||
s_writeb_buffer = old_buff;
|
||||
output_set_writer(old_writer);
|
||||
}
|
||||
~scoped_buffer_t() { screen_.outp().endBuffering(); }
|
||||
};
|
||||
|
||||
// Singleton of the cached escape sequences seen in prompts and similar strings.
|
||||
|
@ -439,12 +424,6 @@ static void s_desired_append_char(screen_t *s, wchar_t b, int c, int indent, siz
|
|||
}
|
||||
}
|
||||
|
||||
/// The writeb function offered to tputs.
|
||||
static int s_writeb(char c) {
|
||||
s_writeb_buffer->push_back(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Write the bytes needed to move screen cursor to the specified position to the specified buffer.
|
||||
/// The actual_cursor field of the specified screen_t will be updated.
|
||||
///
|
||||
|
@ -452,18 +431,20 @@ static int s_writeb(char c) {
|
|||
/// \param b the buffer to send the output escape codes to
|
||||
/// \param new_x the new x position
|
||||
/// \param new_y the new y position
|
||||
static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
||||
static void s_move(screen_t *s, int new_x, int new_y) {
|
||||
if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y) return;
|
||||
|
||||
const scoped_buffer_t buffering(*s);
|
||||
|
||||
// If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We
|
||||
// don't know! We can fix it up though.
|
||||
if (s->actual.cursor.x == common_get_width()) {
|
||||
// Either issue a cr to go back to the beginning of this line, or a nl to go to the
|
||||
// beginning of the next one, depending on what we think is more efficient.
|
||||
if (new_y <= s->actual.cursor.y) {
|
||||
b->push_back('\r');
|
||||
s->outp().push_back('\r');
|
||||
} else {
|
||||
b->push_back('\n');
|
||||
s->outp().push_back('\n');
|
||||
s->actual.cursor.y++;
|
||||
}
|
||||
// Either way we're not in the first column.
|
||||
|
@ -474,7 +455,7 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
|||
int x_steps, y_steps;
|
||||
|
||||
const char *str;
|
||||
scoped_buffer_t scoped_buffer(b);
|
||||
auto &outp = s->outp();
|
||||
|
||||
y_steps = new_y - s->actual.cursor.y;
|
||||
|
||||
|
@ -492,13 +473,13 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
|||
}
|
||||
|
||||
for (i = 0; i < abs(y_steps); i++) {
|
||||
writembs(str);
|
||||
writembs(outp, str);
|
||||
}
|
||||
|
||||
x_steps = new_x - s->actual.cursor.x;
|
||||
|
||||
if (x_steps && new_x == 0) {
|
||||
b->push_back('\r');
|
||||
outp.push_back('\r');
|
||||
x_steps = 0;
|
||||
}
|
||||
|
||||
|
@ -517,10 +498,10 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
|||
multi_str != NULL && multi_str[0] != '\0' && abs(x_steps) * strlen(str) > strlen(multi_str);
|
||||
if (use_multi && cur_term) {
|
||||
char *multi_param = tparm((char *)multi_str, abs(x_steps));
|
||||
writembs(multi_param);
|
||||
writembs(outp, multi_param);
|
||||
} else {
|
||||
for (i = 0; i < abs(x_steps); i++) {
|
||||
writembs(str);
|
||||
writembs(outp, str);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,20 +510,19 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) {
|
|||
}
|
||||
|
||||
/// Set the pen color for the terminal.
|
||||
static void s_set_color(screen_t *s, data_buffer_t *b, highlight_spec_t c) {
|
||||
static void s_set_color(screen_t *s, const environment_t &vars, highlight_spec_t c) {
|
||||
UNUSED(s);
|
||||
scoped_buffer_t scoped_buffer(b);
|
||||
|
||||
unsigned int uc = (unsigned int)c;
|
||||
set_color(highlight_get_color(uc & 0xffff, false),
|
||||
highlight_get_color((uc >> 16) & 0xffff, true));
|
||||
s->outp().set_color(highlight_get_color(uc & 0xfff, false),
|
||||
highlight_get_color((uc >> 16) & 0xffff, true));
|
||||
}
|
||||
|
||||
/// Convert a wide character to a multibyte string and append it to the buffer.
|
||||
static void s_write_char(screen_t *s, data_buffer_t *b, wchar_t c) {
|
||||
scoped_buffer_t scoped_buffer(b);
|
||||
static void s_write_char(screen_t *s, wchar_t c) {
|
||||
scoped_buffer_t outp(*s);
|
||||
s->actual.cursor.x += fish_wcwidth_min_0(c);
|
||||
writech(c);
|
||||
s->outp().writech(c);
|
||||
if (s->actual.cursor.x == s->actual_width && allow_soft_wrap()) {
|
||||
s->soft_wrap_location.x = 0;
|
||||
s->soft_wrap_location.y = s->actual.cursor.y + 1;
|
||||
|
@ -555,17 +535,11 @@ static void s_write_char(screen_t *s, data_buffer_t *b, wchar_t c) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Send the specified string through tputs and append the output to the specified buffer.
|
||||
static void s_write_mbs(data_buffer_t *b, const char *s) {
|
||||
scoped_buffer_t scoped_buffer(b);
|
||||
writembs(s);
|
||||
}
|
||||
/// Send the specified string through tputs and append the output to the screen's outputter.
|
||||
static void s_write_mbs(screen_t *screen, const char *s) { writembs(screen->outp(), s); }
|
||||
|
||||
/// Convert a wide string to a multibyte string and append it to the buffer.
|
||||
static void s_write_str(data_buffer_t *b, const wchar_t *s) {
|
||||
scoped_buffer_t scoped_buffer(b);
|
||||
writestr(s);
|
||||
}
|
||||
static void s_write_str(screen_t *screen, const wchar_t *s) { screen->outp().writestr(s); }
|
||||
|
||||
/// Returns the length of the "shared prefix" of the two lines, which is the run of matching text
|
||||
/// and colors. If the prefix ends on a combining character, do not include the previous character
|
||||
|
@ -608,7 +582,8 @@ static void invalidate_soft_wrap(screen_t *scr) { scr->soft_wrap_location = INVA
|
|||
|
||||
/// Update the screen to match the desired output.
|
||||
static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring &right_prompt) {
|
||||
// if (test_stuff(scr)) return;
|
||||
const environment_t &vars = env_stack_t::principal();
|
||||
const scoped_buffer_t buffering(*scr);
|
||||
const size_t left_prompt_width =
|
||||
calc_prompt_layout(left_prompt, cached_layouts).last_line_width;
|
||||
const size_t right_prompt_width =
|
||||
|
@ -620,8 +595,6 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
size_t actual_lines_before_reset = scr->actual_lines_before_reset;
|
||||
scr->actual_lines_before_reset = 0;
|
||||
|
||||
data_buffer_t output;
|
||||
|
||||
bool need_clear_lines = scr->need_clear_lines;
|
||||
bool need_clear_screen = scr->need_clear_screen;
|
||||
bool has_cleared_screen = false;
|
||||
|
@ -630,7 +603,7 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
// Ensure we don't issue a clear screen for the very first output, to avoid issue #402.
|
||||
if (scr->actual_width != SCREEN_WIDTH_UNINITIALIZED) {
|
||||
need_clear_screen = true;
|
||||
s_move(scr, &output, 0, 0);
|
||||
s_move(scr, 0, 0);
|
||||
s_reset(scr, screen_reset_current_line_contents);
|
||||
|
||||
need_clear_lines = need_clear_lines || scr->need_clear_lines;
|
||||
|
@ -647,8 +620,8 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
const size_t lines_with_stuff = maxi(actual_lines_before_reset, scr->actual.line_count());
|
||||
|
||||
if (left_prompt != scr->actual_left_prompt) {
|
||||
s_move(scr, &output, 0, 0);
|
||||
s_write_str(&output, left_prompt.c_str());
|
||||
s_move(scr, 0, 0);
|
||||
s_write_str(scr, left_prompt.c_str());
|
||||
scr->actual_left_prompt = left_prompt;
|
||||
scr->actual.cursor.x = (int)left_prompt_width;
|
||||
}
|
||||
|
@ -714,22 +687,22 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
// wrapping.
|
||||
if (j + 1 == (size_t)screen_width && should_clear_screen_this_line &&
|
||||
!has_cleared_screen) {
|
||||
s_move(scr, &output, current_width, (int)i);
|
||||
s_write_mbs(&output, clr_eos);
|
||||
s_move(scr, current_width, (int)i);
|
||||
s_write_mbs(scr, clr_eos);
|
||||
has_cleared_screen = true;
|
||||
}
|
||||
|
||||
perform_any_impending_soft_wrap(scr, current_width, (int)i);
|
||||
s_move(scr, &output, current_width, (int)i);
|
||||
s_set_color(scr, &output, o_line.color_at(j));
|
||||
s_write_char(scr, &output, o_line.char_at(j));
|
||||
s_move(scr, current_width, (int)i);
|
||||
s_set_color(scr, vars, o_line.color_at(j));
|
||||
s_write_char(scr, o_line.char_at(j));
|
||||
current_width += fish_wcwidth_min_0(o_line.char_at(j));
|
||||
}
|
||||
|
||||
// Clear the screen if we have not done so yet.
|
||||
if (should_clear_screen_this_line && !has_cleared_screen) {
|
||||
s_move(scr, &output, current_width, (int)i);
|
||||
s_write_mbs(&output, clr_eos);
|
||||
s_move(scr, current_width, (int)i);
|
||||
s_write_mbs(scr, clr_eos);
|
||||
has_cleared_screen = true;
|
||||
}
|
||||
|
||||
|
@ -750,16 +723,16 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
clear_remainder = prev_width > current_width;
|
||||
}
|
||||
if (clear_remainder && clr_eol) {
|
||||
s_set_color(scr, &output, 0xffffffff);
|
||||
s_move(scr, &output, current_width, (int)i);
|
||||
s_write_mbs(&output, clr_eol);
|
||||
s_set_color(scr, vars, 0xffffffff);
|
||||
s_move(scr, current_width, (int)i);
|
||||
s_write_mbs(scr, clr_eol);
|
||||
}
|
||||
|
||||
// Output any rprompt if this is the first line.
|
||||
if (i == 0 && right_prompt_width > 0) { //!OCLINT(Use early exit/continue)
|
||||
s_move(scr, &output, (int)(screen_width - right_prompt_width), (int)i);
|
||||
s_set_color(scr, &output, 0xffffffff);
|
||||
s_write_str(&output, right_prompt.c_str());
|
||||
s_move(scr, (int)(screen_width - right_prompt_width), (int)i);
|
||||
s_set_color(scr, vars, 0xffffffff);
|
||||
s_write_str(scr, right_prompt.c_str());
|
||||
scr->actual.cursor.x += right_prompt_width;
|
||||
|
||||
// We output in the last column. Some terms (Linux) push the cursor further right, past
|
||||
|
@ -770,28 +743,23 @@ static void s_update(screen_t *scr, const wcstring &left_prompt, const wcstring
|
|||
// wrapped. If so, then a cr will go to the beginning of the following line! So instead
|
||||
// issue a bunch of "move left" commands to get back onto the line, and then jump to the
|
||||
// front of it.
|
||||
s_move(scr, &output, scr->actual.cursor.x - (int)right_prompt_width,
|
||||
scr->actual.cursor.y);
|
||||
s_write_str(&output, L"\r");
|
||||
s_move(scr, scr->actual.cursor.x - (int)right_prompt_width, scr->actual.cursor.y);
|
||||
s_write_str(scr, L"\r");
|
||||
scr->actual.cursor.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear remaining lines (if any) if we haven't cleared the screen.
|
||||
if (!has_cleared_screen && scr->desired.line_count() < lines_with_stuff && clr_eol) {
|
||||
s_set_color(scr, &output, 0xffffffff);
|
||||
s_set_color(scr, vars, 0xffffffff);
|
||||
for (size_t i = scr->desired.line_count(); i < lines_with_stuff; i++) {
|
||||
s_move(scr, &output, 0, (int)i);
|
||||
s_write_mbs(&output, clr_eol);
|
||||
s_move(scr, 0, (int)i);
|
||||
s_write_mbs(scr, clr_eol);
|
||||
}
|
||||
}
|
||||
|
||||
s_move(scr, &output, scr->desired.cursor.x, scr->desired.cursor.y);
|
||||
s_set_color(scr, &output, 0xffffffff);
|
||||
|
||||
if (!output.empty()) {
|
||||
write_loop(STDOUT_FILENO, &output.at(0), output.size());
|
||||
}
|
||||
s_move(scr, scr->desired.cursor.x, scr->desired.cursor.y);
|
||||
s_set_color(scr, vars, 0xffffffff);
|
||||
|
||||
// We have now synced our actual screen against our desired screen. Note that this is a big
|
||||
// assignment!
|
||||
|
@ -1206,21 +1174,15 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) {
|
|||
fstat(2, &s->prev_buff_2);
|
||||
}
|
||||
|
||||
bool screen_force_clear_to_end() {
|
||||
bool result = false;
|
||||
void screen_force_clear_to_end() {
|
||||
if (clr_eos) {
|
||||
data_buffer_t output;
|
||||
s_write_mbs(&output, clr_eos);
|
||||
if (!output.empty()) {
|
||||
write_loop(STDOUT_FILENO, &output.at(0), output.size());
|
||||
result = true;
|
||||
}
|
||||
writembs(outputter_t::stdoutput(), clr_eos);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
screen_t::screen_t()
|
||||
: desired(),
|
||||
: outp_(outputter_t::stdoutput()),
|
||||
desired(),
|
||||
actual(),
|
||||
actual_left_prompt(),
|
||||
last_right_prompt_width(),
|
||||
|
|
13
src/screen.h
13
src/screen.h
|
@ -110,10 +110,14 @@ class screen_data_t {
|
|||
bool empty() const { return line_datas.empty(); }
|
||||
};
|
||||
|
||||
class outputter_t;
|
||||
|
||||
/// The class representing the current and desired screen contents.
|
||||
class screen_t {
|
||||
outputter_t &outp_;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
/// Constructor.
|
||||
screen_t();
|
||||
|
||||
/// The internal representation of the desired screen contents.
|
||||
|
@ -144,6 +148,9 @@ class screen_t {
|
|||
/// These status buffers are used to check if any output has occurred other than from fish's
|
||||
/// main loop, in which case we need to redraw.
|
||||
struct stat prev_buff_1, prev_buff_2, post_buff_1, post_buff_2;
|
||||
|
||||
/// \return the outputter for this screen.
|
||||
outputter_t &outp() { return outp_; }
|
||||
};
|
||||
|
||||
/// This is the main function for the screen putput library. It is used to define the desired
|
||||
|
@ -197,8 +204,8 @@ enum screen_reset_mode_t {
|
|||
|
||||
void s_reset(screen_t *s, screen_reset_mode_t mode);
|
||||
|
||||
/// Issues an immediate clr_eos, returning if it existed.
|
||||
bool screen_force_clear_to_end();
|
||||
/// Issues an immediate clr_eos.
|
||||
void screen_force_clear_to_end();
|
||||
|
||||
/// Returns the length of an escape code. Exposed for testing purposes only.
|
||||
size_t escape_code_length(const wchar_t *code);
|
||||
|
|
Loading…
Reference in a new issue