Skip to content

Commit aba68ae

Browse files
authored
Merge pull request #74 from jesseduffield/bracketed-paste
Support bracketed paste events
2 parents 38a8ffb + f9c4dff commit aba68ae

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

gui.go

+24
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ type Gui struct {
157157
// If Mouse is true then mouse events will be enabled.
158158
Mouse bool
159159

160+
IsPasting bool
161+
160162
// If InputEsc is true, when ESC sequence is in the buffer and it doesn't
161163
// match any known sequence, ESC means KeyEsc.
162164
InputEsc bool
@@ -759,6 +761,7 @@ func (g *Gui) MainLoop() error {
759761
}()
760762

761763
Screen.EnableFocus()
764+
Screen.EnablePaste()
762765

763766
previousEnableMouse := false
764767
for {
@@ -847,6 +850,9 @@ func (g *Gui) handleEvent(ev *GocuiEvent) error {
847850
return nil
848851
case eventFocus:
849852
return g.onFocus(ev)
853+
case eventPaste:
854+
g.IsPasting = ev.Start
855+
return nil
850856
default:
851857
return nil
852858
}
@@ -1305,6 +1311,20 @@ func (g *Gui) onKey(ev *GocuiEvent) error {
13051311
switch ev.Type {
13061312
case eventKey:
13071313

1314+
// When pasting text in Ghostty, it sends us '\r' instead of '\n' for
1315+
// newlines. I actually don't quite understand why, because from reading
1316+
// Ghostty's source code (e.g.
1317+
// https://github.com/ghostty-org/ghostty/commit/010338354a0) it does
1318+
// this conversion only for non-bracketed paste mode, but I'm seeing it
1319+
// in bracketed paste mode. Whatever I'm missing here, converting '\r'
1320+
// back to '\n' fixes pasting multi-line text from Ghostty, and doesn't
1321+
// seem harmful for other terminal emulators.
1322+
//
1323+
// KeyCtrlJ (int value 10) is '\r', and KeyCtrlM (int value 13) is '\n'.
1324+
if g.IsPasting && ev.Key == KeyCtrlJ {
1325+
ev.Key = KeyCtrlM
1326+
}
1327+
13081328
err := g.execKeybindings(g.currentView, ev)
13091329
if err != nil {
13101330
return err
@@ -1469,6 +1489,10 @@ func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) error {
14691489
var globalKb *keybinding
14701490
var matchingParentViewKb *keybinding
14711491

1492+
if g.IsPasting && v != nil && !v.Editable {
1493+
return nil
1494+
}
1495+
14721496
// if we're searching, and we've hit n/N/Esc, we ignore the default keybinding
14731497
if v != nil && v.IsSearching() && ev.Mod == ModNone {
14741498
if eventMatchesKey(ev, g.NextSearchMatchKey) {

tcell_driver.go

+9
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ type gocuiEventType uint8
155155
// The 'MouseX' and 'MouseY' fields are valid if 'Type' is 'eventMouse'.
156156
// The 'Width' and 'Height' fields are valid if 'Type' is 'eventResize'.
157157
// The 'Focused' field is valid if 'Type' is 'eventFocus'.
158+
// The 'Start' field is valid if 'Type' is 'eventPaste'. It is true for the
159+
// beginning of a paste operation, false for the end.
158160
// The 'Err' field is valid if 'Type' is 'eventError'.
159161
type GocuiEvent struct {
160162
Type gocuiEventType
@@ -167,6 +169,7 @@ type GocuiEvent struct {
167169
MouseX int
168170
MouseY int
169171
Focused bool
172+
Start bool
170173
N int
171174
}
172175

@@ -178,6 +181,7 @@ const (
178181
eventMouse
179182
eventMouseMove // only used when no button is down, otherwise it's eventMouse
180183
eventFocus
184+
eventPaste
181185
eventInterrupt
182186
eventError
183187
eventRaw
@@ -417,6 +421,11 @@ func (g *Gui) pollEvent() GocuiEvent {
417421
Type: eventFocus,
418422
Focused: tev.Focused,
419423
}
424+
case *tcell.EventPaste:
425+
return GocuiEvent{
426+
Type: eventPaste,
427+
Start: tev.Start(),
428+
}
420429
default:
421430
return GocuiEvent{Type: eventNone}
422431
}

0 commit comments

Comments
 (0)