diff options
| author | 依云 <lilydjwg@gmail.com> | 2026-03-31 11:05:38 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-31 04:05:38 +0100 |
| commit | 6e185ade97de688cb2a8aebe6c590d29be56c66c (patch) | |
| tree | e47ab458c81e39c013dd5cbc0361d761ab8c2137 /crates | |
| parent | fix: resolve git worktrees to main repo in workspace filter (#3366) (diff) | |
| download | atuin-6e185ade97de688cb2a8aebe6c590d29be56c66c.zip | |
fix(ui): make preview line breaking algorithm aware of CJK double-width characters (#3360)
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [x] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [x] I have checked that there are no existing pull requests for the
same thing
Or part of the command may be missing and the user may be misled to run
a wrong command.
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/atuin/src/command/client/search/interactive.rs | 33 |
1 files changed, 21 insertions, 12 deletions
diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index 74600520..2910d129 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -11,7 +11,7 @@ use eyre::Result; use futures_util::FutureExt; use semver::Version; use time::OffsetDateTime; -use unicode_width::UnicodeWidthStr; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use super::{ cursor::Cursor, @@ -1239,18 +1239,27 @@ impl State { let command = if results.is_empty() { String::new() } else { - use itertools::Itertools as _; let s = &results[selected].command; - s.split('\n') - .flat_map(|line| { - line.char_indices() - .step_by(preview_width.into()) - .map(|(i, _)| i) - .chain(Some(line.len())) - .tuple_windows() - .map(|(a, b)| (&line[a..b]).escape_control().to_string()) - }) - .join("\n") + let mut lines = Vec::new(); + for line in s.split('\n') { + let line = line.escape_control(); + let mut width = 0; + let mut start = 0; + for (idx, ch) in line.char_indices() { + let w = ch.width().unwrap_or(0); // None for control chars which should not happen + if width + w > preview_width.into() { + lines.push(line[start..idx].to_owned()); + start = idx; + width = w; + } else { + width += w; + } + } + if width != 0 { + lines.push(line[start..].to_owned()); + } + } + lines.join("\n") }; match compactness { |
