Repository: helix-editor/helix (language: rust)

Issue:
reproducible panic on `goto_file` (`gf`) if document contains non ASCII
### Summary

It seems  that the presence of characters like `🏴‍☠️` in the document messes up offsets used by `goto_file`. The more of `🏴‍☠️` there is in the document the more to the right of the cursor the offset goes. If the document is small enough (relative to the amount of `🏴‍☠️`) this causes a panic inside ropey, otherwise `goto_file` opens incorrect paths (text that happens to be to the right of the cursor at an offset proportional to the amount of `🏴‍☠️`).

### Reproduction Steps

<!-- Ideally provide a key sequence and/or asciinema.org recording. --> 

I tried this ([asciinema](https://asciinema.org/a/A0jSP2aEzCdyw10BxWVSFQ7FE)):

1. `hx`
2. Paste `🏴‍☠️` into scratch buffer
3.  Append `a`, so you have `🏴‍☠️a` on the first line
4. Put cursor on `a`
5. `gf` (`goto_file`)

I expected this to happen:

Go to file `a`.

Instead, this happened:

Panic.


### Helix log


stderr:

```
thread 'main' panicked at /build/helix-25.01.1-vendor.tar.gz/ropey/src/rope.rs:952:36:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic
   3: core::option::unwrap_failed
   4: helix_term::commands::goto_file_impl
   5: helix_term::ui::editor::EditorView::handle_keymap_event::{{closure}}
   6: helix_term::ui::editor::EditorView::handle_keymap_event
   7: <helix_term::ui::editor::EditorView as helix_term::compositor::Component>::handle_event
   8: helix_term::compositor::Compositor::handle_event
   9: hx::main_impl::{{closure}}
  10: tokio::runtime::park::CachedParkThread::block_on
  11: tokio::runtime::context::runtime::enter_runtime
  12: tokio::runtime::runtime::Runtime::block_on
  13: hx::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

<details><summary>helix.log (doesn't seem useful)</summary>

```
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/Dockerfile.*", re: "(?-u)^.*/Dockerfile\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('D'), Literal('o'), Literal('c'), Literal('k'), Literal('e'), Literal('r'), Literal('f'), Literal('i'), Literal('l'), Literal('e'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/dockerfile.*", re: "(?-u)^.*/dockerfile\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('d'), Literal('o'), Literal('c'), Literal('k'), Literal('e'), Literal('r'), Literal('f'), Literal('i'), Literal('l'), Literal('e'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/Containerfile.*", re: "(?-u)^.*/Containerfile\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('C'), Literal('o'), Literal('n'), Literal('t'), Literal('a'), Literal('i'), Literal('n'), Literal('e'), Literal('r'), Literal('f'), Literal('i'), Literal('l'), Literal('e'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/containerfile.*", re: "(?-u)^.*/containerfile\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('c'), Literal('o'), Literal('n'), Literal('t'), Literal('a'), Literal('i'), Literal('n'), Literal('e'), Literal('r'), Literal('f'), Literal('i'), Literal('l'), Literal('e'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/.*ignore", re: "(?-u)^.*/\\..*ignore$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('.'), ZeroOrMore, Literal('i'), Literal('g'), Literal('n'), Literal('o'), Literal('r'), Literal('e')]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/BUILD.*", re: "(?-u)^.*/BUILD\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('B'), Literal('U'), Literal('I'), Literal('L'), Literal('D'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/.env.*", re: "(?-u)^.*/\\.env\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('.'), Literal('e'), Literal('n'), Literal('v'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/.envrc.*", re: "(?-u)^.*/\\.envrc\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('.'), Literal('e'), Literal('n'), Literal('v'), Literal('r'), Literal('c'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/conf/*/*.{inc,conf}", re: "(?-u)^.*/conf/.*/.*\\.(?:conf|inc)$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('c'), Literal('o'), Literal('n'), Literal('f'), Literal('/'), ZeroOrMore, Literal('/'), ZeroOrMore, Literal('.'), Alternates([Tokens([Literal('c'), Literal('o'), Literal('n'), Literal('f')]), Tokens([Literal('i'), Literal('n'), Literal('c')])])]) }
2025-01-25T17:27:40.135 globset [DEBUG] glob converted to regex: Glob { glob: "*/Jenkinsfile.*", re: "(?-u)^.*/Jenkinsfile\\..*$", opts: GlobOptions { case_insensitive: false, literal_separator: false, backslash_escape: true, empty_alternates: false }, tokens: Tokens([ZeroOrMore, Literal('/'), Literal('J'), Literal('e'), Literal('n'), Literal('k'), Literal('i'), Literal('n'), Literal('s'), Literal('f'), Literal('i'), Literal('l'), Literal('e'), Literal('.'), ZeroOrMore]) }
2025-01-25T17:27:40.135 globset [DEBUG] built glob set; 1 literals, 0 basenames, 0 extensions, 0 prefixes, 145 suffixes, 11 required extensions, 10 regexes
2025-01-25T17:27:40.139 mio::poll [TRACE] registering event source with poller: token=Token(94893756129024), interests=READABLE | WRITABLE
2025-01-25T17:27:40.139 mio::poll [TRACE] registering event source with poller: token=Token(94893753422464), interests=READABLE | WRITABLE
2025-01-25T17:27:40.139 mio::poll [TRACE] registering event source with poller: token=Token(0), interests=READABLE
2025-01-25T17:27:40.139 mio::poll [TRACE] registering event source with poller: token=Token(1), interests=READABLE
2025-01-25T17:27:40.142 helix_tui::backend::crossterm [DEBUG] The keyboard enhancement protocol is supported in this terminal (checked in 3.063499ms)
2025-01-25T17:27:40.142 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 0
2025-01-25T17:27:40.390 helix_term::application [DEBUG] received editor event: IdleTimer
2025-01-25T17:27:40.488 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 0
2025-01-25T17:27:40.524 helix_term::application [DEBUG] received editor event: Redraw
2025-01-25T17:27:40.524 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 0
2025-01-25T17:27:40.558 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 1
2025-01-25T17:27:40.728 helix_term::commands [TRACE] entering insert mode with sel: Selection { ranges: [Range { anchor: 1, head: 5, old_visual_position: None }], primary_index: 0 }, text: "\n🏴\u{200d}☠\u{fe0f}"
2025-01-25T17:27:40.728 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 1
2025-01-25T17:27:40.850 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 1
2025-01-25T17:27:40.943 helix_term::application [DEBUG] received editor event: Redraw
2025-01-25T17:27:40.944 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 1
2025-01-25T17:27:40.976 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 1
2025-01-25T17:27:41.096 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.336 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.458 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.481 helix_term::application [DEBUG] received editor event: Redraw
2025-01-25T17:27:41.481 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.584 helix_core::auto_pairs [TRACE] autopairs hook selection: Selection {
    ranges: [
        Range {
            anchor: 4,
            head: 4,
            old_visual_position: None,
        },
    ],
    primary_index: 0,
}
2025-01-25T17:27:41.584 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.707 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 2
2025-01-25T17:27:41.768 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 3
2025-01-25T17:27:41.928 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 3
2025-01-25T17:27:42.032 helix_view::document [DEBUG] id 1 modified - last saved: 0, current: 3
2025-01-25T17:27:42.216 mio::poll [TRACE] deregistering event source from poller
2025-01-25T17:27:42.216 mio::poll [TRACE] deregistering event source from poller
```

</details>


### Platform

Linux

### Terminal Emulator

kitty 0.38.1 created by Kovid Goyal

### Installation Method

nixpkgs

### Helix Version

helix 25.01.1 (e7ac2fcd)

Also reproducible in current `master` (de738bac6ac1d44e4a24e6d1ad7d9f1c7ae2f61e).


Discussion / hints:

Fix patch:
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index ad37df4064ab..24a35aada3a4 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1303,7 +1303,7 @@ fn goto_file_impl(cx: &mut Context, action: Action) {
         let search_end = text
             .line_to_byte(text.byte_to_line(pos) + 1)
             .min(pos + 1000);
-        let search_range = text.slice(search_start..search_end);
+        let search_range = text.byte_slice(search_start..search_end);
         // we also allow paths that are next to the cursor (can be ambiguous but
         // rarely so in practice) so that gf on quoted/braced path works (not sure about this
         // but apparently that is how gf has worked historically in helix)


Test patch: