mirror of
https://github.com/junegunn/fzf.git
synced 2026-10-10 07:15:58 +08:00
Restore bracketed paste mode instead of forcing it off
Ask the terminal whether the mode is already on (DECRQM) and put it back that way on exit. Forcing it off broke pasting in shells that run fzf from a line editor widget, which enable the mode only when the editor starts. Terminals that do not answer fall back to disabling it. - Startup queries go out in one write, cursor position last. Every terminal answers DSR, so its reply bounds the wait: a paste reply still missing by then means the terminal does not know the query. - Bound the first read with select(2). Terminals that never answer escape sequences, such as FreeBSD virtual terminals, blocked startup until a key was pressed, and that keystroke was then discarded. Fix #4887 Fix #2860 Fix #976
This commit is contained in:
1 parent
2885df8395
commit
dab626bd9e
4 files changed
+168
-19
No files matched your search
+56
-5
@@ -24,15 +24,38 @@ const (
|
||||
defaultEscDelay = 100
|
||||
escPollInterval = 5
|
||||
offsetPollTries = 10
|
||||
queryTimeout = 500 * time.Millisecond
|
||||
maxInputBuffer = 1024 * 1024
|
||||
maxSelectTries = 100
|
||||
)
|
||||
|
||||
const DefaultTtyDevice string = "/dev/tty"
|
||||
|
||||
var offsetRegexp = regexp.MustCompile("(.*?)\x00?\x1b\\[([0-9]+);([0-9]+)R")
|
||||
var offsetRegexp = regexp.MustCompile("\x00?\x1b\\[([0-9]+);([0-9]+)R")
|
||||
var offsetRegexpBegin = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
|
||||
|
||||
// DECRPM reply to the DECRQM query for bracketed paste mode. Ps is 1 or 3 when
|
||||
// the mode was already set, 2 or 4 when reset, 0 when the terminal does not
|
||||
// recognize the mode.
|
||||
var pasteModeRegexp = regexp.MustCompile("\x00?\x1b\\[\\?2004;([0-4])\\$y")
|
||||
var pasteModeRegexpBegin = regexp.MustCompile("^\x1b\\[\\?2004;[0-4]\\$y")
|
||||
|
||||
// A report to ask the terminal for, and the reply to recognize it by.
|
||||
type termQuery struct {
|
||||
seq string
|
||||
reply *regexp.Regexp
|
||||
}
|
||||
|
||||
// What we ask the terminal at startup, in the order the queries go out. A
|
||||
// terminal answers them in that order, so the cursor position query is last and
|
||||
// also ends the wait: every terminal fzf supports answers it, so once its reply
|
||||
// arrives, a query still unanswered is one the terminal does not know rather
|
||||
// than one we stopped waiting for too early.
|
||||
var startupQueries = []termQuery{
|
||||
{"?2004$p", pasteModeRegexp},
|
||||
{"6n", offsetRegexp},
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Bell() {
|
||||
r.flushRaw("\a")
|
||||
}
|
||||
@@ -158,6 +181,10 @@ type LightRenderer struct {
|
||||
showCursor bool
|
||||
mutex sync.Mutex
|
||||
|
||||
// Whether bracketed paste was already on before we enabled it. Nil when
|
||||
// the terminal did not answer the query.
|
||||
pasteWasSet *bool
|
||||
|
||||
// Windows only
|
||||
ttyinChannel chan byte
|
||||
inHandle uintptr
|
||||
@@ -230,8 +257,13 @@ func (r *LightRenderer) Init() error {
|
||||
|
||||
if r.fullscreen {
|
||||
r.smcup()
|
||||
} else {
|
||||
y, x := r.findOffset()
|
||||
}
|
||||
|
||||
// Ask everything in one round trip, before the offset is needed.
|
||||
y, x, pasteWasSet := r.queryStartup()
|
||||
r.pasteWasSet = pasteWasSet
|
||||
|
||||
if !r.fullscreen {
|
||||
r.mouse = r.mouse && y >= 0
|
||||
// When --no-clear is used for repetitive relaunching, there is a small
|
||||
// time frame between fzf processes where the user keystrokes are not
|
||||
@@ -318,7 +350,11 @@ func (r *LightRenderer) getBytesInternal(cancellable bool, buffer []byte, nonblo
|
||||
if c == Esc.Int() || nonblock {
|
||||
retries = r.escDelay / escPollInterval
|
||||
}
|
||||
buffer = append(buffer, byte(c))
|
||||
// A non-blocking read that found nothing has no byte to record. Recording
|
||||
// one would put a NUL in the middle of a reply still being assembled.
|
||||
if result.ok() {
|
||||
buffer = append(buffer, byte(c))
|
||||
}
|
||||
|
||||
pc := c
|
||||
for {
|
||||
@@ -446,6 +482,12 @@ func (r *LightRenderer) escSequence(sz *int) Event {
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
|
||||
loc = pasteModeRegexpBegin.FindIndex(r.buffer)
|
||||
if loc != nil && loc[0] == 0 {
|
||||
*sz = loc[1]
|
||||
return Event{Invalid, 0, nil}
|
||||
}
|
||||
|
||||
*sz = 2
|
||||
if r.buffer[1] == 8 {
|
||||
return Event{CtrlAltBackspace, 0, nil}
|
||||
@@ -1019,7 +1061,16 @@ func (r *LightRenderer) disableMouse() {
|
||||
|
||||
func (r *LightRenderer) disableModes() {
|
||||
r.disableMouse()
|
||||
r.csi("?2004l")
|
||||
// Put bracketed paste back the way we found it. A shell that runs fzf from
|
||||
// a line editor widget re-enables the mode only when the editor starts, so
|
||||
// forcing it off here would leave it off for the rest of the session.
|
||||
// Terminals that did not answer the query fall back to disabling, which is
|
||||
// what fzf has always done.
|
||||
if r.pasteWasSet != nil && *r.pasteWasSet {
|
||||
r.csi("?2004h")
|
||||
} else {
|
||||
r.csi("?2004l")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Resume(clear bool, sigcont bool) {
|
||||
|
||||
Reference in new issue
Block a user