gyscos / cursive

A Text User Interface library for the Rust programming language
MIT License
4.26k stars 243 forks source link

[BUG] cursive exits immediately when asked to display a string with a null byte #780

Closed janstarke closed 2 months ago

janstarke commented 4 months ago

Describe the bug

cursive programs exit immediately when asked to display a string with a null byte. No error message is shown, and there doesn't seem to be a panic

To Reproduce

MWE: This program should display a TextView, but instead it exists without any complaint.

use cursive::{views::TextView, Cursive, CursiveExt};

fn main() {
    let mut app = Cursive::new();
    app.add_layer(TextView::new("Hello Nullbyte: \0"));
    app.add_global_callback('q', |a| a.quit());
    app.run();
}

Expected behavior This program should display a TextView, or at least panic with an error message, so that users of cursive know that null bytes are not allowed

Screenshots no screenshot available

Environment

Additional context I found this while fixing janstarke/regview#8

correabuscar commented 4 months ago

Can confirm this happens with cursive 0.20 and with cursive's latest git commit 0a95c82c88c8a791d7fd983e7fb9f568b77551a8 (HEAD, origin/main, origin/HEAD, main)

However the exit(101) apparently happens due to ncurses-rs (a dep of cursive), or a dep of ncurses-rs that got updated(unlikely), because, if I just tell cursive to use version 6 of ncurses-rs and apply some compilation error patches (this and this for ncurses-rs, and this for cursive) which have nothing to do with the issue at hand(ie. they're not the ones fixing it), it will then compile and show no text, but definitely not exit. However to show any text, applying the cursive patch from here is enough.

Initially the program exit with exit code 101, no screenshot added. Then after telling cursive to use `ncurses-rs` version 6, it doesn't exit anymore but silently doesn't show the messages that contain `\0` aka nul bytes: ![empty](https://github.com/gyscos/cursive/assets/106607441/c838b56f-eefd-40d7-822e-ca56f609e740) Then after applying the `\0` patch from the draft PR: ![after_patch](https://github.com/gyscos/cursive/assets/106607441/4a43c1dd-dc37-4064-a2c3-f9046abdad92)
correabuscar commented 4 months ago

Further more I now see, running it like: $ RUST_BACKTRACE=1 cargo run >& /tmp/foo.log cat /tmp/foo.log shows:

    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/stuff`
thread 'main' panicked at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:65:29:
called `Result::unwrap()` on an `Err` value: NulError(16, [72, 101, 108, 108, 111, 32, 78, 117, 108, 108, 98, 121, 116, 101, 58, 32, 0])
stack backtrace:
   0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1649:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1073:23
   4: <&str as ncurses::ToCStr>::to_c_str
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:65:9
   5: ncurses::addstr
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:99:23
   6: ncurses::mvaddstr
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:736:3
   7: <cursive::backends::curses::n::Backend as cursive_core::backend::Backend>::print_at
             at /tmp/cursive/cursive/src/backends/curses/n.rs:397:9
   8: cursive_core::printer::Printer::print_with_width
             at /tmp/cursive/cursive-core/src/printer.rs:239:9
   9: cursive_core::printer::Printer::print
             at /tmp/cursive/cursive-core/src/printer.rs:159:9
  10: <cursive_core::views::text_view::TextView as cursive_core::view::view_trait::View>::draw::{{closure}}::{{closure}}
             at /tmp/cursive/cursive-core/src/views/text_view.rs:402:25
  11: cursive_core::printer::Printer::with_effects
             at /tmp/cursive/cursive-core/src/printer.rs:414:21
  12: cursive_core::printer::Printer::with_style::{{closure}}
             at /tmp/cursive/cursive-core/src/printer.rs:372:13
  13: cursive_core::printer::Printer::with_color
             at /tmp/cursive/cursive-core/src/printer.rs:339:9
  14: cursive_core::printer::Printer::with_style
             at /tmp/cursive/cursive-core/src/printer.rs:371:9
  15: <cursive_core::views::text_view::TextView as cursive_core::view::view_trait::View>::draw::{{closure}}
             at /tmp/cursive/cursive-core/src/views/text_view.rs:401:21
  16: cursive_core::printer::Printer::with_effects
             at /tmp/cursive/cursive-core/src/printer.rs:414:21
  17: cursive_core::printer::Printer::with_style::{{closure}}
             at /tmp/cursive/cursive-core/src/printer.rs:372:13
  18: cursive_core::printer::Printer::with_color
             at /tmp/cursive/cursive-core/src/printer.rs:339:9
  19: cursive_core::printer::Printer::with_style
             at /tmp/cursive/cursive-core/src/printer.rs:371:9
  20: <cursive_core::views::text_view::TextView as cursive_core::view::view_trait::View>::draw
             at /tmp/cursive/cursive-core/src/views/text_view.rs:389:9
  21: cursive_core::view::view_wrapper::ViewWrapper::wrap_draw::{{closure}}
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:54:28
  22: <cursive_core::views::boxed_view::BoxedView as cursive_core::view::view_wrapper::ViewWrapper>::with_view
             at /tmp/cursive/cursive-core/src/views/boxed_view.rs:66:14
  23: cursive_core::view::view_wrapper::ViewWrapper::wrap_draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:54:9
  24: cursive_core::view::view_wrapper::<impl cursive_core::view::view_trait::View for T>::draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:106:9
  25: <cursive_core::views::layer::Layer<T> as cursive_core::view::view_wrapper::ViewWrapper>::wrap_draw::{{closure}}
             at /tmp/cursive/cursive-core/src/views/layer.rs:50:13
  26: cursive_core::printer::Printer::with_color
             at /tmp/cursive/cursive-core/src/printer.rs:339:9
  27: <cursive_core::views::layer::Layer<T> as cursive_core::view::view_wrapper::ViewWrapper>::wrap_draw
             at /tmp/cursive/cursive-core/src/views/layer.rs:46:9
  28: cursive_core::view::view_wrapper::<impl cursive_core::view::view_trait::View for T>::draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:106:9
  29: <cursive_core::views::shadow_view::ShadowView<T> as cursive_core::view::view_wrapper::ViewWrapper>::wrap_draw
             at /tmp/cursive/cursive-core/src/views/shadow_view.rs:107:9
  30: cursive_core::view::view_wrapper::<impl cursive_core::view::view_trait::View for T>::draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:106:9
  31: <cursive_core::views::stack_view::ChildWrapper<T> as cursive_core::view::view_trait::View>::draw
             at /tmp/cursive/cursive-core/src/views/stack_view.rs:237:44
  32: cursive_core::view::view_wrapper::ViewWrapper::wrap_draw::{{closure}}
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:54:28
  33: <cursive_core::views::circular_focus::CircularFocus<T> as cursive_core::view::view_wrapper::ViewWrapper>::with_view
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:171:41
  34: cursive_core::view::view_wrapper::ViewWrapper::wrap_draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:54:9
  35: cursive_core::view::view_wrapper::<impl cursive_core::view::view_trait::View for T>::draw
             at /tmp/cursive/cursive-core/src/view/view_wrapper.rs:106:9
  36: cursive_core::views::stack_view::StackView::draw_fg::{{closure}}
             at /tmp/cursive/cursive-core/src/views/stack_view.rs:669:17
  37: cursive_core::printer::Printer::with_effects
             at /tmp/cursive/cursive-core/src/printer.rs:414:21
  38: cursive_core::printer::Printer::with_style::{{closure}}
             at /tmp/cursive/cursive-core/src/printer.rs:372:13
  39: cursive_core::printer::Printer::with_color
             at /tmp/cursive/cursive-core/src/printer.rs:339:9
  40: cursive_core::printer::Printer::with_style
             at /tmp/cursive/cursive-core/src/printer.rs:371:9
  41: cursive_core::views::stack_view::StackView::draw_fg
             at /tmp/cursive/cursive-core/src/views/stack_view.rs:665:9
  42: cursive_core::views::screens_view::ScreensView<cursive_core::views::stack_view::StackView>::draw_fg
             at /tmp/cursive/cursive-core/src/views/screens_view.rs:98:13
  43: cursive_core::cursive_root::Cursive::draw
             at /tmp/cursive/cursive-core/src/cursive_root.rs:148:9
  44: cursive_core::cursive_run::CursiveRunner<C>::draw
             at /tmp/cursive/cursive-core/src/cursive_run.rs:99:9
  45: cursive_core::cursive_run::CursiveRunner<C>::refresh
             at /tmp/cursive/cursive-core/src/cursive_run.rs:194:9
  46: cursive_core::cursive_run::CursiveRunner<C>::run
             at /tmp/cursive/cursive-core/src/cursive_run.rs:234:9
  47: cursive_core::cursive_root::Cursive::try_run_with
             at /tmp/cursive/cursive-core/src/cursive_root.rs:890:9
  48: <cursive_core::cursive_root::Cursive as cursive::cursive_ext::CursiveExt>::run_ncurses
             at /tmp/cursive/cursive/src/cursive_ext.rs:87:9
  49: <cursive_core::cursive_root::Cursive as cursive::cursive_ext::CursiveExt>::run
             at /tmp/cursive/cursive/src/cursive_ext.rs:76:17
  50: stuff::main
             at ./src/main.rs:7:5
  51: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

which means it got fixed by this commit: https://github.com/jeaye/ncurses-rs/commit/1be70212788a946446602d8d2c62ef8c8f48b708 (thanks @ThomasHabets btw!) (thus it's only in ncurses-rs v6, which for cursive has a PR for that here but it's waiting for the ncurses-rs one first which is here)

correabuscar commented 4 months ago

just making a note that this issue got fixed by a commit https://github.com/gyscos/cursive/commit/760999d300a83adfe2a24dcdfae21525818f27f3#diff-5ab05e635edf74924d76fd94ec7bd7712e5193641e3b12b77cc47662f6d33b64R186

The following no longer applies due to recent commits: nevermind, it still applies (i just forgot something)

ok but because of this https://github.com/unicode-rs/unicode-width/commit/4efb1803faa054f1bea3c0457275ad3c8610170b#diff-2ad10836ccce5ac2056d5679cc92449d9ff9094d4ff5c5803f65b5dd1d52ef19R224 and because it's no longer pinned to 0.1.12 as I thought it was the width of \0 is 1 instead of 0 now, so it will not "delete" \0-es, so it still happens, for example for:

$ cargo run --example select >& /tmp/foo.log || cat /tmp/foo.log
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/examples/select`
thread 'main' panicked at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:65:29:
called `Result::unwrap()` on an `Err` value: NulError(0, [0])
stack backtrace:
   0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1649:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1073:23
   4: <&str as ncurses::ToCStr>::to_c_str
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:65:9
   5: ncurses::addstr
             at /home/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ncurses-5.101.0/src/lib.rs:99:23
   6: <cursive::backends::curses::n::Backend as cursive_core::backend::Backend>::print
             at ./cursive/src/backends/curses/n.rs:401:9
   7: cursive_core::buffer::PrintBuffer::flush
             at ./cursive-core/src/buffer.rs:291:13
   8: cursive_core::cursive_run::CursiveRunner<C>::draw
             at ./cursive-core/src/cursive_run.rs:98:9
   9: cursive_core::cursive_run::CursiveRunner<C>::refresh
             at ./cursive-core/src/cursive_run.rs:193:9
  10: cursive_core::cursive_run::CursiveRunner<C>::run
             at ./cursive-core/src/cursive_run.rs:233:9
  11: cursive_core::cursive_root::Cursive::try_run_with
             at ./cursive-core/src/cursive_root.rs:898:9
  12: cursive::cursive_runnable::CursiveRunnable::try_run
             at ./cursive/src/cursive_runnable.rs:93:9
  13: cursive::cursive_runnable::CursiveRunnable::run
             at ./cursive/src/cursive_runnable.rs:88:9
  14: select::main
             at ./cursive/examples/select.rs:46:5
  15: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

oh I forgot to say here that I put a \0 in Adamstown in cities.txt

Was trying to find out how to make a test that would ensure no \0 exists in the strings in the view, by looking at this:

$ cargo test --example select_test -- --nocapture
    Finished test [unoptimized + debuginfo] target(s) in 0.04s
     Running unittests examples/select_test.rs (target/debug/examples/select_test-61955c01bf129bf6)

running 2 tests
captured piece:
x01234567890123456789012345678901234567890123456789012345678901234567890123456789x
0                                                                                |
1                                                                                |
2                           ┌┤ Where are you from? ├┐                            |
3                           │      Abidjan       ▒  │                            |
4                           │     Abu Dhabi      |  │                            |
5                           │       Abuja        |  │                            |
6                           │       Accra        |  │                            |
7                           │     Adamstown     |  │                            |
8                           │    Addis Ababa     |  │                            |
9                           │      Algiers       |  │                            |
0                           │       Alofi        |  │                            |
1                           │       Amman        |  │                            |
2                           │     Amsterdam      |  │                            |
3                           └───────────────────────┘                            |
4                                                                                |
5                                                                                |
x--------------------------------------------------------------------------------x

test tests::displays ... ok
captured piece:
x01234567890123456789012345678901234567890123456789012345678901234567890123456789x
0................................................................................|
1................................................................................|
2...........................                         ............................|
3...........................                          ...........................|
4...........................                          ...........................|
5.........................┌────────────────────────────┐.........................|
6.........................│ Abu.Dhabi is a.great.city! │ ........................|
7.........................│  .....          ..... ..   │ ........................|
8.........................│  ....     .     ....<Quit> │ ........................|
9.........................└────────────────────────────┘ ........................|
0..........................                          .   ........................|
1...........................                          ...........................|
2...........................                          ...........................|
3...........................                          ...........................|
4............................                         ...........................|
5................................................................................|
x--------------------------------------------------------------------------------x

test tests::interacts ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

(not actually)Fixed with:

diff --git a/cursive-core/src/buffer.rs b/cursive-core/src/buffer.rs
index ad35432..86f5c5c 100644
--- a/cursive-core/src/buffer.rs
+++ b/cursive-core/src/buffer.rs
@@ -183,7 +183,7 @@ impl PrintBuffer {
         // TODO: Use some WithWidth(&str, usize) to not re-compute width a thousand times
         for g in text.graphemes(true) {
             let width = g.width();
-            if width == 0 {
+            if (width == 0) || ("\0" == g) {
                 continue;
             }
             self.set_cell(pos, g, CellWidth::from_usize(width), style);

(see next comment)

correabuscar commented 4 months ago

@gyscos should I make a PR with the above patch for \0-es and add a test too? it might take me hours to figure out, but you could do it in minutes xD (frankly, I'd prefer the latter)

EDIT: Just realized that patch isn't good enough, something else also counts those \0-es, and ends up truncating the string from view. So the test must test for this case as well. For example:

captured piece:
x01234567890123456789012345678901234567890123456789012345678901234567890123456789x
0................................................................................|
1................................................................................|
2................................................................................|
3.............................    Wellington    .|...............................|
4................................West.Island.....................................|
5.................................Willemstad.....................................|
6..................................Windhoek......................................|
7................................Yamoussoukro....................................|
8................................  Yaoundé  .....................................|
9.................................. Yar.n .......................................|
0..................................Yerevan.......................................|
1...................................Zagreb.......................................|
2.............................short nul s  ......▒...............................|
3................................................................................|
4................................................................................|
5................................................................................|
x--------------------------------------------------------------------------------x

for this string:

select.add_item_str("\0\0\0\0\0short \0nul\0 str\0\0\0\0\0\0\0");

EDIT: Here's what I have so far:

diff --git a/cursive-core/src/buffer.rs b/cursive-core/src/buffer.rs
index fb772b9..68f86ca 100644
--- a/cursive-core/src/buffer.rs
+++ b/cursive-core/src/buffer.rs
@@ -183,7 +183,7 @@ impl PrintBuffer {
         // TODO: Use some WithWidth(&str, usize) to not re-compute width a thousand times
         for g in text.graphemes(true) {
             let width = g.width();
-            if width == 0 {
+            if (width == 0) || ("\0" == g) {
                 continue;
             }
             self.set_cell(pos, g, CellWidth::from_usize(width), style);
diff --git a/cursive/examples/select_test.rs b/cursive/examples/select_test.rs
index 4848f18..04f8c1e 100644
--- a/cursive/examples/select_test.rs
+++ b/cursive/examples/select_test.rs
@@ -40,6 +40,8 @@ pub mod tests {
             // (We include the file at compile-time to avoid runtime read errors.)
             let content = include_str!("assets/cities.txt");
             select.add_all_str(content.lines());
+            select.add_item_str("short \0nul\0 1str");
+            select.add_item_str("\0\0\0\0\0short \0nul\0 2str\0\0\0\0\0\0\0");

             // Sets the callback for when "Enter" is pressed.
             select.set_on_submit(show_next_window);
@@ -114,6 +116,24 @@ pub mod tests {
         assert_eq!(screen.find_occurences("Some random string").len(), 0);
     }

+    #[test]
+    fn no_inner_nuls() {
+        let mut s = BasicSetup::new();
+        s.hit_keystroke(Key::End);
+        let screen = s.last_screen().unwrap();
+        s.dump_debug();
+        assert_eq!(screen.find_occurences("short nul 1str").len(), 1, "nuls aka \\0 in strings are supposed to be deleted");
+    }
+
+    #[test]
+    fn dont_count_the_nuls() {
+        let mut s = BasicSetup::new();
+        s.hit_keystroke(Key::End);
+        let screen = s.last_screen().unwrap();
+        s.dump_debug();
+        assert_eq!(screen.find_occurences("short nul 2str").len(), 1, "nuls aka \\0 in strings are supposed to be disregarded when computing size of string");
+    }
+
     #[test]
     fn interacts() {
         let mut s = BasicSetup::new();

It only fails with unicode-width 0.1.13 but not with 0.1.12 because the former has 1 instead of 0 as the width of \0. Because of this, seems more complicated than I initially thought, I think I'll leave this up to you, as I've no idea how you'd want this handled now that \0-es have width 1. Well, technically you did mention how.

If we converted \0 to replacement char (\u{fffd} which had width 1) to work with 0.1.13 unicode-width crate(where \0 is of width 1), then it will fail with 0.1.12 crate, but I've something like this:

diff --git a/cursive-core/src/buffer.rs b/cursive-core/src/buffer.rs
index fb772b9..6cb374b 100644
--- a/cursive-core/src/buffer.rs
+++ b/cursive-core/src/buffer.rs
@@ -183,10 +183,16 @@ impl PrintBuffer {
         // TODO: Use some WithWidth(&str, usize) to not re-compute width a thousand times
         for g in text.graphemes(true) {
             let width = g.width();
+            //if (width == 0) || ("\0" == g) {
             if width == 0 {
                 continue;
             }
-            self.set_cell(pos, g, CellWidth::from_usize(width), style);
+            if "\0" == g {
+                assert_eq!(1,width,"\\0 should've had a width of 1");
+                self.set_cell(pos, "\u{fffd}", CellWidth::from_usize(width), style);
+            } else {
+                self.set_cell(pos, g, CellWidth::from_usize(width), style);
+            }
             pos.x += width;
         }
     }
diff --git a/cursive/examples/select_test.rs b/cursive/examples/select_test.rs
index 4848f18..9a97b7e 100644
--- a/cursive/examples/select_test.rs
+++ b/cursive/examples/select_test.rs
@@ -40,6 +40,7 @@ pub mod tests {
             // (We include the file at compile-time to avoid runtime read errors.)
             let content = include_str!("assets/cities.txt");
             select.add_all_str(content.lines());
+            select.add_item_str("short \0nul\0 1str");

             // Sets the callback for when "Enter" is pressed.
             select.set_on_submit(show_next_window);
@@ -114,6 +115,15 @@ pub mod tests {
         assert_eq!(screen.find_occurences("Some random string").len(), 0);
     }

+    #[test]
+    fn nuls_become_replacement_char() {
+        let mut s = BasicSetup::new();
+        s.hit_keystroke(Key::End);
+        let screen = s.last_screen().unwrap();
+        s.dump_debug();
+        assert_eq!(screen.find_occurences("short \u{fffd}nul\u{FFFD} 1str").len(), 1, "nuls aka \\0 in strings are supposed to be deleted");
+    }
+
     #[test]
     fn interacts() {
         let mut s = BasicSetup::new();
diff --git a/cursive-core/src/utils/lines/spans/tests.rs b/cursive-core/src/utils/lines/spans/tests.rs
index 73d2aa2..fb7ea5a 100644
--- a/cursive-core/src/utils/lines/spans/tests.rs
+++ b/cursive-core/src/utils/lines/spans/tests.rs
@@ -16,6 +16,12 @@ fn input() -> StyledString {
     text
 }

+#[test]
+fn test_nuls_have_width_1() {
+    use unicode_width::UnicodeWidthStr;
+    assert_eq!("\0".width(), 1, "nul chars should have width 1 since unicode-width 0.1.13, seen here: https://github.com/unicode-rs/unicode-width/commit/4efb1803faa054f1bea3c0457275ad3c8610170b#diff-2ad10836ccce5ac2056d5679cc92449d9ff9094d4ff5c5803f65b5dd1d52ef19R224");
+}
+
 #[test]
 fn test_next_line_char() {
     use unicode_width::UnicodeWidthStr;

I personally like this one with the replacement char, because the \0 becomes visible now. EDIT the quote at top of this https://github.com/unicode-rs/unicode-width/pull/45 suggests they(the nuls and more control chars) should be rendered(with fallback glyph if not interpreted), so this replacement char idea seems like a good one, but the implementation may be not be up to par here.

gyscos commented 4 months ago

The crash should now be fixed with the latest commit, which ignores \0 (when using unicode-width < 0.1.13), or replaces it with (when using unicode-width >= 0.1.13).

Note that on my terminal, the handling of is a bit buggy: it is treated as a single-width character, but is rendered as a double-width one: this is on Alacritty 0.13.2:

screenshot issue 780

The same happens on gnone-terminal. Might depend on the font used. This happens both with monospace and with Source code pro. Ah - with Ubuntu Mono it looks good! On a macOS terminal with the default font, it also looks properly single-width. :shrug:

gyscos commented 2 months ago

Fix has now be released. I'll close this, but feel free to re-open if you think you're still having this issue.