Extract unwrapping for buffer-writes that are assumed to not fail

This commit is contained in:
Johannes Altmanninger
2025-03-20 22:02:38 +01:00
parent 9206a91701
commit 39aa478108
3 changed files with 73 additions and 34 deletions

View File

@@ -10,6 +10,7 @@
use crate::global_safety::RelaxedAtomicBool;
use crate::key;
use crate::libc::MB_CUR_MAX;
use crate::output::Output;
use crate::parse_util::parse_util_escape_string_with_quote;
use crate::termsize::Termsize;
use crate::wchar::{decode_byte_from_char, encode_byte_to_char, prelude::*};
@@ -1377,6 +1378,38 @@ pub fn write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<()> {
Ok(())
}
// Output writes always succeed; this adapter allows us to use it in a write-like macro.
struct OutputWriteAdapter<'a, T: Output>(&'a mut T);
impl<'a, T: Output> std::fmt::Write for OutputWriteAdapter<'a, T> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.0.write_bytes(s.as_bytes());
Ok(())
}
}
impl<'a, T: Output> std::io::Write for OutputWriteAdapter<'a, T> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.write_bytes(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub(crate) fn do_write_to_output(writer: &mut impl Output, args: std::fmt::Arguments<'_>) {
let mut adapter = OutputWriteAdapter(writer);
std::fmt::write(&mut adapter, args).unwrap()
}
#[macro_export]
macro_rules! write_to_output {
($out:expr, $($arg:tt)*) => {{
$crate::common::do_write_to_output($out, format_args!($($arg)*));
}};
}
/// A rusty port of the C++ `read_loop()` function from `common.cpp`. This should be deprecated in
/// favor of native rust read/write methods at some point.
///

View File

@@ -42,6 +42,16 @@ pub fn set_color_support(val: ColorSupport) {
COLOR_SUPPORT.store(val.bits(), Ordering::Relaxed);
}
pub(crate) trait Output {
fn write_bytes(&mut self, buf: &[u8]);
}
impl Output for Vec<u8> {
fn write_bytes(&mut self, buf: &[u8]) {
self.extend_from_slice(buf);
}
}
fn index_for_color(c: RgbColor) -> u8 {
if c.is_named() || !(get_color_support().contains(ColorSupport::TERM_256COLOR)) {
return c.to_name_index();
@@ -65,14 +75,13 @@ fn write_color_escape(outp: &mut Outputter, term: &Term, todo: &CStr, mut idx: u
if term.max_colors == Some(8) && idx > 8 {
idx -= 8;
}
write!(
write_to_output!(
outp,
"\x1B[{}m",
(if idx > 7 { 82 } else { 30 }) + i32::from(idx) + ((i32::from(!is_fg)) * 10)
)
.expect("Writing to in-memory buffer should never fail");
);
} else {
write!(outp, "\x1B[{};5;{}m", if is_fg { 38 } else { 48 }, idx).unwrap();
write_to_output!(outp, "\x1B[{};5;{}m", if is_fg { 38 } else { 48 }, idx);
}
}
}
@@ -183,15 +192,14 @@ pub fn write_color(&mut self, color: RgbColor, is_fg: bool) -> bool {
// Foreground: ^[38;2;<r>;<g>;<b>m
// Background: ^[48;2;<r>;<g>;<b>m
let rgb = color.to_color24();
write!(
write_to_output!(
self,
"\x1B[{};2;{};{};{}m",
if is_fg { 38 } else { 48 },
rgb.r,
rgb.g,
rgb.b
)
.expect("Outputter::write should never fail");
);
true
}
@@ -423,6 +431,13 @@ fn flush(&mut self) -> Result<()> {
}
}
impl Output for Outputter {
fn write_bytes(&mut self, buf: &[u8]) {
self.contents.extend_from_slice(buf);
self.maybe_flush();
}
}
impl Outputter {
/// Emit a terminfo string, like tputs.
/// affcnt (number of lines affected) is assumed to be 1, i.e. not applicable.
@@ -472,17 +487,9 @@ fn drop(&mut self) {
}
}
impl<'a> Write for BufferedOuputter<'a> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.0
.write(buf)
.expect("Writing to in-memory buffer should never fail");
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> {
self.0.flush().unwrap();
Ok(())
impl<'a> Output for BufferedOuputter<'a> {
fn write_bytes(&mut self, buf: &[u8]) {
self.0.write_bytes(buf);
}
}

View File

@@ -106,6 +106,7 @@
use crate::output::parse_color;
use crate::output::parse_color_maybe_none;
use crate::output::BufferedOuputter;
use crate::output::Output;
use crate::output::Outputter;
use crate::pager::{PageRendering, Pager, SelectionMotion};
use crate::panic::AT_EXIT;
@@ -689,12 +690,11 @@ fn read_i(parser: &Parser) {
data.clear(EditableLineTag::Commandline);
data.update_buff_pos(EditableLineTag::Commandline, None);
// OSC 133 "Command start"
write!(
write_to_output!(
&mut BufferedOuputter::new(Outputter::stdoutput()),
"\x1b]133;C;cmdline_url={}\x07",
escape_string(&command, EscapeStringStyle::Url),
)
.unwrap();
);
event::fire_generic(parser, L!("fish_preexec").to_owned(), vec![command.clone()]);
let eval_res = reader_run_command(parser, &command);
signal_clear_cancel();
@@ -707,12 +707,11 @@ fn read_i(parser: &Parser) {
parser.libdata_mut().exit_current_script = false;
// OSC 133 "Command finished"
write!(
write_to_output!(
&mut BufferedOuputter::new(Outputter::stdoutput()),
"\x1b]133;D;{}\x07",
parser.get_last_status()
)
.unwrap();
);
event::fire_generic(parser, L!("fish_postexec").to_owned(), vec![command]);
// Allow any pending history items to be returned in the history array.
data.history.resolve_pending();
@@ -1451,7 +1450,7 @@ pub fn request_cursor_position(
assert!(wait_guard.is_none());
*wait_guard = Some(BlockingWait::CursorPosition(cursor_position_wait));
}
let _ = out.write_all(b"\x1b[6n");
out.write_bytes(b"\x1b[6n");
self.save_screen_state();
}
@@ -2580,7 +2579,7 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
}
}
fn xtgettcap(out: &mut impl Write, cap: &str) {
fn xtgettcap(out: &mut impl Output, cap: &str) {
FLOG!(
reader,
format!(
@@ -2589,16 +2588,16 @@ fn xtgettcap(out: &mut impl Write, cap: &str) {
format!("\x1bP+q{}\x1b\\", DisplayAsHex(cap))
)
);
let _ = write!(out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap));
write_to_output!(out, "\x1bP+q{}\x1b\\", DisplayAsHex(cap));
}
fn query_capabilities_via_dcs(out: &mut impl std::io::Write) {
let _ = out.write_all(b"\x1b[?2026h"); // begin synchronized update
let _ = out.write_all(b"\x1b[?1049h"); // enable alternative screen buffer
xtgettcap(out.by_ref(), "indn");
xtgettcap(out.by_ref(), "cuu");
let _ = out.write_all(b"\x1b[?1049l"); // disable alternative screen buffer
let _ = out.write_all(b"\x1b[?2026l"); // end synchronized update
fn query_capabilities_via_dcs(out: &mut impl Output) {
out.write_bytes(b"\x1b[?2026h"); // begin synchronized update
out.write_bytes(b"\x1b[?1049h"); // enable alternative screen buffer
xtgettcap(out, "indn");
xtgettcap(out, "cuu");
out.write_bytes(b"\x1b[?1049l"); // disable alternative screen buffer
out.write_bytes(b"\x1b[?2026l"); // end synchronized update
}
impl<'a> Reader<'a> {