From f11ca377f471d29a1e9098cb0dd0a9e83568f734 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Tue, 21 Jul 2020 16:49:27 -0400 Subject: [PATCH] Add clipboard paste support to textarea --- go.mod | 1 + go.sum | 2 ++ textinput/textinput.go | 44 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/go.mod b/go.mod index c9dbf05..a10b7e6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/charmbracelet/bubbles go 1.13 require ( + github.com/atotto/clipboard v0.1.2 // indirect github.com/charmbracelet/bubbletea v0.9.1-0.20200713153904-2f53eeb54b90 github.com/mattn/go-runewidth v0.0.9 github.com/muesli/termenv v0.5.3-0.20200625163851-04b5c30e4c04 diff --git a/go.sum b/go.sum index 444ddac..7432de1 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= +github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= diff --git a/textinput/textinput.go b/textinput/textinput.go index 769abf6..2ea3ad9 100644 --- a/textinput/textinput.go +++ b/textinput/textinput.go @@ -5,6 +5,7 @@ import ( "time" "unicode" + "github.com/atotto/clipboard" tea "github.com/charmbracelet/bubbletea" rw "github.com/mattn/go-runewidth" "github.com/muesli/termenv" @@ -126,6 +127,47 @@ func (m *Model) Reset() { m.blink = false } +// Paste pastes the contents of the clipboard into the text area (if supported). +func (m *Model) Paste() { + pasteString, err := clipboard.ReadAll() + if err != nil { + m.Err = err + } + paste := []rune(pasteString) + + availSpace := m.CharLimit - len(m.value) + + // If the char limit's been reached cancel + if m.CharLimit > 0 && availSpace <= 0 { + return + } + + // If there's not enough space to paste the whole thing cut the pasted + // runes down so they'll fit + if availSpace < len(paste) { + paste = paste[:len(paste)-availSpace] + } + + // Stuff before and after the cursor + head := m.value[:m.pos] + tailSrc := m.value[m.pos:] + tail := make([]rune, len(tailSrc)) + copy(tail, tailSrc) + + // Insert pasted runes + for _, r := range paste { + head = append(head, r) + availSpace-- + m.pos++ + if m.CharLimit > 0 && availSpace <= 0 { + break + } + } + + // Put it all back together + m.value = append(head, tail...) +} + // If a max width is defined, perform some logic to treat the visible area // as a horizontally scrolling viewport. func (m *Model) handleOverflow() { @@ -322,6 +364,8 @@ func Update(msg tea.Msg, m Model) (Model, tea.Cmd) { m.value = m.value[m.pos:] m.pos = 0 m.offset = 0 + case tea.KeyCtrlV: // ^V paste + m.Paste() case tea.KeyRune: // input a regular character if msg.Alt {