aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-22 14:30:16 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-22 14:30:16 +0300
commit4846cb1324a6a9e46bc89250cf8a289b91866a9c (patch)
treea083847aa50941799a31493266770ce58f3f4b19 /src
parentb76fb4950c30e9bb7836bcd54792066d077a1911 (diff)
fix: skip wide spacer cells in pane reads
refs #698
Diffstat (limited to 'src')
-rw-r--r--src/ghostty/mod.rs24
-rw-r--r--src/pane/terminal.rs21
2 files changed, 42 insertions, 3 deletions
diff --git a/src/ghostty/mod.rs b/src/ghostty/mod.rs
index 17f9af32..2b45e3d8 100644
--- a/src/ghostty/mod.rs
+++ b/src/ghostty/mod.rs
@@ -742,9 +742,11 @@ impl Terminal {
})
}
- pub fn screen_graphemes(&self, x: u16, y: u32) -> Result<Vec<u32>, Error> {
+ pub fn screen_cell(&self, x: u16, y: u32) -> Result<(CellWide, Vec<u32>), Error> {
let grid_ref = self.grid_ref(ghostty_screen_point(x, y))?;
- grid_ref_graphemes(&grid_ref)
+ let wide = grid_ref_wide(&grid_ref)?;
+ let graphemes = grid_ref_graphemes(&grid_ref)?;
+ Ok((wide, graphemes))
}
fn viewport_graphemes_and_style(&self, x: u16, y: u32) -> Result<(Vec<u32>, CellStyle), Error> {
@@ -1393,6 +1395,24 @@ fn grid_ref_graphemes(grid_ref: &ffi::GhosttyGridRef) -> Result<Vec<u32>, Error>
Ok(buffer)
}
+fn grid_ref_wide(grid_ref: &ffi::GhosttyGridRef) -> Result<CellWide, Error> {
+ let mut raw = ffi::GhosttyCell::default();
+ unsafe {
+ ffi::ghostty_grid_ref_cell(grid_ref, &mut raw).into_result()?;
+ }
+
+ let mut wide = ffi::GhosttyCellWide_GHOSTTY_CELL_WIDE_NARROW;
+ unsafe {
+ ffi::ghostty_cell_get(
+ raw,
+ ffi::GhosttyCellData_GHOSTTY_CELL_DATA_WIDE,
+ (&mut wide as *mut ffi::GhosttyCellWide).cast(),
+ )
+ .into_result()?;
+ }
+ Ok(CellWide::from_raw(wide))
+}
+
fn kitty_placement_u32(
iterator: ffi::GhosttyKittyGraphicsPlacementIterator,
data: ffi::GhosttyKittyGraphicsPlacementData,
diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs
index a87b9622..ed5d1061 100644
--- a/src/pane/terminal.rs
+++ b/src/pane/terminal.rs
@@ -1686,7 +1686,10 @@ fn ghostty_screen_row(
) -> Result<String, crate::ghostty::Error> {
let mut line = String::new();
for x in 0..cols {
- let graphemes = core.terminal.screen_graphemes(x, y)?;
+ let (wide, graphemes) = core.terminal.screen_cell(x, y)?;
+ if wide == crate::ghostty::CellWide::SpacerTail {
+ continue;
+ }
if graphemes.is_empty()
|| graphemes.first().copied() == Some(crate::ghostty::KITTY_UNICODE_PLACEHOLDER)
{
@@ -1715,6 +1718,9 @@ fn ghostty_line_from_cells(
fn ghostty_cell_symbol(
cells: &crate::ghostty::RowCellIter<'_>,
) -> Result<String, crate::ghostty::Error> {
+ if cells.wide()? == crate::ghostty::CellWide::SpacerTail {
+ return Ok(String::new());
+ }
let text = cells.grapheme_text()?;
if text.chars().next().map(u32::from) == Some(crate::ghostty::KITTY_UNICODE_PLACEHOLDER) {
return Ok(" ".to_string());
@@ -2954,6 +2960,19 @@ mod tests {
}
#[test]
+ fn plain_text_reads_skip_wide_character_spacer_cells() {
+ let (tx, _rx) = mpsc::channel(4);
+ let mut terminal = crate::ghostty::Terminal::new(40, 3, 100).unwrap();
+ terminal.write("日本語テスト ABC 123".as_bytes());
+ let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap();
+
+ assert_eq!(pane.visible_text(), "日本語テスト ABC 123\n");
+ assert_eq!(pane.recent_text(3), "日本語テスト ABC 123\n");
+ assert_eq!(pane.recent_unwrapped_text(3), "日本語テスト ABC 123");
+ assert_eq!(pane.detection_text(), "日本語テスト ABC 123\n");
+ }
+
+ #[test]
fn visible_ansi_preserves_cell_style_sequences() {
let (tx, _rx) = mpsc::channel(4);
let mut terminal = crate::ghostty::Terminal::new(20, 3, 100).unwrap();