diff --git a/filepicker/filepicker.go b/filepicker/filepicker.go index 1d2a1cc23..45bc7fd94 100644 --- a/filepicker/filepicker.go +++ b/filepicker/filepicker.go @@ -385,11 +385,15 @@ func (m Model) View() string { size := strings.Replace(humanize.Bytes(uint64(info.Size())), " ", "", 1) //nolint:gosec name := f.Name() + isDir := f.IsDir() if isSymlink { symlinkPath, _ = filepath.EvalSymlinks(filepath.Join(m.CurrentDirectory, name)) + if target, err := os.Stat(symlinkPath); err == nil && target.IsDir() { + isDir = true + } } - disabled := !m.canSelect(name) && !f.IsDir() + disabled := !m.canSelect(name) && !isDir if m.selected == i { //nolint:nestif selected := "" @@ -495,7 +499,10 @@ func (m Model) didSelectFile(msg tea.Msg) (bool, string) { } } - if (!isDir && m.FileAllowed) || (isDir && m.DirAllowed) && m.Path != "" { + if isDir { + return false, "" + } + if m.FileAllowed && m.Path != "" { return true, m.Path } diff --git a/textinput/textinput.go b/textinput/textinput.go index 363089b2d..df83e0d74 100644 --- a/textinput/textinput.go +++ b/textinput/textinput.go @@ -921,8 +921,7 @@ func (m Model) Cursor() *tea.Cursor { w := lipgloss.Width promptWidth := w(m.promptView()) - xOffset := m.Position() + - promptWidth + xOffset := promptWidth + uniseg.StringWidth(string(m.value[:m.pos])) if m.width > 0 { xOffset = min(xOffset, m.width+promptWidth) } diff --git a/textinput/textinput_test.go b/textinput/textinput_test.go index b5e344b99..3e223c2cf 100644 --- a/textinput/textinput_test.go +++ b/textinput/textinput_test.go @@ -118,3 +118,22 @@ func sendString(m Model, str string) Model { return m } + +func TestCursorWideCharacterOffset(t *testing.T) { + t.Parallel() + + m := New() + m.Prompt = "" + m.Focus() + m.SetVirtualCursor(false) + m.SetValue("你好") + m.SetCursor(len([]rune("你好"))) + + c := m.Cursor() + if c == nil { + t.Fatal("expected cursor, got nil") + } + if c.Position.X != 4 { + t.Fatalf("expected cursor X offset 4 for two wide runes, got %d", c.Position.X) + } +}