fix(formatting): prevent crashes on files with multibyte characters (#51)
* Fix crash when truncating to middle of a character * Fix alignment of file names with wide characters * Respect use ::formatting convention
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -191,6 +191,7 @@ dependencies = [
|
|||||||
"structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"structopt 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"termion 1.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tui 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tui 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ jwalk = "0.5"
|
|||||||
signal-hook = "0.1.10"
|
signal-hook = "0.1.10"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
|
unicode-width = "0.1.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "0.16.0"
|
insta = "0.16.0"
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
|
use ::std::iter::FromIterator;
|
||||||
|
use ::unicode_width::UnicodeWidthChar;
|
||||||
|
|
||||||
|
fn truncate_iter_to_unicode_width<Input, Collect>(iter: Input, width: usize) -> Collect
|
||||||
|
where
|
||||||
|
Input: Iterator<Item = char>,
|
||||||
|
Collect: FromIterator<char>,
|
||||||
|
{
|
||||||
|
let mut chunk_width = 0;
|
||||||
|
iter.take_while(|ch| {
|
||||||
|
chunk_width += ch.width().unwrap_or(0);
|
||||||
|
chunk_width <= width
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn truncate_middle(row: &str, max_length: u16) -> String {
|
pub fn truncate_middle(row: &str, max_length: u16) -> String {
|
||||||
if max_length < 6 {
|
if max_length < 6 {
|
||||||
let mut res = String::from(row);
|
truncate_iter_to_unicode_width(row.chars(), max_length as usize)
|
||||||
res.truncate(max_length as usize);
|
|
||||||
res
|
|
||||||
} else if row.len() as u16 > max_length {
|
} else if row.len() as u16 > max_length {
|
||||||
let first_slice = &row[0..(max_length as usize / 2) - 2];
|
let split_point = (max_length as usize / 2) - 2;
|
||||||
let second_slice = &row[(row.len() - (max_length / 2) as usize + 2)..row.len()];
|
let first_slice = truncate_iter_to_unicode_width::<_, String>(row.chars(), split_point);
|
||||||
|
let second_slice =
|
||||||
|
truncate_iter_to_unicode_width::<_, Vec<_>>(row.chars().rev(), split_point)
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
if max_length % 2 == 0 {
|
if max_length % 2 == 0 {
|
||||||
format!("{}[...]{}", first_slice, second_slice)
|
format!("{}[...]{}", first_slice, second_slice)
|
||||||
} else {
|
} else {
|
||||||
@@ -25,3 +45,16 @@ pub fn truncate_end(row: &str, max_len: u16) -> String {
|
|||||||
row.to_string()
|
row.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncate_middle_char_boundary() {
|
||||||
|
assert_eq!(
|
||||||
|
truncate_middle("굿걸 - 누가 방송국을 털었나 E06.mp4", 44),
|
||||||
|
"굿걸 - 누가 방송국을[...]국을 털었나 E06.mp4",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use ::tui::buffer::Buffer;
|
use ::tui::buffer::Buffer;
|
||||||
use ::tui::layout::Rect;
|
use ::tui::layout::Rect;
|
||||||
use ::tui::style::{Color, Modifier, Style};
|
use ::tui::style::{Color, Modifier, Style};
|
||||||
|
use ::unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use crate::state::tiles::{FileType, Tile};
|
use crate::state::tiles::{FileType, Tile};
|
||||||
use crate::ui::format::{truncate_middle, DisplaySize, DisplaySizeRounded};
|
use crate::ui::format::{truncate_middle, DisplaySize, DisplaySizeRounded};
|
||||||
@@ -167,11 +168,11 @@ pub fn draw_filled_rect(buf: &mut Buffer, fill_style: Style, rect: &Rect) {
|
|||||||
|
|
||||||
pub fn draw_tile_text_on_grid(buf: &mut Buffer, tile: &Tile, selected: bool) {
|
pub fn draw_tile_text_on_grid(buf: &mut Buffer, tile: &Tile, selected: bool) {
|
||||||
let first_line = tile_first_line(&tile);
|
let first_line = tile_first_line(&tile);
|
||||||
let first_line_length = first_line.chars().count() as u16;
|
let first_line_length = first_line.width() as u16;
|
||||||
let first_line_start_position =
|
let first_line_start_position =
|
||||||
((tile.width - first_line_length) as f64 / 2.0).ceil() as u16 + tile.x;
|
((tile.width - first_line_length) as f64 / 2.0).ceil() as u16 + tile.x;
|
||||||
let second_line = tile_second_line(&tile);
|
let second_line = tile_second_line(&tile);
|
||||||
let second_line_length = second_line.chars().count();
|
let second_line_length = second_line.width();
|
||||||
let second_line_start_position =
|
let second_line_start_position =
|
||||||
((tile.width - second_line_length as u16) as f64 / 2.0).ceil() as u16 + tile.x;
|
((tile.width - second_line_length as u16) as f64 / 2.0).ceil() as u16 + tile.x;
|
||||||
let (background_style, first_line_style, second_line_style) = tile_style(&tile, selected);
|
let (background_style, first_line_style, second_line_style) = tile_style(&tile, selected);
|
||||||
|
|||||||
Reference in New Issue
Block a user