mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
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 '
This commit is contained in:
parent
0f15f1ab73
commit
d8188fce7b
13
CHANGELOG.md
13
CHANGELOG.md
@ -3,6 +3,19 @@ CHANGELOG
|
|||||||
|
|
||||||
0.43.0
|
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 /`)
|
- `--listen` server can report program state in JSON format (`GET /`)
|
||||||
```sh
|
```sh
|
||||||
# fzf server started in "headless" mode
|
# fzf server started in "headless" mode
|
||||||
|
@ -51,6 +51,7 @@ var whiteSuffix *regexp.Regexp
|
|||||||
var offsetComponentRegex *regexp.Regexp
|
var offsetComponentRegex *regexp.Regexp
|
||||||
var offsetTrimCharsRegex *regexp.Regexp
|
var offsetTrimCharsRegex *regexp.Regexp
|
||||||
var activeTempFiles []string
|
var activeTempFiles []string
|
||||||
|
var passThroughRegex *regexp.Regexp
|
||||||
|
|
||||||
const clearCode string = "\x1b[2J"
|
const clearCode string = "\x1b[2J"
|
||||||
|
|
||||||
@ -60,6 +61,11 @@ func init() {
|
|||||||
offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
|
offsetComponentRegex = regexp.MustCompile(`([+-][0-9]+)|(-?/[1-9][0-9]*)`)
|
||||||
offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
|
offsetTrimCharsRegex = regexp.MustCompile(`[^0-9/+-]`)
|
||||||
activeTempFiles = []string{}
|
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
|
type jumpMode int
|
||||||
@ -1958,7 +1964,13 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
|
|||||||
if ansi != nil {
|
if ansi != nil {
|
||||||
ansi.lbg = -1
|
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 {
|
if lineNo >= height || t.pwindow.Y() == height-1 && t.pwindow.X() > 0 {
|
||||||
t.previewed.filled = true
|
t.previewed.filled = true
|
||||||
t.previewer.scrollable = true
|
t.previewer.scrollable = true
|
||||||
@ -1971,6 +1983,9 @@ func (t *Terminal) renderPreviewText(height int, lines []string, lineNo int, unc
|
|||||||
t.renderPreviewSpinner()
|
t.renderPreviewSpinner()
|
||||||
t.pwindow.Move(y, x)
|
t.pwindow.Move(y, x)
|
||||||
}
|
}
|
||||||
|
for _, passThrough := range passThroughs {
|
||||||
|
t.tui.PassThrough(passThrough)
|
||||||
|
}
|
||||||
var fillRet tui.FillReturn
|
var fillRet tui.FillReturn
|
||||||
prefixWidth := 0
|
prefixWidth := 0
|
||||||
_, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
|
_, _, ansi = extractColor(line, ansi, func(str string, ansi *ansiState) bool {
|
||||||
|
@ -33,6 +33,7 @@ func (r *FullscreenRenderer) Init() {}
|
|||||||
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
|
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
|
||||||
func (r *FullscreenRenderer) Pause(bool) {}
|
func (r *FullscreenRenderer) Pause(bool) {}
|
||||||
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||||
|
func (r *FullscreenRenderer) PassThrough(string) {}
|
||||||
func (r *FullscreenRenderer) Clear() {}
|
func (r *FullscreenRenderer) Clear() {}
|
||||||
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
|
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
|
||||||
func (r *FullscreenRenderer) Refresh() {}
|
func (r *FullscreenRenderer) Refresh() {}
|
||||||
|
@ -31,6 +31,11 @@ const consoleDevice string = "/dev/tty"
|
|||||||
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
|
var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R")
|
||||||
var offsetRegexpBegin *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) {
|
func (r *LightRenderer) stderr(str string) {
|
||||||
r.stderrInternal(str, true, "")
|
r.stderrInternal(str, true, "")
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,11 @@ const (
|
|||||||
AttrClear = Attr(1 << 8)
|
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) Resize(maxHeightFunc func(int) int) {}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
func (r *FullscreenRenderer) defaultTheme() *ColorTheme {
|
||||||
|
@ -474,6 +474,7 @@ type Renderer interface {
|
|||||||
RefreshWindows(windows []Window)
|
RefreshWindows(windows []Window)
|
||||||
Refresh()
|
Refresh()
|
||||||
Close()
|
Close()
|
||||||
|
PassThrough(string)
|
||||||
NeedScrollbarRedraw() bool
|
NeedScrollbarRedraw() bool
|
||||||
|
|
||||||
GetChar() Event
|
GetChar() Event
|
||||||
|
Loading…
Reference in New Issue
Block a user