aboutsummaryrefslogtreecommitdiffstats
path: root/crates
diff options
context:
space:
mode:
author依云 <lilydjwg@gmail.com>2026-03-31 11:05:38 +0800
committerGitHub <noreply@github.com>2026-03-31 04:05:38 +0100
commit6e185ade97de688cb2a8aebe6c590d29be56c66c (patch)
treee47ab458c81e39c013dd5cbc0361d761ab8c2137 /crates
parentfix: resolve git worktrees to main repo in workspace filter (#3366) (diff)
downloadatuin-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.rs33
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 {