m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-18 16:45:38 -05:00

(Experimental) Add support for iTerm2 inline image protocol

Close #1102

  fzf --preview 'imgcat -W $FZF_PREVIEW_COLUMNS -H $FZF_PREVIEW_LINES {}'

Notes:
* There is no good way to determine the height of the rendered image,
  so we assume that the image takes the full height of the preview
  window. So the image cannot be displayed with the other text.
* fzf-preview.sh script was updated to use `imgcat` if it's available
  but `chafa` is not.
* iTerm2 also supports Sixel, so adding support for this protocol is not
  quite necessary but it renders animated GIFs much better (e.g. looping).
This commit is contained in:
Junegunn Choi
2023-11-07 11:42:32 +09:00
parent 250d507bdf
commit 230fc49ae2
3 changed files with 39 additions and 13 deletions

View File

@@ -66,7 +66,8 @@ func init() {
// * 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
// * https://en.wikipedia.org/wiki/Sixel
passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?`)
// * https://iterm2.com/documentation-images.html
passThroughRegex = regexp.MustCompile(`\x1bPtmux;\x1b\x1b.*?[^\x1b]\x1b\\|\x1b(_G|P[0-9;]*q).*?\x1b\\\r?|\x1b]1337;.*?\a`)
}
type jumpMode int
@@ -125,7 +126,7 @@ type previewed struct {
numLines int
offset int
filled bool
sixel bool
image bool
wipe bool
wireframe bool
}
@@ -2027,7 +2028,7 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
var ansi *ansiState
spinnerRedraw := t.pwindow.Y() == 0
wiped := false
sixel := false
image := false
wireframe := false
Loop:
for _, line := range lines {
@@ -2055,18 +2056,25 @@ Loop:
t.pwindow.Move(y, x)
}
for idx, passThrough := range passThroughs {
// Handling Sixel output
// Handling Sixel/iTerm image
requiredLines := 0
isSixel := strings.HasPrefix(passThrough, "\x1bP")
if isSixel {
isItermImage := strings.HasPrefix(passThrough, "\x1b]1337;")
isImage := isSixel || isItermImage
if isImage {
t.previewed.wipe = true
if t.termSize.PxHeight > 0 {
// NOTE: We don't have a good way to get the height of an iTerm image,
// so we assume that it requires the full height of the preview
// window.
requiredLines = height
if isSixel && t.termSize.PxHeight > 0 {
rows := strings.Count(passThrough, "-")
requiredLines = int(math.Ceil(float64(rows*6*t.termSize.Lines) / float64(t.termSize.PxHeight)))
}
}
// Render wireframe when Sixel image cannot be displayed entirely
// Render wireframe when the image cannot be displayed entirely
if requiredLines > 0 && y+requiredLines > height {
top := true
for ; y < height; y++ {
@@ -2081,17 +2089,17 @@ Loop:
}
// Clear previous wireframe or any other text
if (t.previewed.wireframe || isSixel && !t.previewed.sixel) && !wiped {
if (t.previewed.wireframe || isImage && !t.previewed.image) && !wiped {
wiped = true
for i := y + 1; i < height; i++ {
t.pwindow.MoveAndClear(i, 0)
}
// Required for tcell to clear the previous text
if !t.previewed.sixel {
if !t.previewed.image {
t.tui.Sync(false)
}
}
sixel = sixel || isSixel
image = image || isImage
if idx == 0 {
t.pwindow.MoveAndClear(y, x)
} else {
@@ -2154,7 +2162,7 @@ Loop:
}
lineNo++
}
t.previewed.sixel = sixel
t.previewed.image = image
t.previewed.wireframe = wireframe
}