From 2ffb815341c9c97568d4553745eda8e470f5c37a Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 6 Dec 2019 21:25:41 -0500 Subject: handle line wrapping with wide characters + combining characters --- src/row.rs | 50 +++++++++++++++++++++++++++-- tests/data/fixtures/combining.in | 1 + tests/data/fixtures/combining/14.json | 17 ++++++++++ tests/data/fixtures/combining/14.typescript | 1 + 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/data/fixtures/combining/14.json create mode 100644 tests/data/fixtures/combining/14.typescript diff --git a/src/row.rs b/src/row.rs index 8f96c27..a0d44a4 100644 --- a/src/row.rs +++ b/src/row.rs @@ -191,8 +191,30 @@ impl Row { prev_attrs = *attrs; } - contents.extend(cell.contents().as_bytes()); prev_pos.col += if cell.is_wide() { 2 } else { 1 }; + if prev_pos.col >= self.cols() + && !self.wrapped + && cell.is_wide() + && cell.contents().chars().count() > 1 + { + // alternately, we could backspace enough to overwrite + // the second to last character, then ICH and rewrite + // the second to last character and then reposition, + // but that's a lot more complicated and not sure if + // it's worth it for this much of an edge case + let cell_contents = cell.contents(); + let mut chars = cell_contents.chars(); + let base = chars.next().unwrap(); + let mut bytes = [0; 4]; + contents.extend(base.encode_utf8(&mut bytes).bytes()); + crate::term::Backspace::default().write_buf(contents); + for c in chars { + contents + .extend(c.encode_utf8(&mut bytes).bytes()); + } + } else { + contents.extend(cell.contents().as_bytes()); + } } else if erase.is_none() { erase = Some((pos.col, attrs)); } @@ -216,6 +238,7 @@ impl Row { // while it's true that most of the logic in this is identical to // write_contents_formatted, i can't figure out how to break out the // common parts without making things noticeably slower. + #[allow(clippy::too_many_lines)] pub fn write_contents_diff( &self, contents: &mut Vec, @@ -312,8 +335,31 @@ impl Row { prev_attrs = *attrs; } - contents.extend(cell.contents().as_bytes()); prev_pos.col += if cell.is_wide() { 2 } else { 1 }; + if prev_pos.col >= self.cols() + && !self.wrapped + && cell.is_wide() + && cell.contents().chars().count() > 1 + { + // alternately, we could backspace enough to overwrite + // the second to last character, then ICH and rewrite + // the second to last character and then reposition, + // but that's a lot more complicated and not sure if + // it's worth it for this much of an edge case + let cell_contents = cell.contents(); + let mut chars = cell_contents.chars(); + let base = chars.next().unwrap(); + let mut bytes = [0; 4]; + contents.extend(base.encode_utf8(&mut bytes).bytes()); + crate::term::Backspace::default().write_buf(contents); + prev_pos.col -= 1; + for c in chars { + contents + .extend(c.encode_utf8(&mut bytes).bytes()); + } + } else { + contents.extend(cell.contents().as_bytes()); + } } else if erase.is_none() { erase = Some((pos.col, attrs)); } diff --git a/tests/data/fixtures/combining.in b/tests/data/fixtures/combining.in index b9c62ca..5f62920 100644 --- a/tests/data/fixtures/combining.in +++ b/tests/data/fixtures/combining.in @@ -11,3 +11,4 @@ a \u{0301} \x1b[H\x1b[Ja\u{0301} \x1b[H\x1b[J\x1b[2C\u{0301} +\x1b[H\x1b[J\x1b[1;79Ha\x08\u{301} diff --git a/tests/data/fixtures/combining/14.json b/tests/data/fixtures/combining/14.json new file mode 100644 index 0000000..bdc1fe2 --- /dev/null +++ b/tests/data/fixtures/combining/14.json @@ -0,0 +1,17 @@ +{ + "contents": " á", + "cells": { + "0,78": { + "contents": "á", + "is_wide": true + }, + "0,79": { + "contents": "", + "is_wide_continuation": true + } + }, + "cursor_position": [ + 0, + 79 + ] +} \ No newline at end of file diff --git a/tests/data/fixtures/combining/14.typescript b/tests/data/fixtures/combining/14.typescript new file mode 100644 index 0000000..513bc90 --- /dev/null +++ b/tests/data/fixtures/combining/14.typescript @@ -0,0 +1 @@ +á \ No newline at end of file -- cgit v1.2.3-54-g00ecf