From c3e07c9b364d40046941fb2c176ec30b70a558dc Mon Sep 17 00:00:00 2001 From: Florian Rey Date: Mon, 17 Jun 2024 07:13:33 +0200 Subject: [PATCH 1/7] fix: progress default spring option --- progress/progress.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/progress/progress.go b/progress/progress.go index fa04635..defa981 100644 --- a/progress/progress.go +++ b/progress/progress.go @@ -187,13 +187,15 @@ func New(opts ...Option) Model { PercentFormat: " %3.0f%%", colorProfile: termenv.ColorProfile(), } - if !m.springCustomized { - m.SetSpringOptions(defaultFrequency, defaultDamping) - } for _, opt := range opts { opt(&m) } + + if !m.springCustomized { + m.SetSpringOptions(defaultFrequency, defaultDamping) + } + return m } From 36baf3d64ee64d2768c65c516701493d9a271232 Mon Sep 17 00:00:00 2001 From: Gabriel Fu Date: Tue, 18 Jun 2024 23:45:04 +0800 Subject: [PATCH 2/7] Update table.go (#539) --- table/table.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/table/table.go b/table/table.go index 0bdd6d7..3b8d3e2 100644 --- a/table/table.go +++ b/table/table.go @@ -431,7 +431,7 @@ func (m *Model) renderRow(r int) string { if m.styleFunc != nil { cellStyle = m.styleFunc(r, i, value) if r == m.cursor { - cellStyle.Inherit(m.styles.Selected) + cellStyle = cellStyle.Inherit(m.styles.Selected) } } else { cellStyle = m.styles.Cell From 64a67d167062e075d80a132afc0851fd1b2c6b89 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Tue, 18 Jun 2024 16:15:15 -0400 Subject: [PATCH 3/7] chore: update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a19189e..d2b0513 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -3,7 +3,7 @@ cursor/ @maaslalani filepicker/ @maaslalani help/ @meowgorithm key/ @meowgorithm -list/ @muesli +list/ @meowgorithm paginator/ @maaslalani progress/ @meowgorithm spinner/ @meowgorithm From 1a3627eb98a47b0fcad93c244ef139e11b97676d Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Tue, 16 Jul 2024 20:46:52 -0400 Subject: [PATCH 4/7] feat(textarea): inherit styles from base (and elsewhere) This reduces the amount of styling needed for what's likely more common use cases, such as using Styles.Base to apply a background color to the entire field. --- textarea/textarea.go | 67 ++++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/textarea/textarea.go b/textarea/textarea.go index 0e868e0..479c81d 100644 --- a/textarea/textarea.go +++ b/textarea/textarea.go @@ -135,6 +135,37 @@ type Style struct { Text lipgloss.Style } +func (s Style) computedCursorLine() lipgloss.Style { + return s.CursorLine.Inherit(s.Base).Inline(true) +} + +func (s Style) computedCursorLineNumber() lipgloss.Style { + return s.CursorLineNumber. + Inherit(s.CursorLine). + Inherit(s.Base). + Inline(true) +} + +func (s Style) computedEndOfBuffer() lipgloss.Style { + return s.EndOfBuffer.Inherit(s.Base).Inline(true) +} + +func (s Style) computedLineNumber() lipgloss.Style { + return s.LineNumber.Inherit(s.Base).Inline(true) +} + +func (s Style) computedPlaceholder() lipgloss.Style { + return s.Placeholder.Inherit(s.Base).Inline(true) +} + +func (s Style) computedPrompt() lipgloss.Style { + return s.Prompt.Inherit(s.Base).Inline(true) +} + +func (s Style) computedText() lipgloss.Style { + return s.Text.Inherit(s.Base).Inline(true) +} + // line is the input to the text wrapping function. This is stored in a struct // so that it can be hashed and memoized. type line struct { @@ -1068,7 +1099,7 @@ func (m Model) View() string { if m.Value() == "" && m.row == 0 && m.col == 0 && m.Placeholder != "" { return m.placeholderView() } - m.Cursor.TextStyle = m.style.CursorLine + m.Cursor.TextStyle = m.style.computedCursorLine() var s strings.Builder var style lipgloss.Style @@ -1081,29 +1112,29 @@ func (m Model) View() string { wrappedLines := m.memoizedWrap(line, m.width) if m.row == l { - style = m.style.CursorLine + style = m.style.computedCursorLine() } else { - style = m.style.Text + style = m.style.computedText() } for wl, wrappedLine := range wrappedLines { prompt := m.getPromptString(displayLine) - prompt = m.style.Prompt.Render(prompt) + prompt = m.style.computedPrompt().Render(prompt) s.WriteString(style.Render(prompt)) displayLine++ if m.ShowLineNumbers { if wl == 0 { if m.row == l { - s.WriteString(style.Render(m.style.CursorLineNumber.Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) + s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) } else { - s.WriteString(style.Render(m.style.LineNumber.Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) + s.WriteString(style.Render(m.style.computedLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) } } else { if m.row == l { - s.WriteString(style.Render(m.style.CursorLineNumber.Render(fmt.Sprintf(m.lineNumberFormat, " ")))) + s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, " ")))) } else { - s.WriteString(style.Render(m.style.LineNumber.Render(fmt.Sprintf(m.lineNumberFormat, " ")))) + s.WriteString(style.Render(m.style.computedLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, " ")))) } } } @@ -1144,11 +1175,11 @@ func (m Model) View() string { // To do this we can simply pad out a few extra new lines in the view. for i := 0; i < m.height; i++ { prompt := m.getPromptString(displayLine) - prompt = m.style.Prompt.Render(prompt) + prompt = m.style.computedPrompt().Render(prompt) s.WriteString(prompt) displayLine++ - s.WriteString(m.style.EndOfBuffer.Render(string(m.EndOfBufferCharacter))) + s.WriteString(m.style.computedEndOfBuffer().Render(string(m.EndOfBufferCharacter))) s.WriteRune('\n') } @@ -1174,7 +1205,7 @@ func (m Model) placeholderView() string { var ( s strings.Builder p = m.Placeholder - style = m.style.Placeholder.Inline(true) + style = m.style.computedPlaceholder() ) // word wrap lines @@ -1185,16 +1216,16 @@ func (m Model) placeholderView() string { plines := strings.Split(strings.TrimSpace(pwrap), "\n") for i := 0; i < m.height; i++ { - lineStyle := m.style.Placeholder - lineNumberStyle := m.style.LineNumber + lineStyle := m.style.computedPlaceholder() + lineNumberStyle := m.style.computedLineNumber() if len(plines) > i { - lineStyle = m.style.CursorLine - lineNumberStyle = m.style.CursorLineNumber + lineStyle = m.style.computedCursorLine() + lineNumberStyle = m.style.computedCursorLineNumber() } // render prompt prompt := m.getPromptString(i) - prompt = m.style.Prompt.Render(prompt) + prompt = m.style.computedPrompt().Render(prompt) s.WriteString(lineStyle.Render(prompt)) // when show line numbers enabled: @@ -1218,7 +1249,7 @@ func (m Model) placeholderView() string { // first line case i == 0: // first character of first line as cursor with character - m.Cursor.TextStyle = m.style.Placeholder + m.Cursor.TextStyle = m.style.computedPlaceholder() m.Cursor.SetChar(string(plines[0][0])) s.WriteString(lineStyle.Render(m.Cursor.View())) @@ -1232,7 +1263,7 @@ func (m Model) placeholderView() string { } default: // end of line buffer character - eob := m.style.EndOfBuffer.Render(string(m.EndOfBufferCharacter)) + eob := m.style.computedEndOfBuffer().Render(string(m.EndOfBufferCharacter)) s.WriteString(eob) } From 95d338b099d541c8d5467730bfa4e6e33a55ac0c Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 17 Jul 2024 08:49:49 -0400 Subject: [PATCH 5/7] feat(textarea): dynamically determine line number integer padding --- textarea/textarea.go | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/textarea/textarea.go b/textarea/textarea.go index 479c81d..227fb51 100644 --- a/textarea/textarea.go +++ b/textarea/textarea.go @@ -266,9 +266,6 @@ type Model struct { // vertically such that we can maintain the same navigating position. lastCharOffset int - // lineNumberFormat is the format string used to display line numbers. - lineNumberFormat string - // viewport is the vertically-scrollable viewport of the multi-line text // input. viewport *viewport.Model @@ -299,11 +296,10 @@ func New() Model { Cursor: cur, KeyMap: DefaultKeyMap, - value: make([][]rune, minHeight, defaultMaxHeight), - focus: false, - col: 0, - row: 0, - lineNumberFormat: "%3v ", + value: make([][]rune, minHeight, defaultMaxHeight), + focus: false, + col: 0, + row: 0, viewport: &vp, } @@ -1101,11 +1097,12 @@ func (m Model) View() string { } m.Cursor.TextStyle = m.style.computedCursorLine() - var s strings.Builder - var style lipgloss.Style - lineInfo := m.LineInfo() - - var newLines int + var ( + s strings.Builder + style lipgloss.Style + newLines int + lineInfo = m.LineInfo() + ) displayLine := 0 for l, line := range m.value { @@ -1126,15 +1123,15 @@ func (m Model) View() string { if m.ShowLineNumbers { if wl == 0 { if m.row == l { - s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) + s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(l + 1)))) } else { - s.WriteString(style.Render(m.style.computedLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, l+1)))) + s.WriteString(style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(l + 1)))) } } else { if m.row == l { - s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, " ")))) + s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(" ")))) } else { - s.WriteString(style.Render(m.style.computedLineNumber().Render(fmt.Sprintf(m.lineNumberFormat, " ")))) + s.WriteString(style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(" ")))) } } } @@ -1187,6 +1184,15 @@ func (m Model) View() string { return m.style.Base.Render(m.viewport.View()) } +// formatLineNumber formats the line number for display dynamically based on +// the maximum number of lines +func (m Model) formatLineNumber(x any) string { + // XXX: ultimately we should use a max buffer height, which has yet to be + // implemented. + digits := len(strconv.Itoa(m.MaxHeight)) + return fmt.Sprintf(" %*v ", digits, x) +} + func (m Model) getPromptString(displayLine int) (prompt string) { prompt = m.Prompt if m.promptFunc == nil { @@ -1240,7 +1246,7 @@ func (m Model) placeholderView() string { ln = strconv.Itoa(i + 1) fallthrough case len(plines) > i: - s.WriteString(lineStyle.Render(lineNumberStyle.Render(fmt.Sprintf(m.lineNumberFormat, ln)))) + s.WriteString(lineStyle.Render(lineNumberStyle.Render(m.formatLineNumber(ln)))) default: } } From c30c185b9304035d4f9d28ae6edaf1b0e4f2f076 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 17 Jul 2024 08:58:29 -0400 Subject: [PATCH 6/7] feat(textarea): end of buffer lines span the full block This is for styling purposes, particularly when you want to apply a background to the entire textarea. --- textarea/textarea.go | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/textarea/textarea.go b/textarea/textarea.go index 227fb51..2356131 100644 --- a/textarea/textarea.go +++ b/textarea/textarea.go @@ -1098,10 +1098,11 @@ func (m Model) View() string { m.Cursor.TextStyle = m.style.computedCursorLine() var ( - s strings.Builder - style lipgloss.Style - newLines int - lineInfo = m.LineInfo() + s strings.Builder + style lipgloss.Style + newLines int + widestLineNumber int + lineInfo = m.LineInfo() ) displayLine := 0 @@ -1120,22 +1121,33 @@ func (m Model) View() string { s.WriteString(style.Render(prompt)) displayLine++ + var ln string if m.ShowLineNumbers { if wl == 0 { if m.row == l { - s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(l + 1)))) + ln = style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(l + 1))) + s.WriteString(ln) } else { - s.WriteString(style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(l + 1)))) + ln = style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(l + 1))) + s.WriteString(ln) } } else { if m.row == l { - s.WriteString(style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(" ")))) + ln = style.Render(m.style.computedCursorLineNumber().Render(m.formatLineNumber(" "))) + s.WriteString(ln) } else { - s.WriteString(style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(" ")))) + ln = style.Render(m.style.computedLineNumber().Render(m.formatLineNumber(" "))) + s.WriteString(ln) } } } + // Note the widest line number for padding purposes later. + lnw := lipgloss.Width(ln) + if lnw > widestLineNumber { + widestLineNumber = lnw + } + strwidth := uniseg.StringWidth(string(wrappedLine)) padding := m.width - strwidth // If the trailing space causes the line to be wider than the @@ -1176,7 +1188,11 @@ func (m Model) View() string { s.WriteString(prompt) displayLine++ - s.WriteString(m.style.computedEndOfBuffer().Render(string(m.EndOfBufferCharacter))) + // Write end of buffer content + leftGutter := string(m.EndOfBufferCharacter) + rightGapWidth := m.Width() - lipgloss.Width(leftGutter) + widestLineNumber + rightGap := strings.Repeat(" ", max(0, rightGapWidth)) + s.WriteString(m.style.computedEndOfBuffer().Render(leftGutter + rightGap)) s.WriteRune('\n') } From a9344b59531d1b947978485dea07c5597299889a Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Wed, 17 Jul 2024 09:02:38 -0400 Subject: [PATCH 7/7] chore(textarea): lift an if condition into a parent loop --- textarea/textarea.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/textarea/textarea.go b/textarea/textarea.go index 2356131..153f39e 100644 --- a/textarea/textarea.go +++ b/textarea/textarea.go @@ -767,10 +767,7 @@ func (m *Model) wordRight() { func (m *Model) doWordRight(fn func(charIdx int, pos int)) { // Skip spaces forward. - for { - if m.col < len(m.value[m.row]) && !unicode.IsSpace(m.value[m.row][m.col]) { - break - } + for m.col >= len(m.value[m.row]) || unicode.IsSpace(m.value[m.row][m.col]) { if m.row == len(m.value)-1 && m.col == len(m.value[m.row]) { // End of text. break