7 Commits

12 changed files with 64 additions and 20 deletions

2
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/charmbracelet/bubbles module github.com/Maks1mS/bubbles
go 1.13 go 1.13

View File

@@ -3,7 +3,7 @@ package help
import ( import (
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
) )

View File

@@ -5,7 +5,7 @@ import (
"io" "io"
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/muesli/reflow/truncate" "github.com/muesli/reflow/truncate"

View File

@@ -1,6 +1,6 @@
package list package list
import "github.com/charmbracelet/bubbles/key" import "github.com/Maks1mS/bubbles/key"
// KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which // KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which
// is used to render the menu menu. // is used to render the menu menu.

View File

@@ -10,11 +10,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/charmbracelet/bubbles/help" "github.com/Maks1mS/bubbles/help"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
"github.com/charmbracelet/bubbles/paginator" "github.com/Maks1mS/bubbles/paginator"
"github.com/charmbracelet/bubbles/spinner" "github.com/Maks1mS/bubbles/spinner"
"github.com/charmbracelet/bubbles/textinput" "github.com/Maks1mS/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/muesli/reflow/ansi" "github.com/muesli/reflow/ansi"

View File

@@ -7,7 +7,7 @@ package paginator
import ( import (
"fmt" "fmt"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
) )

View File

@@ -3,7 +3,7 @@ package spinner_test
import ( import (
"testing" "testing"
"github.com/charmbracelet/bubbles/spinner" "github.com/Maks1mS/bubbles/spinner"
) )
func TestSpinnerNew(t *testing.T) { func TestSpinnerNew(t *testing.T) {

View File

@@ -3,8 +3,8 @@ package table
import ( import (
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
"github.com/charmbracelet/bubbles/viewport" "github.com/Maks1mS/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"

View File

@@ -5,10 +5,10 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/Maks1mS/bubbles/cursor"
"github.com/Maks1mS/bubbles/key"
"github.com/Maks1mS/bubbles/viewport"
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
"github.com/charmbracelet/bubbles/cursor"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
rw "github.com/mattn/go-runewidth" rw "github.com/mattn/go-runewidth"
@@ -212,6 +212,8 @@ type Model struct {
// viewport is the vertically-scrollable viewport of the multi-line text // viewport is the vertically-scrollable viewport of the multi-line text
// input. // input.
viewport *viewport.Model viewport *viewport.Model
isChanged bool
} }
// New creates a new model with default settings. // New creates a new model with default settings.
@@ -281,6 +283,12 @@ func (m *Model) SetValue(s string) {
m.InsertString(s) m.InsertString(s)
} }
func (m *Model) SetValueAndReset(s string) {
m.Reset()
m.InsertString(s)
m.ResetWithoutValue()
}
// InsertString inserts a string at the cursor position. // InsertString inserts a string at the cursor position.
func (m *Model) InsertString(s string) { func (m *Model) InsertString(s string) {
lines := strings.Split(s, "\n") lines := strings.Split(s, "\n")
@@ -320,6 +328,10 @@ func (m Model) Value() string {
return strings.TrimSuffix(v.String(), "\n") return strings.TrimSuffix(v.String(), "\n")
} }
func (m Model) IsChanged() bool {
return m.isChanged
}
// Length returns the number of characters currently in the text input. // Length returns the number of characters currently in the text input.
func (m *Model) Length() int { func (m *Model) Length() int {
var l int var l int
@@ -450,12 +462,25 @@ func (m *Model) Blur() {
// Reset sets the input to its default state with no input. // Reset sets the input to its default state with no input.
func (m *Model) Reset() { func (m *Model) Reset() {
m.value = make([][]rune, minHeight, maxHeight) m.value = make([][]rune, minHeight, maxHeight)
m.ResetWithoutValue()
}
func (m *Model) ResetWithoutValue() {
m.ResetView()
m.ResetChanged()
}
func (m *Model) ResetView() {
m.col = 0 m.col = 0
m.row = 0 m.row = 0
m.viewport.GotoTop() m.viewport.GotoTop()
m.SetCursor(0) m.SetCursor(0)
} }
func (m *Model) ResetChanged() {
m.isChanged = false
}
// handle a clipboard paste event, if supported. // handle a clipboard paste event, if supported.
func (m *Model) handlePaste(v string) { func (m *Model) handlePaste(v string) {
paste := []rune(v) paste := []rune(v)
@@ -497,6 +522,7 @@ func (m *Model) handlePaste(v string) {
// Put it all back together // Put it all back together
value := append(head, tail...) value := append(head, tail...)
m.SetValue(string(value)) m.SetValue(string(value))
m.isChanged = true
// Reset blink state if necessary and run overflow checks // Reset blink state if necessary and run overflow checks
m.SetCursor(m.col + len(paste)) m.SetCursor(m.col + len(paste))
@@ -506,6 +532,7 @@ func (m *Model) handlePaste(v string) {
// not the cursor blink should be reset. // not the cursor blink should be reset.
func (m *Model) deleteBeforeCursor() { func (m *Model) deleteBeforeCursor() {
m.value[m.row] = m.value[m.row][m.col:] m.value[m.row] = m.value[m.row][m.col:]
m.isChanged = true
m.SetCursor(0) m.SetCursor(0)
} }
@@ -514,6 +541,7 @@ func (m *Model) deleteBeforeCursor() {
// the cursor so as not to reveal word breaks in the masked input. // the cursor so as not to reveal word breaks in the masked input.
func (m *Model) deleteAfterCursor() { func (m *Model) deleteAfterCursor() {
m.value[m.row] = m.value[m.row][:m.col] m.value[m.row] = m.value[m.row][:m.col]
m.isChanged = true
m.SetCursor(len(m.value[m.row])) m.SetCursor(len(m.value[m.row]))
} }
@@ -533,6 +561,7 @@ func (m *Model) transposeLeft() {
if m.col < len(m.value[m.row]) { if m.col < len(m.value[m.row]) {
m.SetCursor(m.col + 1) m.SetCursor(m.col + 1)
} }
m.isChanged = true
} }
// deleteWordLeft deletes the word left to the cursor. Returns whether or not // deleteWordLeft deletes the word left to the cursor. Returns whether or not
@@ -573,6 +602,8 @@ func (m *Model) deleteWordLeft() {
} else { } else {
m.value[m.row] = append(m.value[m.row][:m.col], m.value[m.row][oldCol:]...) m.value[m.row] = append(m.value[m.row][:m.col], m.value[m.row][oldCol:]...)
} }
m.isChanged = true
} }
// deleteWordRight deletes the word right to the cursor. // deleteWordRight deletes the word right to the cursor.
@@ -607,6 +638,7 @@ func (m *Model) deleteWordRight() {
} }
m.SetCursor(oldCol) m.SetCursor(oldCol)
m.isChanged = true
} }
// characterRight moves the cursor one character to the right. // characterRight moves the cursor one character to the right.
@@ -692,6 +724,7 @@ func (m *Model) uppercaseRight() {
m.doWordRight(func(_ int, i int) { m.doWordRight(func(_ int, i int) {
m.value[m.row][i] = unicode.ToUpper(m.value[m.row][i]) m.value[m.row][i] = unicode.ToUpper(m.value[m.row][i])
}) })
m.isChanged = true
} }
// lowercaseRight changes the word to the right to lowercase. // lowercaseRight changes the word to the right to lowercase.
@@ -699,6 +732,7 @@ func (m *Model) lowercaseRight() {
m.doWordRight(func(_ int, i int) { m.doWordRight(func(_ int, i int) {
m.value[m.row][i] = unicode.ToLower(m.value[m.row][i]) m.value[m.row][i] = unicode.ToLower(m.value[m.row][i])
}) })
m.isChanged = true
} }
// capitalizeRight changes the word to the right to title case. // capitalizeRight changes the word to the right to title case.
@@ -708,6 +742,7 @@ func (m *Model) capitalizeRight() {
m.value[m.row][i] = unicode.ToTitle(m.value[m.row][i]) m.value[m.row][i] = unicode.ToTitle(m.value[m.row][i])
} }
}) })
m.isChanged = true
} }
// LineInfo returns the number of characters from the start of the // LineInfo returns the number of characters from the start of the
@@ -873,6 +908,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
} }
if len(m.value[m.row]) > 0 { if len(m.value[m.row]) > 0 {
m.value[m.row] = append(m.value[m.row][:max(0, m.col-1)], m.value[m.row][m.col:]...) m.value[m.row] = append(m.value[m.row][:max(0, m.col-1)], m.value[m.row][m.col:]...)
m.isChanged = true
if m.col > 0 { if m.col > 0 {
m.SetCursor(m.col - 1) m.SetCursor(m.col - 1)
} }
@@ -880,6 +916,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
case key.Matches(msg, m.KeyMap.DeleteCharacterForward): case key.Matches(msg, m.KeyMap.DeleteCharacterForward):
if len(m.value[m.row]) > 0 && m.col < len(m.value[m.row]) { if len(m.value[m.row]) > 0 && m.col < len(m.value[m.row]) {
m.value[m.row] = append(m.value[m.row][:m.col], m.value[m.row][m.col+1:]...) m.value[m.row] = append(m.value[m.row][:m.col], m.value[m.row][m.col+1:]...)
m.isChanged = true
} }
if m.col >= len(m.value[m.row]) { if m.col >= len(m.value[m.row]) {
m.mergeLineBelow(m.row) m.mergeLineBelow(m.row)
@@ -942,6 +979,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.col = min(m.col, len(m.value[m.row])) m.col = min(m.col, len(m.value[m.row]))
m.value[m.row] = append(m.value[m.row][:m.col], append(msg.Runes, m.value[m.row][m.col:]...)...) m.value[m.row] = append(m.value[m.row][:m.col], append(msg.Runes, m.value[m.row][m.col:]...)...)
m.isChanged = true
m.SetCursor(m.col + len(msg.Runes)) m.SetCursor(m.col + len(msg.Runes))
} }
@@ -1150,6 +1188,8 @@ func (m *Model) mergeLineBelow(row int) {
if len(m.value) > 0 { if len(m.value) > 0 {
m.value = m.value[:len(m.value)-1] m.value = m.value[:len(m.value)-1]
} }
m.isChanged = true
} }
// mergeLineAbove merges the current line the cursor is on with the line above. // mergeLineAbove merges the current line the cursor is on with the line above.
@@ -1173,6 +1213,8 @@ func (m *Model) mergeLineAbove(row int) {
if len(m.value) > 0 { if len(m.value) > 0 {
m.value = m.value[:len(m.value)-1] m.value = m.value[:len(m.value)-1]
} }
m.isChanged = true
} }
func (m *Model) splitLine(row, col int) { func (m *Model) splitLine(row, col int) {
@@ -1190,6 +1232,8 @@ func (m *Model) splitLine(row, col int) {
m.col = 0 m.col = 0
m.row++ m.row++
m.isChanged = true
} }
// Paste is a command for pasting from the clipboard into the text input. // Paste is a command for pasting from the clipboard into the text input.

View File

@@ -5,9 +5,9 @@ import (
"time" "time"
"unicode" "unicode"
"github.com/Maks1mS/bubbles/cursor"
"github.com/Maks1mS/bubbles/key"
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
"github.com/charmbracelet/bubbles/cursor"
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
rw "github.com/mattn/go-runewidth" rw "github.com/mattn/go-runewidth"

View File

@@ -1,6 +1,6 @@
package viewport package viewport
import "github.com/charmbracelet/bubbles/key" import "github.com/Maks1mS/bubbles/key"
const spacebar = " " const spacebar = " "

View File

@@ -4,7 +4,7 @@ import (
"math" "math"
"strings" "strings"
"github.com/charmbracelet/bubbles/key" "github.com/Maks1mS/bubbles/key"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
) )