Skip to content

Commit

Permalink
fix: remove terminal resize problems during file selection
Browse files Browse the repository at this point in the history
  • Loading branch information
Filippo Trotter authored and Filippo Trotter committed Jul 11, 2024
1 parent 49a8376 commit 7800b7b
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 34 deletions.
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func ReadFlags(cmd *cobra.Command) {
// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
FilesSelector: modelutils.InitialModel(currentDir, 20, 20),
}
clearScreen()
// Bubble Tea program
Expand Down
2 changes: 1 addition & 1 deletion utils/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func StartServer(port string) {
// Initialize the file selector model with the directory argument
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5), // Initialize the FilesSelector model with window height
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5, pty.Window.Width-5), // Initialize the FilesSelector model with window height
}
if model.Error != nil {
wish.Println(s, model.Error.Error())
Expand Down
14 changes: 7 additions & 7 deletions utils/tui/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

func TestModel(t *testing.T) {
t.Run("Init", func(t *testing.T) {
model := Model{FilesSelector: modelutils.InitialModel(".", 10)}
model := Model{FilesSelector: modelutils.InitialModel(".", 10, 10)}
cmd := model.Init()
assert.Nil(t, cmd)
})
Expand All @@ -32,7 +32,7 @@ func TestModel(t *testing.T) {
name: "FileSelection to ModeSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
setup: func(m *Model) {
m.FilesSelector.FilesPath = []string{"path/test/file1", "path/test/file2"}
Expand All @@ -51,7 +51,7 @@ func TestModel(t *testing.T) {
name: "FileSelection to ActionSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
setup: func(m *Model) {
m.FilesSelector.FilesPath = []string{"path/test/file1"}
Expand All @@ -69,7 +69,7 @@ func TestModel(t *testing.T) {
name: "No file selected",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
verify: func(t *testing.T, m Model) {
Expand Down Expand Up @@ -156,7 +156,7 @@ func TestModel(t *testing.T) {
name: "ModeSelection to FileSelection",
model: Model{
State: "ModeSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", ""),
Files: []string{"file1.txt", "file2.txt"},
},
Expand All @@ -174,7 +174,7 @@ func TestModel(t *testing.T) {
name: "ActionSelection to FileSelection",
model: Model{
State: "ActionSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", ""),
Files: []string{"file1.txt"},
Expand Down Expand Up @@ -424,7 +424,7 @@ func TestModel(t *testing.T) {
tests := []viewTest{
{
name: "FileSelection View",
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(".", 10)},
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(".", 10, 10)},
expected: "Select the files you want to modify",
},
{
Expand Down
47 changes: 38 additions & 9 deletions utils/tui/modelutils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package modelutils
import (
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand All @@ -19,9 +20,10 @@ type FilesSelector struct {
WindowHeight int
Error error
NoFileSelected bool
WindowWidth int
}

func InitialModel(currentDir string, windowHeight int) FilesSelector {
func InitialModel(currentDir string, windowHeight int, windowWidth int) FilesSelector {
var filesAndDir []string
selectedFilesAndDir := make(map[int]bool)

Expand All @@ -47,6 +49,7 @@ func InitialModel(currentDir string, windowHeight int) FilesSelector {
FilesAndDir: filesAndDir,
SelectedFilesAndDir: selectedFilesAndDir,
WindowHeight: windowHeight,
WindowWidth: windowWidth,
}
}

Expand Down Expand Up @@ -111,24 +114,41 @@ func (m FilesSelector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Done = true
}
}
case tea.WindowSizeMsg:
return m, m.doResize(msg)
}
return m, nil
}

func (m *FilesSelector) doResize(msg tea.WindowSizeMsg) tea.Cmd {
m.WindowHeight = msg.Height
m.WindowWidth = msg.Width
return nil
}

func (m FilesSelector) View() string {
if m.Error != nil {
return Paint("red").Render(fmt.Sprintf("An error occurred: %v", m.Error))
}

s := Paint("silver").Render("\n Select the files you want to modify...") + "\n"
s += Paint("silver").Render("\n Selected files till now:") + "\n"
// Help messages
helpMessages := []string{
"'q' to quit 'esc' to move to parent directory",
"'↑' to go up 'x' to modify selected files",
"'↓' to go down 'enter' to select pointed file/move to pointed sub folder",
}

// File selection and error messages
var sb strings.Builder
sb.WriteString(Paint("silver").Render("\n Select the files you want to modify...") + "\n")
sb.WriteString(Paint("silver").Render("\n Selected files till now:") + "\n")
if m.NoFileSelected {
s += Paint("red").Render("\n No file selected. Please select at least one file or quit.") + "\n"
sb.WriteString(Paint("red").Render("\n No file selected. Please select at least one file or quit.") + "\n")
}
for i := 0; i < len(m.FilesPath); i++ {
s += fmt.Sprintf(" %s\n", Paint("green").Render(m.FilesPath[i]))
sb.WriteString(fmt.Sprintf(" %s\n", Paint("green").Render(m.FilesPath[i])))
}
s += "\n"
sb.WriteString("\n")

for i := m.scrollOffset; i < m.scrollOffset+m.WindowHeight && i < len(m.FilesAndDir); i++ {
choice := m.FilesAndDir[i]
Expand All @@ -150,10 +170,19 @@ func (m FilesSelector) View() string {
cursor = Paint("red").Render(" ➪")
}

s += fmt.Sprintf("%s %s\n", cursor, choice)
sb.WriteString(fmt.Sprintf("%s %s\n", cursor, choice))
}
s += Paint("silver").Render("\n 'q' to quit 'esc' to move to parent directory\n '↑' to go up 'x' to modify selected files\n '↓' to go down 'enter' to select pointed file/move to pointed sub folder")
return s

fileSelection := sb.String()

// Combine file selection with help messages
helpView := lipgloss.JoinVertical(lipgloss.Left, helpMessages...)
content := lipgloss.JoinVertical(lipgloss.Left, fileSelection, helpView)

// Place the content in the center of the screen
fullView := lipgloss.Place(m.WindowWidth, m.WindowHeight, lipgloss.Left, lipgloss.Left, content)

return fullView
}

func Paint(color string) lipgloss.Style {
Expand Down
32 changes: 16 additions & 16 deletions utils/tui/modelutils/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ func TestFilesSelector(t *testing.T) {
{
name: "InitialModel",
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
},
verify: func(t *testing.T, m FilesSelector) {
assert.Equal(t, tempDir, m.CurrentDir)
assert.Contains(t, m.FilesAndDir, subDir)
assert.NotNil(t, m.SelectedFilesAndDir)
assert.Equal(t, 0, m.cursor)
assert.Equal(t, 10, m.WindowHeight)
assert.Equal(t, 10, 10, m.WindowHeight)
assert.NoError(t, m.Error)
},
},
{
name: "KeyDown",
msg: tea.KeyMsg{Type: tea.KeyDown},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)

},
verify: func(t *testing.T, m FilesSelector) {
Expand All @@ -56,7 +56,7 @@ func TestFilesSelector(t *testing.T) {
name: "KeyUp",
msg: tea.KeyMsg{Type: tea.KeyUp},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
m.cursor = 1
},
verify: func(t *testing.T, m FilesSelector) {
Expand All @@ -67,7 +67,7 @@ func TestFilesSelector(t *testing.T) {
name: "EnterDirectory",
msg: tea.KeyMsg{Type: tea.KeyEnter},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
m.cursor = 1
},
verify: func(t *testing.T, m FilesSelector) {
Expand All @@ -78,7 +78,7 @@ func TestFilesSelector(t *testing.T) {
name: "SelectFile",
msg: tea.KeyMsg{Type: tea.KeyEnter},
setup: func(m *FilesSelector) {
*m = InitialModel(subDir, 10)
*m = InitialModel(subDir, 10, 10)
m.cursor = 0
},
verify: func(t *testing.T, m FilesSelector) {
Expand All @@ -89,7 +89,7 @@ func TestFilesSelector(t *testing.T) {
name: "Exit",
msg: tea.KeyMsg{Type: tea.KeyCtrlC},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
},
verify: func(t *testing.T, m FilesSelector) {
// Call Update with the exit message
Expand All @@ -105,7 +105,7 @@ func TestFilesSelector(t *testing.T) {
name: "MoveToPreviousDir",
msg: tea.KeyMsg{Type: tea.KeyEsc},
setup: func(m *FilesSelector) {
*m = InitialModel(subDir, 10)
*m = InitialModel(subDir, 10, 10)
},
verify: func(t *testing.T, m FilesSelector) {
assert.Equal(t, tempDir, m.CurrentDir)
Expand All @@ -115,7 +115,7 @@ func TestFilesSelector(t *testing.T) {
name: "Confirm",
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
m.FilesPath = []string{tempFile}
},
verify: func(t *testing.T, m FilesSelector) {
Expand All @@ -127,7 +127,7 @@ func TestFilesSelector(t *testing.T) {
name: "No file selected",
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
},
verify: func(t *testing.T, m FilesSelector) {
assert.Equal(t, tempDir, m.CurrentDir)
Expand All @@ -138,7 +138,7 @@ func TestFilesSelector(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
model := InitialModel(tempDir, 10)
model := InitialModel(tempDir, 10, 10)
if tt.setup != nil {
tt.setup(&model)
}
Expand Down Expand Up @@ -173,7 +173,7 @@ func TestFilesSelectorView(t *testing.T) {
{
name: "No selected file",
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
},
verify: func(t *testing.T, view string) {
assert.Contains(t, view, "Select the files you want to modify...")
Expand All @@ -184,7 +184,7 @@ func TestFilesSelectorView(t *testing.T) {
{
name: " with a selected file",
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
m.cursor = 1
m.FilesPath = append(m.FilesPath, tempFile1)
},
Expand All @@ -198,7 +198,7 @@ func TestFilesSelectorView(t *testing.T) {
{
name: " inside subdir",
setup: func(m *FilesSelector) {
*m = InitialModel(tempDir, 10)
*m = InitialModel(tempDir, 10, 10)
msg := tea.KeyMsg{Type: tea.KeyEnter}
m.cursor = 1
newModel, _ := m.Update(msg)
Expand All @@ -212,7 +212,7 @@ func TestFilesSelectorView(t *testing.T) {
{
name: "Navigate above root directory",
setup: func(m *FilesSelector) {
*m = InitialModel("/", 10)
*m = InitialModel("/", 10, 10)
msg := tea.KeyMsg{Type: tea.KeyEsc}
m.cursor = 1
newModel, _ := m.Update(msg)
Expand All @@ -225,7 +225,7 @@ func TestFilesSelectorView(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
model := InitialModel(tempDir, 10)
model := InitialModel(tempDir, 10, 10)
if tt.setup != nil {
tt.setup(&model)
}
Expand Down

0 comments on commit 7800b7b

Please sign in to comment.