parent
e05bd5cb2a
commit
3cbe4ec793
|
|
@ -2546,63 +2546,51 @@ impl<'a> RowCellIter<'a> {
|
|||
}
|
||||
|
||||
pub fn grapheme_text(&self) -> Result<String, Error> {
|
||||
let mut bytes = Vec::new();
|
||||
let mut codepoints = Vec::new();
|
||||
let mut text = String::new();
|
||||
self.grapheme_text_into(&mut bytes, &mut text)?;
|
||||
self.grapheme_text_into(&mut codepoints, &mut text)?;
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
pub fn grapheme_text_into(&self, bytes: &mut Vec<u8>, text: &mut String) -> Result<(), Error> {
|
||||
pub fn grapheme_text_into(
|
||||
&self,
|
||||
codepoints: &mut Vec<u32>,
|
||||
text: &mut String,
|
||||
) -> Result<(), Error> {
|
||||
text.clear();
|
||||
bytes.clear();
|
||||
codepoints.clear();
|
||||
|
||||
let mut buffer = ffi::GhosttyBuffer {
|
||||
ptr: ptr::null_mut(),
|
||||
cap: 0,
|
||||
len: 0,
|
||||
};
|
||||
let result = unsafe {
|
||||
ffi::ghostty_render_state_row_cells_get(
|
||||
self.cells.raw,
|
||||
ffi::GhosttyRenderStateRowCellsData_GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_UTF8,
|
||||
(&mut buffer as *mut ffi::GhosttyBuffer).cast(),
|
||||
)
|
||||
};
|
||||
match result {
|
||||
ffi::GhosttyResult_GHOSTTY_SUCCESS if buffer.len == 0 => {
|
||||
return self.raw_cell_text_into(text);
|
||||
}
|
||||
ffi::GhosttyResult_GHOSTTY_SUCCESS => {
|
||||
return Err(Error(ffi::GhosttyResult_GHOSTTY_INVALID_VALUE));
|
||||
}
|
||||
ffi::GhosttyResult_GHOSTTY_OUT_OF_SPACE => {}
|
||||
other => return Err(Error(other)),
|
||||
}
|
||||
|
||||
if buffer.len == 0 {
|
||||
return self.raw_cell_text_into(text);
|
||||
}
|
||||
bytes.resize(buffer.len, 0);
|
||||
let mut buffer = ffi::GhosttyBuffer {
|
||||
ptr: bytes.as_mut_ptr(),
|
||||
cap: bytes.len(),
|
||||
len: 0,
|
||||
};
|
||||
let mut len = 0u32;
|
||||
unsafe {
|
||||
ffi::ghostty_render_state_row_cells_get(
|
||||
self.cells.raw,
|
||||
ffi::GhosttyRenderStateRowCellsData_GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_UTF8,
|
||||
(&mut buffer as *mut ffi::GhosttyBuffer).cast(),
|
||||
ffi::GhosttyRenderStateRowCellsData_GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_LEN,
|
||||
(&mut len as *mut u32).cast(),
|
||||
)
|
||||
.into_result()?;
|
||||
};
|
||||
|
||||
if len == 0 {
|
||||
return self.raw_cell_text_into(text);
|
||||
}
|
||||
|
||||
// Avoid GRAPHEMES_UTF8 here. In libghostty-vt 0.6.7's vendor, its
|
||||
// required-byte query can underreport multi-codepoint graphemes.
|
||||
codepoints.resize(len as usize, 0);
|
||||
unsafe {
|
||||
ffi::ghostty_render_state_row_cells_get(
|
||||
self.cells.raw,
|
||||
ffi::GhosttyRenderStateRowCellsData_GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF,
|
||||
codepoints.as_mut_ptr().cast(),
|
||||
)
|
||||
.into_result()?;
|
||||
}
|
||||
if buffer.len > bytes.len() {
|
||||
return Err(Error(ffi::GhosttyResult_GHOSTTY_OUT_OF_SPACE));
|
||||
}
|
||||
bytes.truncate(buffer.len);
|
||||
match std::str::from_utf8(bytes) {
|
||||
Ok(value) => text.push_str(value),
|
||||
Err(_) => text.push_str(&String::from_utf8_lossy(bytes)),
|
||||
|
||||
for codepoint in codepoints.iter().copied() {
|
||||
match char::from_u32(codepoint) {
|
||||
Some(ch) => text.push(ch),
|
||||
None => text.push(char::REPLACEMENT_CHARACTER),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2947,6 +2935,32 @@ mod tests {
|
|||
assert_eq!(render_state.dirty().unwrap(), Dirty::Clean);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_cells_handle_issue_453_unicode_payload() {
|
||||
let mut terminal = Terminal::new(80, 3, 100).unwrap();
|
||||
terminal.write("README 👨👩👧👦 🧑💻 ✅ ⚡ 漢字 café é 🏳️🌈 🚀\r\n".as_bytes());
|
||||
|
||||
let mut render_state = RenderState::new().unwrap();
|
||||
render_state.update(&terminal).unwrap();
|
||||
|
||||
let mut row_iterator = RowIterator::new().unwrap();
|
||||
let mut rows = render_state
|
||||
.populate_row_iterator(&mut row_iterator)
|
||||
.unwrap();
|
||||
let mut row_cells = RowCells::new().unwrap();
|
||||
let mut codepoints = Vec::new();
|
||||
let mut text = String::new();
|
||||
|
||||
while rows.next() {
|
||||
let mut cells = rows.populate_cells(&mut row_cells).unwrap();
|
||||
while cells.next() {
|
||||
cells
|
||||
.grapheme_text_into(&mut codepoints, &mut text)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_state_row_dirty_can_be_cleared_independently() {
|
||||
let mut terminal = Terminal::new(8, 3, 100).unwrap();
|
||||
|
|
|
|||
|
|
@ -1058,7 +1058,7 @@ impl GhosttyPaneTerminal {
|
|||
Ok(rows) => rows,
|
||||
Err(_) => return,
|
||||
};
|
||||
let mut grapheme_bytes = Vec::new();
|
||||
let mut grapheme_codepoints = Vec::new();
|
||||
let mut symbol_scratch = String::new();
|
||||
let mut y = 0u16;
|
||||
while y < area.height && rows.next() {
|
||||
|
|
@ -1081,7 +1081,7 @@ impl GhosttyPaneTerminal {
|
|||
&cells,
|
||||
basic.wide,
|
||||
hide_kitty_placeholders,
|
||||
&mut grapheme_bytes,
|
||||
&mut grapheme_codepoints,
|
||||
&mut symbol_scratch,
|
||||
) {
|
||||
Ok(symbol) => symbol,
|
||||
|
|
@ -1226,7 +1226,7 @@ fn ghostty_collect_dirty_patch(
|
|||
let Ok(mut rows) = render_state.populate_row_iterator(&mut row_iterator) else {
|
||||
fallback!("populate_rows_error");
|
||||
};
|
||||
let mut grapheme_bytes = Vec::new();
|
||||
let mut grapheme_codepoints = Vec::new();
|
||||
let mut symbol_scratch = String::new();
|
||||
let mut patch_rows = Vec::new();
|
||||
let mut y = 0u16;
|
||||
|
|
@ -1264,7 +1264,7 @@ fn ghostty_collect_dirty_patch(
|
|||
&cells,
|
||||
basic.wide,
|
||||
hide_kitty_placeholders,
|
||||
&mut grapheme_bytes,
|
||||
&mut grapheme_codepoints,
|
||||
&mut symbol_scratch,
|
||||
) {
|
||||
Ok(symbol) => symbol.to_owned(),
|
||||
|
|
@ -1539,7 +1539,7 @@ fn ghostty_buffer_symbol_into<'a>(
|
|||
cells: &crate::ghostty::RowCellIter<'_>,
|
||||
wide: crate::ghostty::CellWide,
|
||||
hide_kitty_placeholders: bool,
|
||||
grapheme_bytes: &mut Vec<u8>,
|
||||
grapheme_codepoints: &mut Vec<u32>,
|
||||
symbol_scratch: &'a mut String,
|
||||
) -> Result<&'a str, crate::ghostty::Error> {
|
||||
symbol_scratch.clear();
|
||||
|
|
@ -1547,7 +1547,7 @@ fn ghostty_buffer_symbol_into<'a>(
|
|||
crate::ghostty::CellWide::SpacerTail => {}
|
||||
crate::ghostty::CellWide::SpacerHead => symbol_scratch.push(' '),
|
||||
crate::ghostty::CellWide::Narrow | crate::ghostty::CellWide::Wide => {
|
||||
cells.grapheme_text_into(grapheme_bytes, symbol_scratch)?;
|
||||
cells.grapheme_text_into(grapheme_codepoints, symbol_scratch)?;
|
||||
let hidden_kitty_placeholder = hide_kitty_placeholders
|
||||
&& symbol_scratch.chars().next().map(u32::from)
|
||||
== Some(crate::ghostty::KITTY_UNICODE_PLACEHOLDER);
|
||||
|
|
|
|||
Loading…
Reference in New Issue