Repository: eza-community/eza (language: rust)

Issue:
bug: Alignment issue with long cyrillic names
`eza --version` : `v0.15.0 [+git]`. Running `eza --icons=auto --color=auto --group-directories-first`. OS: Archlinux on AMD64.

![image](https://github.com/eza-community/eza/assets/60626727/d7a4d3db-4879-4d3d-90cd-98fa6e092c14)

Used version of `eza` from commit bba97b5a835997e00f05971948a371149a17b403 that should have fixed this, but, sadly, it didn't


Discussion / hints:
Does it work for non-cyrillic names for you? What terminal, font? Do you have ligatures enabled?
> Does it work for non-cyrillic names for you? What terminal, font? Do you have ligatures enabled?

Alignment is fine for non-cyrillic names. Terminal is WezTerm, font is `IosevkaTerm Nerd Font`, ligatures are off.
Also, for some reason after renaming problematic directories grid was layouted differently

Alignment after rename (grapheme cluster length is the same):
![image](https://github.com/eza-community/eza/assets/60626727/1b2b6ef9-5b7d-41b1-b0aa-298ccdf84d9f)

Grid layout before rename:
![image](https://github.com/eza-community/eza/assets/60626727/6cfac3ab-338d-4dc5-9f03-60b4904e5162)

Grid layout after rename:
![image](https://github.com/eza-community/eza/assets/60626727/a9703303-98e2-4c97-9e8e-34e86de78b17)

UPD: the last screenshot was wrong
I suspect that eza or it's dependencies use string length in bytes instead of grapheme cluster length or other Unicode-aware length function to calculate the alignment, but i have zero knowledge of Rust to confirm that
> I suspect that eza or it's dependencies use string length in bytes instead of grapheme cluster length to calculate the alignment, but i have zero knowledge of Rust to confirm that

That sounds plausible, looking at the definition of length `.len()` of a `String` in the standard library:

> Returns the length of this String, in bytes, not [char](https://doc.rust-lang.org/std/primitive.char.html)s or graphemes. In other words, it might not be what a human considers the length of the string. [1]

[1]: https://doc.rust-lang.org/std/string/struct.String.html#method.len
interesting.. we stuck with our own (rustadopt) fork of ansiterm instead of moving to nu_ansi_term specifically because of the api change and how exa calculated the width of unicode variable width characters.
I'm curious to see where this regressed exactly
> interesting.. we stuck with our own (rustadopt) fork of ansiterm instead of moving to nu_ansi_term specifically because of the api change and how exa calculated the width of unicode variable width characters. I'm curious to see where this regressed exactly

I'm going to try and build `exa`, maybe this is a bug since `exa` times
This was not a thing in `exa`
![image](https://github.com/eza-community/eza/assets/60626727/49d8673c-4bf5-4506-a7bb-68c780d7b808)

UPD: Also, the bug in `eza` is still there with `--no-quotes`, so someone definitely ruined the file/directory name length calculation somewhere
I believe I have found the problem.. 

At some point, `*contents.width()`, which is the exact function that made us fork `ansiterm-rs`, which calculates the actual width of a unicode string in terms of an `ANSI` string, for options where `icons`, `hyperlinks`, `quotes` are enabled,  was replaced with `filename.bare_width()` which is just the `string.len()`

There is this: 
```
// the actual *contents.width() is larger than actually needed, so we take only the filename
```

I'm currently testing moving back to the previous option. Hopefully there will be no other regressions caused by it.

EDIT Update: 
I tested some Cyrillic filenames with spaces, and despite reverting everything to the previous width calculation, there are still serious issues with the spacing when those characters are introduced, especially with spaces/quotes. Will continue to look into this. 
@PThorpe92 I attempted to bisect the bad commit. Seems like it broke with commit 45857f0cf0526effcb6dd77f2af6376592323137
@PThorpe92 Yep, it's the icons that somehow break alignment. Cannot reproduce on latest version with `--icons=never`
what the hell...

that certainly wasn't the commit I was expecting :eyes:  

Thanks for the tip that should make it much easier to solve
Hi, it seems there is problem with other characteras as fractions:
![image](https://github.com/eza-community/eza/assets/53124818/f7d981f6-3988-43b0-a996-931a58117c35)

the problem seems to be with `--icons`
![image](https://github.com/eza-community/eza/assets/53124818/d88405d6-40e1-4983-b70c-58120a6e2faa)

>the problem seems to be with --icons

yeah that's what was discovered..

>Yep, it's the icons that somehow break alignment. Cannot reproduce on latest version with --icons=never

Fix patch:
diff --git a/src/output/file_name.rs b/src/output/file_name.rs
index edd88df4a..2b0f768e7 100644
--- a/src/output/file_name.rs
+++ b/src/output/file_name.rs
@@ -2,6 +2,7 @@ use std::fmt::Debug;
 use std::path::Path;
 
 use ansiterm::{ANSIString, Style};
+use unicode_width::UnicodeWidthStr;
 
 use crate::fs::mounts::MountedFs;
 use crate::fs::{File, FileTarget};
@@ -443,8 +444,8 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
     }
 
     /// For grid's use, to cover the case of hyperlink escape sequences
-    pub fn bare_width(&self) -> usize {
-        self.file.name.len()
+    pub fn bare_utf8_width(&self) -> usize {
+        UnicodeWidthStr::width(self.file.name.as_str())
     }
 }
 
diff --git a/src/output/grid.rs b/src/output/grid.rs
index 2986e4514..4e6b2ff1c 100644
--- a/src/output/grid.rs
+++ b/src/output/grid.rs
@@ -71,20 +71,20 @@ impl<'a> Render<'a> {
                     EmbedHyperlinks::On,
                     ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing),
                 ) => {
-                    filename.bare_width()
+                    filename.bare_utf8_width()
                         + classification_width
                         + 1
                         + (spacing as usize)
                         + space_filename_offset
                 }
                 (EmbedHyperlinks::On, ShowIcons::Never) => {
-                    filename.bare_width() + classification_width + space_filename_offset
+                    filename.bare_utf8_width() + classification_width + space_filename_offset
                 }
                 (
                     EmbedHyperlinks::Off,
                     ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing),
                 ) => {
-                    filename.bare_width()
+                    filename.bare_utf8_width()
                         + classification_width
                         + 1
                         + (spacing as usize)
diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs
index 113c75895..3f2044212 100644
--- a/src/output/grid_details.rs
+++ b/src/output/grid_details.rs
@@ -190,13 +190,13 @@ impl<'a> Render<'a> {
                     filename.options.show_icons,
                 ) {
                     (EmbedHyperlinks::On, ShowIcons::Automatic(spacing)) => {
-                        filename.bare_width() + 1 + (spacing as usize) + space_filename_offset
+                        filename.bare_utf8_width() + 1 + (spacing as usize) + space_filename_offset
                     }
                     (EmbedHyperlinks::On, ShowIcons::Always(spacing)) => {
-                        filename.bare_width() + 1 + (spacing as usize) + space_filename_offset
+                        filename.bare_utf8_width() + 1 + (spacing as usize) + space_filename_offset
                     }
                     (EmbedHyperlinks::On, ShowIcons::Never) => {
-                        filename.bare_width() + space_filename_offset
+                        filename.bare_utf8_width() + space_filename_offset
                     }
                     (EmbedHyperlinks::Off, _) => *contents.width(),
                 };


Test patch: