helix icon indicating copy to clipboard operation
helix copied to clipboard

Crash while yanking text

Open Andersama opened this issue 1 year ago • 8 comments

Summary

Helix crashed as I yanked text on a left vertical split. Not sure that I can replicate it, it was after quite a while of editing. Was using the diagnostics menu several times over, <ctrl+o> to jump around. Getting used to editing with helix. Not familiar with rust so while I'd love to debug this myself, the error reads like a vector out of bounds assert failing in c++. The two files in vertical panes were different sizes I'd almost suspect something checked a selection offset against the size of the wrong file. I had been copy / pasting between the two panes but I hadn't yet learned the commands to switch between the panes so I was switching between the two using the mouse.

thread 'main' panicked at 'called Result::unwrap() on an Err value: Char index out of bounds: char index 175088, Rope/RopeSlice char length 47656', C:\Users\runneradmin.cargo\registry\src\github.com-1ecc6299db9ec823\ropey-1.5.0\src\slice.rs:349:41

Reproduction Steps

No response

Helix log

helix.log "project_name" is what I was working on last 2022-10-08T00:39:14.051 was the last timestamp

Platform

Windows

Terminal Emulator

cmd

Helix Version

helix 22.08.1 (66276ce6)

Andersama avatar Oct 09 '22 07:10 Andersama

Possibly related: #4157

Ordoviz avatar Oct 09 '22 20:10 Ordoviz

Fairly confident my initial guess was correct, I just took a quick look at the file sizes of the two files I was editing at the time. 47656 is roughly the size of the target file I was yanking from and the char index is around in the other file where I was editing.

I think I found the area that might be responsible for the bug, this appears to be the only reference to yank being called and it's to do with handling mouse events. There's likely a problem where the "current" view is not updated in time with the mouse click. It could be there's a missing mouse down handler or mouse up handler to make sure the last clicked pane is the "current" one used for the remaining function calls.

https://github.com/helix-editor/helix/blob/c15f1ea274d300feb23208324aa5b27d7274bebd/helix-term/src/ui/editor.rs#L1197

            MouseEventKind::Up(MouseButton::Left) => {
                if !config.middle_click_paste {
                    return EventResult::Ignored(None);
                }

                let (view, doc) = current!(cxt.editor);
               
                if doc
                    .selection(view.id)
                    .primary()
                    .slice(doc.text().slice(..))
                    .len_chars()
                    <= 1
                {
                    return EventResult::Ignored(None);
                }

                commands::MappableCommand::yank_main_selection_to_primary_clipboard.execute(cxt);

                EventResult::Consumed(None)
            }

Andersama avatar Oct 09 '22 22:10 Andersama

It might need a view.ensure_cursor_in_view(doc, scrolloff);

kirawi avatar Oct 10 '22 13:10 kirawi

I'm not entirely sure this would be something quick to fix, I might be wrong.

In theory, whatever happened to me should never happen*, because what it suggests is that the yank command (and I'm assuming other commands will likely have this problem as well) can try to run on what is effectively stale data. If I "yank" or do any command the command itself should be paired not only with an offset and size but with the panel it was executed on, so that when it executes later (as it seems selections don't carry this information and is instead merged with the "current" view) it can be sure to do exactly what the user intended at the time it was typed (or clicked). This would suggest select operations would likely need a smart pointer to refer to the panel and there needs to be some check that the "current" panel matches before any operation.

EG: I'd imagine there's already a bug which allows me to try to yank, some other thing closes a panel and then have the yank command run on the wrong panel and continue on like nothing happened. Provided every command can be properly undone and/or you've got version control maybe that's fine. But in the couple of days I've been playing around with vim and helix I've managed to do some incredibly destructive edits by accident so I can only imagine this bug causing someone a real headache in the future. Crashing also the way it did could also be a major pain point, if I wasn't already in the habit of saving my work as I go it could easily be that a crash like this undoes hours of someone's work.

Andersama avatar Oct 11 '22 00:10 Andersama

I think*, learning a bit of rust here something like:

    let selected = doc.selection(view.id);
    let ranges = selected.ranges();
    let out_of_bounds = ranges.iter().any(|&r| r.to() > text.len_bytes());

Might detect the problem before things go horribly wrong.

Andersama avatar Oct 12 '22 12:10 Andersama

@Andersama Thanks for looking into this, have you considered a pull request? I am sure someone will be able to help you with the finer details of Rust.

David-Else avatar Oct 26 '22 09:10 David-Else

I'm somewhat hoping / crossing fingers that someone can confirm the bug is fixed by something else. What I saw when looking at the code was a lot of areas which might need a check like this. I'd be worried writing a pr that I'd miss something. My code's also doing a brute force range check, that could have a performance impact.

I'm already getting my around rust, not a fan, but it's ok.

Andersama avatar Oct 29 '22 22:10 Andersama

I think this might be the cause of a few other issues* #3978 looks like a similar crash.

Andersama avatar Oct 29 '22 23:10 Andersama

This is still a problem, here is my stack trace:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Char index out of bounds: char index 2220, Rope/RopeSlice char length 589', /home/jummit/.local/share/cargo/registry/src/github.com-1ecc6299db9ec823/ropey-1.5.0/src/slice.rs:349:41
stack backtrace:
   0: rust_begin_unwind
             at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/panicking.rs:143:14
   2: core::result::unwrap_failed
             at /rustc/fe5b13d681f25ee6474be29d748c65adcd91f69e/library/core/src/result.rs:1785:5
   3: ropey::slice::RopeSlice::char_to_byte
   4: helix_core::graphemes::nth_prev_grapheme_boundary
   5: helix_core::selection::Range::grapheme_aligned
   6: helix_core::selection::Selection::ensure_invariants
   7: helix_view::document::Document::set_selection
   8: helix_term::commands::jump_backward
   9: helix_term::ui::editor::EditorView::handle_keymap_event::{{closure}}
  10: helix_term::ui::editor::EditorView::handle_keymap_event
  11: <helix_term::ui::editor::EditorView as helix_term::compositor::Component>::handle_event
  12: helix_term::compositor::Compositor::handle_event
  13: helix_term::application::Application::handle_terminal_events
  14: helix_term::application::Application::event_loop_until_idle::{{closure}}
  15: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
  16: std::thread::local::LocalKey<T>::with
  17: tokio::park::thread::CachedParkThread::block_on
  18: tokio::runtime::scheduler::multi_thread::MultiThread::block_on
  19: tokio::runtime::Runtime::block_on
  20: hx::main

Jummit avatar Nov 09 '22 10:11 Jummit

Have this same bug with a much smaller file: thread 'main' panicked at 'Attempt to slice past end of RopeSlice: slice end 465, RopeSlice length 463', C:\Users\Cameron\.cargo\registry\src\github.com-1ecc6299db9ec823\ropey-1.5.0\src\slice.rs:645:9

creikey avatar Nov 20 '22 23:11 creikey

I am fairly certain that #5790 fixes this so I am going to close this issue. If you are still having problems feel free to open a new issue.

pascalkuthe avatar Feb 11 '23 20:02 pascalkuthe

@pascalkuthe It sounds like from your patch that the issue was a form of desynchronization of the edit history. Without digging too deep into the source code, does the history of edits include which panel / screen the edit was made on? Because the reason I suspected there was some underlying bug was in part because keyboard actions should be somewhat processed instantly, therefore you really shouldn't have out of bounds errors given the gui's current state because it's already there. I didn't think about edit history which could make things interesting.

What happens say if you perform some actions -> they get logged into the edit history, then you remove a panel where an edit was made and you try to undo?

Andersama avatar May 07 '23 19:05 Andersama