on main help sign in
fix(fsext): DirTrim renders wrong character for non-ASCII directory names (#3214)
commit188dea64e2e84468c4d2059e4e8ce2f580c2efdb authorZF <130743669+feizhuzheng@users.noreply.github.com> dateTue 30 Jun 2026, 03:35 (3 months ago) parentb39a85e
2 files changed+38−1
internal/fsext/fileutil.go+6−1 internal/fsext/fileutil_dirtrim_test.go+32
internal/fsext/fileutil.go+6−1
@@ -14,6 +14,7 @@ import (
1414 "github.com/charlievieth/fastwalk"
1515 "github.com/charmbracelet/crush/internal/csync"
1616 "github.com/charmbracelet/crush/internal/home"
17+ "github.com/charmbracelet/x/ansi"
1718)
1819
1920type FileInfo struct {
@@ -204,7 +205,11 @@ func DirTrim(pwd string, lim int) string {
204205 if i == len(dirs)-1 {
205206 out = dirs[i]
206207 } else if i >= len(dirs)-lim {
207− out = string(dirs[i][0]) + out
208+ // Keep the first grapheme cluster, not the first byte: CJK,
209+ // combining marks, and emoji can span multiple bytes and runes,
210+ // so a byte or single rune would render the wrong character.
211+ first, _ := ansi.FirstGraphemeCluster(dirs[i], ansi.GraphemeWidth)
212+ out = first + out
208213 } else {
209214 out = "..." + out
210215 break
internal/fsext/fileutil_dirtrim_test.gonew file+32
@@ -0,0 +1,32 @@
1+package fsext
2+
3+import (
4+ "path/filepath"
5+ "testing"
6+
7+ "github.com/stretchr/testify/require"
8+)
9+
10+func TestDirTrimUnicode(t *testing.T) {
11+ sep := string(filepath.Separator)
12+
13+ // An abbreviated component keeps its first character.
14+ require.Equal(t,
15+ filepath.Join("~", "...", "p", "file"),
16+ DirTrim(sep+"home"+sep+"user"+sep+"project"+sep+"file", 2),
17+ )
18+
19+ // A multi-byte (e.g. CJK) component must keep its first whole character,
20+ // not its first byte, which would render a wrong character.
21+ require.Equal(t,
22+ filepath.Join("~", "...", "项", "file"),
23+ DirTrim(sep+"home"+sep+"项目"+sep+"file", 2),
24+ )
25+
26+ // A grapheme cluster spanning multiple runes (a base letter plus a
27+ // combining accent) must be kept whole, not split after the base rune.
28+ require.Equal(t,
29+ filepath.Join("~", "...", "e\u0301", "file"),
30+ DirTrim(sep+"home"+sep+"e\u0301cole"+sep+"file", 2),
31+ )
32+}