Skip to content

Commit d1fc051

Browse files
authored
fix(pager): memory/cpu usage when using soft-wrap (#827)
easily reproducible, especially with something like ```sh PAGER="gum pager" man ls ``` Problems here were: - reflow's truncate is not very efficient - useless calls to truncate - bad strings.Replace calls (not necessary, and wouldn't work if text has ansi sequences)
1 parent e7c916c commit d1fc051

File tree

3 files changed

+26
-18
lines changed

3 files changed

+26
-18
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ require (
1414
github.com/charmbracelet/x/ansi v0.8.0
1515
github.com/charmbracelet/x/editor v0.1.0
1616
github.com/charmbracelet/x/term v0.2.1
17-
github.com/muesli/reflow v0.3.0
1817
github.com/muesli/roff v0.1.0
1918
github.com/muesli/termenv v0.15.3-0.20241211131612-0d230cb6eb15
2019
github.com/rivo/uniseg v0.4.7
@@ -40,6 +39,7 @@ require (
4039
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
4140
github.com/muesli/cancelreader v0.2.2 // indirect
4241
github.com/muesli/mango v0.2.0 // indirect
42+
github.com/muesli/reflow v0.3.0 // indirect
4343
github.com/yuin/goldmark v1.7.4 // indirect
4444
github.com/yuin/goldmark-emoji v1.0.4 // indirect
4545
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect

pager/pager.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/charmbracelet/bubbles/viewport"
1414
tea "github.com/charmbracelet/bubbletea"
1515
"github.com/charmbracelet/lipgloss"
16-
"github.com/muesli/reflow/truncate"
16+
"github.com/charmbracelet/x/ansi"
1717
)
1818

1919
type keymap struct {
@@ -146,17 +146,21 @@ func (m *model) processText(msg tea.WindowSizeMsg) {
146146
if m.showLineNumbers {
147147
text.WriteString(m.lineNumberStyle.Render(fmt.Sprintf("%4d │ ", i+1)))
148148
}
149-
for m.softWrap && lipgloss.Width(line) > m.maxWidth {
150-
truncatedLine := truncate.String(line, uint(m.maxWidth)) //nolint: gosec
151-
text.WriteString(textStyle.Render(truncatedLine))
152-
text.WriteString("\n")
153-
if m.showLineNumbers {
154-
text.WriteString(m.lineNumberStyle.Render(" │ "))
149+
idx := 0
150+
if w := ansi.StringWidth(line); m.softWrap && w > m.maxWidth {
151+
for w > idx {
152+
if m.showLineNumbers && idx != 0 {
153+
text.WriteString(m.lineNumberStyle.Render(" │ "))
154+
}
155+
truncatedLine := ansi.Cut(line, idx, m.maxWidth+idx)
156+
idx += m.maxWidth
157+
text.WriteString(textStyle.Render(truncatedLine))
158+
text.WriteString("\n")
155159
}
156-
line = strings.Replace(line, truncatedLine, "", 1)
160+
} else {
161+
text.WriteString(textStyle.Render(line)) //nolint: gosec
162+
text.WriteString("\n")
157163
}
158-
text.WriteString(textStyle.Render(truncate.String(line, uint(m.maxWidth)))) //nolint: gosec
159-
text.WriteString("\n")
160164
}
161165

162166
diffHeight := m.viewport.Height - lipgloss.Height(text.String())

pager/search.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/charmbracelet/bubbles/textinput"
99
"github.com/charmbracelet/gum/internal/utils"
1010
"github.com/charmbracelet/lipgloss"
11-
"github.com/muesli/reflow/truncate"
11+
"github.com/charmbracelet/x/ansi"
1212
)
1313

1414
type search struct {
@@ -150,14 +150,18 @@ func (s *search) PrevMatch(m *model) {
150150
func softWrapEm(str string, maxWidth int, softWrap bool) string {
151151
var text strings.Builder
152152
for _, line := range strings.Split(str, "\n") {
153-
for softWrap && lipgloss.Width(line) > maxWidth {
154-
truncatedLine := truncate.String(line, uint(maxWidth)) //nolint: gosec
155-
text.WriteString(truncatedLine)
153+
idx := 0
154+
if w := ansi.StringWidth(line); softWrap && w > maxWidth {
155+
for w > idx {
156+
truncatedLine := ansi.Cut(line, idx, maxWidth+idx)
157+
idx += maxWidth
158+
text.WriteString(truncatedLine)
159+
text.WriteString("\n")
160+
}
161+
} else {
162+
text.WriteString(line) //nolint: gosec
156163
text.WriteString("\n")
157-
line = strings.Replace(line, truncatedLine, "", 1)
158164
}
159-
text.WriteString(truncate.String(line, uint(maxWidth))) //nolint: gosec
160-
text.WriteString("\n")
161165
}
162166

163167
return text.String()

0 commit comments

Comments
 (0)