Skip to content

Commit b3002ea

Browse files
committed
textarea: support secondary prompts
The secondary prompt is displayed on every line after the first. See for example: [![asciicast](https://asciinema.org/a/iFBPBwoDZOzcoRJOgfmPk8ObH.svg)](https://asciinema.org/a/iFBPBwoDZOzcoRJOgfmPk8ObH)
1 parent 93e3c75 commit b3002ea

File tree

1 file changed

+41
-5
lines changed

1 file changed

+41
-5
lines changed

textarea/textarea.go

+41-5
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ type Style struct {
109109
LineNumber lipgloss.Style
110110
Placeholder lipgloss.Style
111111
Prompt lipgloss.Style
112+
NextPrompt lipgloss.Style
112113
Text lipgloss.Style
113114
}
114115

@@ -123,6 +124,10 @@ type Model struct {
123124
EndOfBufferCharacter rune
124125
KeyMap KeyMap
125126

127+
// NextPrompt, if set, is used for all lines
128+
// except the first.
129+
NextPrompt string
130+
126131
// Styling. FocusedStyle and BlurredStyle are used to style the textarea in
127132
// focused and blurred states.
128133
FocusedStyle Style
@@ -219,6 +224,7 @@ func DefaultStyles() (Style, Style) {
219224
LineNumber: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "249", Dark: "7"}),
220225
Placeholder: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
221226
Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
227+
NextPrompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
222228
Text: lipgloss.NewStyle(),
223229
}
224230
blurred := Style{
@@ -229,6 +235,7 @@ func DefaultStyles() (Style, Style) {
229235
LineNumber: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "249", Dark: "7"}),
230236
Placeholder: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
231237
Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
238+
NextPrompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
232239
Text: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "245", Dark: "7"}),
233240
}
234241

@@ -682,7 +689,7 @@ func (m *Model) SetWidth(w int) {
682689
// Account for base style borders and padding.
683690
inputWidth -= m.style.Base.GetHorizontalFrameSize()
684691

685-
inputWidth -= rw.StringWidth(m.Prompt)
692+
inputWidth -= max(rw.StringWidth(m.Prompt), rw.StringWidth(m.NextPrompt))
686693
m.width = clamp(inputWidth, minWidth, maxWidth)
687694
}
688695

@@ -848,6 +855,11 @@ func (m Model) View() string {
848855

849856
var newLines int
850857

858+
prompt, nextPrompt := m.getPromptStrings()
859+
prompt = m.style.Prompt.Render(prompt)
860+
nextPrompt = m.style.NextPrompt.Render(nextPrompt)
861+
862+
firstDisplayLine := true
851863
for l, line := range m.value {
852864
wrappedLines := wrap(line, m.width)
853865

@@ -858,7 +870,12 @@ func (m Model) View() string {
858870
}
859871

860872
for wl, wrappedLine := range wrappedLines {
861-
s.WriteString(style.Render(m.style.Prompt.Render(m.Prompt)))
873+
selectedPrompt := nextPrompt
874+
if firstDisplayLine {
875+
selectedPrompt = prompt
876+
firstDisplayLine = false
877+
}
878+
s.WriteString(style.Render(selectedPrompt))
862879

863880
if m.ShowLineNumbers {
864881
if wl == 0 {
@@ -907,7 +924,7 @@ func (m Model) View() string {
907924
// Always show at least `m.Height` lines at all times.
908925
// To do this we can simply pad out a few extra new lines in the view.
909926
for i := 0; i < m.height; i++ {
910-
s.WriteString(m.style.Prompt.Render(m.Prompt))
927+
s.WriteString(nextPrompt)
911928

912929
if m.ShowLineNumbers {
913930
lineNumber := m.style.EndOfBuffer.Render((fmt.Sprintf(m.lineNumberFormat, string(m.EndOfBufferCharacter))))
@@ -920,6 +937,22 @@ func (m Model) View() string {
920937
return m.style.Base.Render(m.viewport.View())
921938
}
922939

940+
func (m Model) getPromptStrings() (prompt, nextPrompt string) {
941+
prompt = m.Prompt
942+
nextPrompt = m.NextPrompt
943+
if nextPrompt == "" {
944+
return prompt, prompt
945+
}
946+
pl := rw.StringWidth(prompt)
947+
npl := rw.StringWidth(nextPrompt)
948+
if pl > npl {
949+
nextPrompt = fmt.Sprintf("%*s", pl-npl, "") + nextPrompt
950+
} else if npl > pl {
951+
prompt = fmt.Sprintf("%*s", npl-pl, "") + prompt
952+
}
953+
return prompt, nextPrompt
954+
}
955+
923956
// placeholderView returns the prompt and placeholder view, if any.
924957
func (m Model) placeholderView() string {
925958
var (
@@ -928,7 +961,9 @@ func (m Model) placeholderView() string {
928961
style = m.style.Placeholder.Inline(true)
929962
)
930963

931-
prompt := m.style.Prompt.Render(m.Prompt)
964+
prompt, nextPrompt := m.getPromptStrings()
965+
966+
prompt = m.style.Prompt.Render(prompt)
932967
s.WriteString(m.style.CursorLine.Render(prompt))
933968

934969
if m.ShowLineNumbers {
@@ -943,9 +978,10 @@ func (m Model) placeholderView() string {
943978
s.WriteString(m.style.CursorLine.Render(style.Render(p[1:] + strings.Repeat(" ", max(0, m.width-rw.StringWidth(p))))))
944979

945980
// The rest of the new lines
981+
nextPrompt = m.style.NextPrompt.Render(nextPrompt)
946982
for i := 1; i < m.height; i++ {
947983
s.WriteRune('\n')
948-
s.WriteString(prompt)
984+
s.WriteString(nextPrompt)
949985

950986
if m.ShowLineNumbers {
951987
eob := m.style.EndOfBuffer.Render((fmt.Sprintf(m.lineNumberFormat, string(m.EndOfBufferCharacter))))

0 commit comments

Comments
 (0)