mirror of
https://github.com/wez/wezterm.git
synced 2024-12-25 22:33:52 +03:00
buffer processing in Performer::print
This helps to accumulate multi-char grapheme sequences into a single cell.
This commit is contained in:
parent
10f0403973
commit
6c1f089f13
@ -114,27 +114,18 @@ impl Screen {
|
||||
|
||||
/// Set a cell. the x and y coordinates are relative to the visible screeen
|
||||
/// origin. 0,0 is the top left.
|
||||
pub fn set_cell(
|
||||
&mut self,
|
||||
x: usize,
|
||||
y: VisibleRowIndex,
|
||||
c: char,
|
||||
attr: &CellAttributes,
|
||||
) -> &Cell {
|
||||
pub fn set_cell(&mut self, x: usize, y: VisibleRowIndex, cell: &Cell) -> &Cell {
|
||||
let line_idx = self.phys_row(y);
|
||||
debug!(
|
||||
"set_cell {} x={} y={} phys={} {:?}",
|
||||
c, x, y, line_idx, attr
|
||||
);
|
||||
debug!("set_cell x={} y={} phys={} {:?}", x, y, line_idx, cell);
|
||||
|
||||
let line = self.line_mut(line_idx);
|
||||
line.invalidate_implicit_links();
|
||||
|
||||
if attr.hyperlink.is_some() {
|
||||
if cell.attrs().hyperlink.is_some() {
|
||||
line.set_has_hyperlink(true);
|
||||
}
|
||||
|
||||
line.set_cell(x, Cell::new(c, attr.clone()))
|
||||
line.set_cell(x, cell.clone())
|
||||
}
|
||||
|
||||
pub fn clear_line(
|
||||
|
@ -80,10 +80,7 @@ impl Terminal {
|
||||
pub fn advance_bytes<B: AsRef<[u8]>>(&mut self, bytes: B, host: &mut TerminalHost) {
|
||||
let bytes = bytes.as_ref();
|
||||
|
||||
let mut performer = Performer {
|
||||
state: &mut self.state,
|
||||
host,
|
||||
};
|
||||
let mut performer = Performer::new(&mut self.state, host);
|
||||
|
||||
self.parser.parse(bytes, |action| performer.perform(action));
|
||||
}
|
||||
|
@ -1225,9 +1225,9 @@ impl TerminalState {
|
||||
let limit = (x + n as usize).min(self.screen().physical_cols);
|
||||
{
|
||||
let screen = self.screen_mut();
|
||||
let blank = CellAttributes::default();
|
||||
let blank = Cell::default();
|
||||
for x in x..limit as usize {
|
||||
screen.set_cell(x, y, ' ', &blank);
|
||||
screen.set_cell(x, y, &blank);
|
||||
}
|
||||
}
|
||||
self.clear_selection_if_intersects(x..limit, y as ScrollbackOrVisibleRowIndex);
|
||||
@ -1414,6 +1414,7 @@ impl TerminalState {
|
||||
pub(crate) struct Performer<'a> {
|
||||
pub state: &'a mut TerminalState,
|
||||
pub host: &'a mut TerminalHost,
|
||||
print: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> Deref for Performer<'a> {
|
||||
@ -1430,7 +1431,65 @@ impl<'a> DerefMut for Performer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Performer<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.flush_print();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Performer<'a> {
|
||||
pub fn new(state: &'a mut TerminalState, host: &'a mut TerminalHost) -> Self {
|
||||
Self {
|
||||
state,
|
||||
host,
|
||||
print: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_print(&mut self) {
|
||||
let p = match self.print.take() {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
|
||||
for g in unicode_segmentation::UnicodeSegmentation::graphemes(p.as_str(), true) {
|
||||
if self.wrap_next {
|
||||
self.new_line(true);
|
||||
}
|
||||
|
||||
let x = self.cursor.x;
|
||||
let y = self.cursor.y;
|
||||
let width = self.screen().physical_cols;
|
||||
|
||||
let pen = self.pen.clone();
|
||||
|
||||
// Assign the cell and extract its printable width
|
||||
let print_width = {
|
||||
let cell = self
|
||||
.screen_mut()
|
||||
.set_cell(x, y, &Cell::new_grapheme(g, pen.clone()));
|
||||
// the max(1) here is to ensure that we advance to the next cell
|
||||
// position for zero-width graphemes. We want to make sure that
|
||||
// they occupy a cell so that we can re-emit them when we output them.
|
||||
// If we didn't do this, then we'd effectively filter them out from
|
||||
// the model, which seems like a lossy design choice.
|
||||
cell.width().max(1)
|
||||
};
|
||||
|
||||
self.clear_selection_if_intersects(
|
||||
x..x + print_width,
|
||||
y as ScrollbackOrVisibleRowIndex,
|
||||
);
|
||||
|
||||
if x + print_width < width {
|
||||
self.cursor.x += print_width;
|
||||
self.wrap_next = false;
|
||||
} else {
|
||||
self.wrap_next = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform(&mut self, action: Action) {
|
||||
match action {
|
||||
Action::Print(c) => self.print(c),
|
||||
@ -1444,33 +1503,12 @@ impl<'a> Performer<'a> {
|
||||
|
||||
/// Draw a character to the screen
|
||||
fn print(&mut self, c: char) {
|
||||
if self.wrap_next {
|
||||
self.new_line(true);
|
||||
}
|
||||
|
||||
let x = self.cursor.x;
|
||||
let y = self.cursor.y;
|
||||
let width = self.screen().physical_cols;
|
||||
|
||||
let pen = self.pen.clone();
|
||||
|
||||
// Assign the cell and extract its printable width
|
||||
let print_width = {
|
||||
let cell = self.screen_mut().set_cell(x, y, c, &pen);
|
||||
cell.width()
|
||||
};
|
||||
|
||||
self.clear_selection_if_intersects(x..x + print_width, y as ScrollbackOrVisibleRowIndex);
|
||||
|
||||
if x + print_width < width {
|
||||
self.cursor.x += print_width;
|
||||
self.wrap_next = false;
|
||||
} else {
|
||||
self.wrap_next = true;
|
||||
}
|
||||
// We buffer up the chars to increase the chances of correctly grouping graphemes into cells
|
||||
self.print.get_or_insert_with(String::new).push(c);
|
||||
}
|
||||
|
||||
fn control(&mut self, control: ControlCode) {
|
||||
self.flush_print();
|
||||
match control {
|
||||
ControlCode::LineFeed | ControlCode::VerticalTab | ControlCode::FormFeed => {
|
||||
self.new_line(true /* TODO: depend on terminal mode */)
|
||||
@ -1488,6 +1526,7 @@ impl<'a> Performer<'a> {
|
||||
}
|
||||
|
||||
fn csi_dispatch(&mut self, csi: CSI) {
|
||||
self.flush_print();
|
||||
match csi {
|
||||
CSI::Sgr(sgr) => self.state.perform_csi_sgr(sgr),
|
||||
CSI::Cursor(cursor) => self.state.perform_csi_cursor(cursor, self.host),
|
||||
@ -1502,6 +1541,7 @@ impl<'a> Performer<'a> {
|
||||
}
|
||||
|
||||
fn esc_dispatch(&mut self, esc: Esc) {
|
||||
self.flush_print();
|
||||
match esc {
|
||||
Esc::Code(EscCode::StringTerminator) => {
|
||||
// String Terminator (ST); explicitly has nothing to do here, as its purpose is
|
||||
@ -1528,6 +1568,7 @@ impl<'a> Performer<'a> {
|
||||
}
|
||||
|
||||
fn osc_dispatch(&mut self, osc: OperatingSystemCommand) {
|
||||
self.flush_print();
|
||||
match osc {
|
||||
OperatingSystemCommand::SetIconNameAndWindowTitle(title)
|
||||
| OperatingSystemCommand::SetWindowTitle(title) => {
|
||||
|
Loading…
Reference in New Issue
Block a user