Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions examples/textarea-selection/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"fmt"
"log"

"github.com/charmbracelet/bubbles/v2/textarea"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss/v2"
)

type model struct {
textarea textarea.Model
}

func initialModel() model {
ta := textarea.New()
ta.Placeholder = "Try text selection with mouse or Shift+arrows!"
ta.SetValue("Welcome to Bubbles!\n\nThis textarea now supports:\n- Mouse selection (click and drag)\n- Double-click to select words\n- Triple-click to select lines\n- Shift+arrows for keyboard selection\n- Ctrl+A to select all\n- Ctrl+C/X to copy/cut")
ta.Focus()
ta.SetWidth(60)
ta.SetHeight(10)

return model{textarea: ta}
}

func (m model) Init() tea.Cmd {
return textarea.Blink
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.textarea.SetWidth(msg.Width)
m.textarea.SetHeight(msg.Height - 4)
case tea.KeyPressMsg:
if msg.String() == "ctrl+c" && !m.textarea.HasSelection() {
return m, tea.Quit
}
}

var cmd tea.Cmd
m.textarea, cmd = m.textarea.Update(msg)
return m, cmd
}

func (m model) View() string {
help := lipgloss.NewStyle().Foreground(lipgloss.Color("241"))

status := ""
if m.textarea.HasSelection() {
selected := m.textarea.GetSelectedText()
if len(selected) > 30 {
selected = selected[:27] + "..."
}
status = fmt.Sprintf("Selected: %q", selected)
}

return fmt.Sprintf(
"%s\n\n%s\n%s",
m.textarea.View(),
status,
help.Render("ctrl+c to quit • mouse/keyboard to select text"),
)
}

func main() {
p := tea.NewProgram(
initialModel(),
tea.WithAltScreen(),
tea.WithMouseAllMotion(), // Enable mouse support
)
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}
93 changes: 93 additions & 0 deletions textarea/selection_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package textarea

import (
"testing"
"github.com/stretchr/testify/assert"
)

func TestTextSelection(t *testing.T) {
ta := New()
ta.SetValue("Hello world")

// Test SelectAll
ta.SelectAll()
assert.True(t, ta.HasSelection())
assert.Equal(t, "Hello world", ta.GetSelectedText())

// Test ClearSelection
ta.ClearSelection()
assert.False(t, ta.HasSelection())

// Test SetSelection
ta.SetSelection(
Position{Row: 0, Col: 0},
Position{Row: 0, Col: 5},
)
assert.Equal(t, "Hello", ta.GetSelectedText())

// Test DeleteSelection
ta.DeleteSelection()
assert.Equal(t, " world", ta.Value())
}

func TestMouseToPosition(t *testing.T) {
ta := New()
ta.SetValue("Line 1\nLine 2\nLine 3")
ta.SetWidth(20)

// Test position calculation
pos := ta.mouseToPosition(0, 0)
assert.Equal(t, Position{Row: 0, Col: 0}, pos)

pos = ta.mouseToPosition(5, 1)
assert.Equal(t, Position{Row: 1, Col: 5}, pos)
}

func TestWordSelection(t *testing.T) {
ta := New()
ta.SetValue("Hello beautiful world")

// Select "beautiful"
ta.SelectWord(Position{Row: 0, Col: 8})
assert.Equal(t, "beautiful", ta.GetSelectedText())
}

func TestLineSelection(t *testing.T) {
ta := New()
ta.SetValue("Line 1\nLine 2\nLine 3")

// Select second line
ta.SelectLine(1)
assert.Equal(t, "Line 2", ta.GetSelectedText())
}

func TestMultiLineSelection(t *testing.T) {
ta := New()
ta.SetValue("Line 1\nLine 2\nLine 3")

// Select from middle of first line to middle of second line
ta.SetSelection(
Position{Row: 0, Col: 3},
Position{Row: 1, Col: 3},
)
assert.Equal(t, "e 1\nLin", ta.GetSelectedText())
}

func TestSelectionBounds(t *testing.T) {
ta := New()
ta.SetValue("Test")

// Test out of bounds selection
ta.SetSelection(
Position{Row: 0, Col: -1},
Position{Row: 0, Col: 100},
)
assert.Equal(t, "Test", ta.GetSelectedText())

// Test inverted selection (end before start)
ta.SetSelection(
Position{Row: 0, Col: 3},
Position{Row: 0, Col: 1},
)
assert.Equal(t, "es", ta.GetSelectedText())
}
Loading