summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-10-07 18:20:27 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-10-07 18:36:33 +0900
commitd8188fce7b7bea982e7f9050c35e488e49fb8fd0 (patch)
tree5391297645ff3d5d19b4dba6885b92a388e21633
parent0f15f1ab73826b615caf395473160efc355143cb (diff)
downloadfzf-d8188fce7b7bea982e7f9050c35e488e49fb8fd0.tar.gz
Experimental support for Kitty image protocol in preview window
Close #3228 * Works inside and outside of tmux * There is a problem where fzf unnecessarily displays the scroll offset indicator at the topbright of the screen when the image just fits the preview window. This is because `kitty icat` generates an extra line after the image area. # A 5-row images; an extra row at the end confuses fzf ["\e_Ga ... \e[9C􎻮̅̅ࠪ􎻮̅̍ࠪ􎻮̅̎ࠪ􎻮̅̐ࠪ􎻮̅̒ࠪ􎻮̅̽ࠪ􎻮̅̾ࠪ􎻮̅̿ࠪ􎻮̅͆ࠪ􎻮̅͊ࠪ􎻮̅͋ࠪ\n", "\r\e[9C􎻮̍̅ࠪ􎻮̍̍ࠪ􎻮̍̎ࠪ􎻮̍̐ࠪ􎻮̍̒ࠪ􎻮̍̽ࠪ􎻮̍̾ࠪ􎻮̍̿ࠪ􎻮̍͆ࠪ􎻮̍͊ࠪ􎻮̍͋ࠪ\n", "\r\e[9C􎻮̎̅ࠪ􎻮̎̍ࠪ􎻮̎̎ࠪ􎻮̎̐ࠪ􎻮̎̒ࠪ􎻮̎̽ࠪ􎻮̎̾ࠪ􎻮̎̿ࠪ􎻮̎͆ࠪ􎻮̎͊ࠪ􎻮̎͋ࠪ\n", "\r\e[9C􎻮̐̅ࠪ􎻮̐̍ࠪ􎻮̐̎ࠪ􎻮̐̐ࠪ􎻮̐̒ࠪ􎻮̐̽ࠪ􎻮̐̾ࠪ􎻮̐̿ࠪ􎻮̐͆ࠪ􎻮̐͊ࠪ􎻮̐͋ࠪ\n", "\r\e[9C􎻮̒̅ࠪ􎻮̒̍ࠪ􎻮̒̎ࠪ􎻮̒̐ࠪ􎻮̒̒ࠪ􎻮̒̽ࠪ􎻮̒̾ࠪ􎻮̒̿ࠪ􎻮̒͆ࠪ􎻮̒͊ࠪ􎻮̒͋ࠪ\n", "\r\e[39m\e8"] * Example: fzf --preview=' if file --mime-type {} | grep -qF 'image/'; then # --transfer-mode=memory is the fastest option but if you want fzf to be able # to redraw the image on terminal resize or on 'change-preview-window', # you need to use --transfer-mode=stream. kitty icat --clear --transfer-mode=memory --stdin=no --place=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0 {} else bat --color=always {} fi '
-rw-r--r--CHANGELOG.md13
-rw-r--r--src/terminal.go17
-rw-r--r--src/tui/dummy.go1
-rw-r--r--src/tui/light.go5
-rw-r--r--src/tui/tcell.go5
-rw-r--r--src/tui/tui.go1
6 files changed, 41 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e18411b..3614beb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,19 @@ CHANGELOG
0.43.0
------
+- Experimental, partial support for Kitty image protocol in the preview window
+ ```sh
+ fzf --preview='
+ if file --mime-type {} | grep -qF 'image/'; then
+ # --transfer-mode=memory is the fastest option but if you want fzf to be able
+ # to redraw the image on terminal resize or on 'change-preview-window',
+ # you need to use --transfer-mode=stream.
+ kitty icat --clear --transfer-mode=memory --stdin=no --place=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0 {}
+ else
+ bat --color=always {}
+ fi
+ '
+ ```
- `--listen` server can report program state in JSON format (`GET /`)
```sh
# fzf server started in "headless" mode
diff --git a/src/terminal.go b/src/terminal.go
index 3a8c773d..07525de3 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -51,6 +51,7 @@ var whiteSuffix *regexp.Regexp
var offsetComponentRegex *regexp.Regexp
var offsetTrimCharsRegex *regexp.Regexp
var activeTempFiles []string
+var passThroughRegex *regexp.Regexp
const clearCode string = "\x1b[2J"
@@ -60,6 +61,11 @@ func init() {
offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
activeTempFiles = []string{}
+
+ // Parts of the preview output that should be passed through to the terminal
+ // * https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it
+ // * https://sw.kovidgoyal.net/kitty/graphics-protocol
+ passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b_G.*?\x1b\\`)
}
type jumpMode int
@@ -1958,7 +1964,13 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
if ansi != nil {
ansi.lbg = -1
}
- line = strings.TrimRight(line, "\r\n")
+
+ passThroughs := passThroughRegex.FindAllString(line, -1)
+ if passThroughs != nil {
+ line = passThroughRegex.ReplaceAllString(line, "")
+ }
+ line = strings.TrimLeft(strings.TrimRight(line, "\r\n"), "\r")
+
if lineNo >= height || t.pwindow.Y() == height-1 && t.pwindow.X() > 0 {
t.previewed.filled = true
t.previewer.scrollable = true
@@ -1971,6 +1983,9 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
t.renderPreviewSpinner()
t.pwindow.Move(y, x)
}
+ for _, passThrough := range passThroughs {
+ t.tui.PassThrough(passThrough)
+ }
var fillRet tui.FillReturn
prefixWidth := 0
_, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
diff --git a/src/tui/dummy.go b/src/tui/dummy.go
index 7a02a8af..352e2b09 100644
--- a/src/tui/dummy.go
+++ b/src/tui/dummy.go
@@ -33,6 +33,7 @@ func (r *FullscreenRenderer) Init() {}
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) Pause(bool) {}
func (r *FullscreenRenderer) Resume(bool, bool) {}
+func (r *FullscreenRenderer) PassThrough(string) {}
func (r *FullscreenRenderer) Clear() {}
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
func (r *FullscreenRenderer) Refresh() {}
diff --git a/src/tui/light.go b/src/tui/light.go
index cff59c90..cc828fa9 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -31,6 +31,11 @@ const consoleDevice string = "/dev/tty"
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R")
+func (r *LightRenderer) PassThrough(str string) {
+ r.queued.WriteString(str)
+ r.flush()
+}
+
func (r *LightRenderer) stderr(str string) {
r.stderrInternal(str, true, "")
}
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 8f6806d4..0c3d4694 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -98,6 +98,11 @@ const (
AttrClear = Attr(1 << 8)
)
+func (r *FullscreenRenderer) PassThrough(str string) {
+ // No-op
+ // https://github.com/gdamore/tcell/issues/363#issuecomment-680665073
+}
+
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
diff --git a/src/tui/tui.go b/src/tui/tui.go
index 9a88c455..4039565a 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -474,6 +474,7 @@ type Renderer interface {
RefreshWindows(windows []Window)
Refresh()
Close()
+ PassThrough(string)
NeedScrollbarRedraw() bool
GetChar() Event