charmbracelet / bubbletea

A powerful little TUI framework 🏗
MIT License
25.65k stars 744 forks source link

Part of the screen is not rendering when the view is exactly the size of the screen #1004

Closed Art-S-D closed 2 months ago

Art-S-D commented 2 months ago

Describe the bug When the View method returns a string that has as many lines as the terminal, only the first lines of the screen will be redrawn. It seems to only happen when using a strings.Builder and goes away when there are more lines than the height of the terminal

Setup Please complete the following information along with version numbers, if applicable.

To Reproduce

Here is a minimal version of the code I use

package main

import (
    "os"
    "strings"

    tea "github.com/charmbracelet/bubbletea"
)

type model struct {
    screenWidth  int
    screenHeight int
    screenStart  int

    content []string
}

func (m *model) Init() tea.Cmd {
    return nil
}

// this one works
func (m *model) View2() string {
    out := m.content[m.screenStart : m.screenStart+m.screenHeight]
    return strings.Join(out, "\n")
}

// this one does not work
func (m *model) View() string {
    var sb strings.Builder

    // uncommenting this line fixes the bug
    // sb.WriteRune('\n')

    for i, line := range m.content {
        if i >= m.screenStart && i < m.screenStart+m.screenHeight {
            sb.WriteString(line)
            sb.WriteRune('\n')
        }
    }
    return sb.String()
}

func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "ctrl+c", "q":
            return m, tea.Quit
        case "up":
            m.screenStart -= 1
            if m.screenStart < 0 {
                m.screenStart = 0
            }
        case "down":
            m.screenStart += 1
            if m.screenStart > len(m.content)-m.screenHeight {
                m.screenStart = len(m.content) - m.screenHeight
            }
        }
    case tea.WindowSizeMsg:
        m.screenHeight = msg.Height
        m.screenWidth = msg.Width
    }
    return m, nil
}

func main() {
    bytes, err := os.ReadFile("lorem_ipsum.txt")
    if err != nil {
        panic(err.Error())
    }
    lorem := string(bytes)
    model := model{
        content: strings.Split(lorem, "\n"),
    }
    p := tea.NewProgram(
        &model,
        tea.WithAltScreen(),
    )
    if _, err := p.Run(); err != nil {
        panic(err.Error())
    }
}

Scrolling the screen down works perfectly but when scrolling up it only redraws the first line.

Source Code https://github.com/Art-S-D/test-bubbletea

Expected behavior The entire screen should render.

Screenshots asciicast

Art-S-D commented 2 months ago

I tried logging the result from sb.String() to a file and it looks correct

Art-S-D commented 2 months ago

After looking at it more, it looks like it's the last \n that breaks things

meowgorithm commented 2 months ago

Hi! This is a dup of https://github.com/charmbracelet/bubbletea/issues/304 (which we should re-examine). If you feel it's not a dupe please reopen this issue.