mirror of
https://github.com/charmbracelet/bubbles.git
synced 2026-07-20 18:49:28 -06:00
Merge 7ac13af7886591ff4b15effd5f43ff578ecc2728 into 492ed7d1a49c1102022e47d5bc5bf03b6a79921d
This commit is contained in:
commit
e632274f97
@ -14,6 +14,7 @@ import (
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/atotto/clipboard"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
rw "github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
@ -745,35 +746,27 @@ func (m Model) placeholderView() string {
|
||||
render = styles.Placeholder.Render
|
||||
)
|
||||
|
||||
p := make([]rune, m.Width()+1)
|
||||
copy(p, []rune(m.Placeholder))
|
||||
|
||||
m.virtualCursor.TextStyle = styles.Placeholder
|
||||
m.virtualCursor.SetChar(string(p[:1]))
|
||||
v += m.virtualCursor.View()
|
||||
|
||||
// If the entire placeholder is already set and no padding is needed, finish
|
||||
if m.Width() < 1 && len(p) <= 1 {
|
||||
return styles.Prompt.Render(m.Prompt) + v
|
||||
graphemes := uniseg.NewGraphemes(m.Placeholder)
|
||||
if !graphemes.Next() {
|
||||
m.virtualCursor.TextStyle = styles.Placeholder
|
||||
m.virtualCursor.SetChar(" ")
|
||||
return styles.Prompt.Render(m.Prompt) + m.virtualCursor.View()
|
||||
}
|
||||
|
||||
// If Width is set then size placeholder accordingly
|
||||
if m.Width() > 0 {
|
||||
// available width is width - len + cursor offset of 1
|
||||
minWidth := lipgloss.Width(m.Placeholder)
|
||||
availWidth := m.Width() - minWidth + 1
|
||||
first := graphemes.Str()
|
||||
rest := m.Placeholder[len(first):]
|
||||
|
||||
// if width < len, 'subtract'(add) number to len and dont add padding
|
||||
if availWidth < 0 {
|
||||
minWidth += availWidth
|
||||
availWidth = 0
|
||||
}
|
||||
// append placeholder[len] - cursor, append padding
|
||||
v += render(string(p[1:minWidth]))
|
||||
v += render(strings.Repeat(" ", availWidth))
|
||||
m.virtualCursor.TextStyle = styles.Placeholder
|
||||
m.virtualCursor.SetChar(first)
|
||||
v += m.virtualCursor.View()
|
||||
|
||||
if m.Width() > 0 {
|
||||
remainingWidth := max(0, m.Width()-uniseg.StringWidth(first))
|
||||
rest = ansi.Truncate(rest, remainingWidth, "…")
|
||||
v += render(rest)
|
||||
v += render(strings.Repeat(" ", max(0, remainingWidth-uniseg.StringWidth(rest))))
|
||||
} else {
|
||||
// if there is no width, the placeholder can be any length
|
||||
v += render(string(p[1:]))
|
||||
v += render(rest)
|
||||
}
|
||||
|
||||
return styles.Prompt.Render(m.Prompt) + v
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
func Test_CurrentSuggestion(t *testing.T) {
|
||||
@ -49,31 +50,43 @@ func Test_SlicingOutsideCap(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestChinesePlaceholder(t *testing.T) {
|
||||
t.Skip("Skipping flaky test, the returned view seems incorrect. TODO: Needs investigation.")
|
||||
textinput := New()
|
||||
textinput.Placeholder = "输入消息..."
|
||||
textinput.SetWidth(20)
|
||||
|
||||
got := textinput.View()
|
||||
expected := "> 输入消息... "
|
||||
got := ansi.Strip(textinput.View())
|
||||
expected := "> 输入消息... "
|
||||
if got != expected {
|
||||
t.Fatalf("expected %q but got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlaceholderTruncate(t *testing.T) {
|
||||
t.Skip("Skipping flaky test, the returned view seems incorrect. TODO: Needs investigation.")
|
||||
textinput := New()
|
||||
textinput.Placeholder = "A very long placeholder, or maybe not so much"
|
||||
textinput.SetWidth(10)
|
||||
|
||||
got := textinput.View()
|
||||
expected := "> A very …"
|
||||
got := ansi.Strip(textinput.View())
|
||||
expected := "> A very lo…"
|
||||
if got != expected {
|
||||
t.Fatalf("expected %q but got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmojiPlaceholder(t *testing.T) {
|
||||
textinput := New()
|
||||
textinput.Placeholder = "🤔scope"
|
||||
textinput.SetWidth(20)
|
||||
|
||||
got := textinput.View()
|
||||
if strings.ContainsRune(got, '\x00') {
|
||||
t.Fatalf("expected placeholder view without null bytes, got %q", got)
|
||||
}
|
||||
if ansi.Strip(got) != "> 🤔scope " {
|
||||
t.Fatalf("unexpected emoji placeholder rendering: %q", ansi.Strip(got))
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleValidateFunc() {
|
||||
creditCardNumber := New()
|
||||
creditCardNumber.Placeholder = "4505 **** **** 1234"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user