Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fzf for openSUSE:Factory checked in 
at 2021-12-25 20:16:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fzf (Old)
 and      /work/SRC/openSUSE:Factory/.fzf.new.2520 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fzf"

Sat Dec 25 20:16:41 2021 rev:21 rq:942381 version:0.29.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/fzf/fzf.changes  2021-11-17 01:15:22.878191043 
+0100
+++ /work/SRC/openSUSE:Factory/.fzf.new.2520/fzf.changes        2021-12-25 
20:17:05.813271640 +0100
@@ -1,0 +2,16 @@
+Fri Dec 24 22:54:43 UTC 2021 - Matej Cepl <mc...@suse.com>
+
+- Update to 0.29.0:
+  - Added `change-preview(...)` action to change the `--preview` command
+      - cf. `preview(...)` is a one-off action that doesn't change the
+        default preview command
+  - Added `change-preview-window(...)` action
+      - You can rotate through the different options separated by `|`
+        ```sh
+        fzf --preview 'cat {}' --preview-window right:40% \
+            --bind 
'ctrl-/:change-preview-window(right,70%|down,40%,border-top|hidden|)'
+        ```
+  - Fixed rendering of the prompt line when overflow occurs with
+    `--info=inline`
+
+-------------------------------------------------------------------

Old:
----
  0.28.0.tar.gz

New:
----
  0.29.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fzf.spec ++++++
--- /var/tmp/diff_new_pack.mArBfJ/_old  2021-12-25 20:17:06.389272109 +0100
+++ /var/tmp/diff_new_pack.mArBfJ/_new  2021-12-25 20:17:06.397272115 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           fzf
-Version:        0.28.0
+Version:        0.29.0
 Release:        0
 Summary:        A command-line fuzzy finder
 License:        MIT

++++++ 0.28.0.tar.gz -> 0.29.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/ADVANCED.md new/fzf-0.29.0/ADVANCED.md
--- old/fzf-0.28.0/ADVANCED.md  2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/ADVANCED.md  2021-12-24 17:46:01.000000000 +0100
@@ -429,30 +429,34 @@
 Kubernetes pods.
 
 ```bash
-#!/usr/bin/env bash
-
-read -ra tokens < <(
-  kubectl get pods --all-namespaces |
-    fzf --info=inline --layout=reverse --header-lines=1 --border \
+pods() {
+  FZF_DEFAULT_COMMAND="kubectl get pods --all-namespaces" \
+    fzf --info=inline --layout=reverse --header-lines=1 \
         --prompt "$(kubectl config current-context | sed 's/-context$//')> " \
-        --header $'Press CTRL-O to open log in editor\n\n' \
-        --bind ctrl-/:toggle-preview \
-        --bind 'ctrl-o:execute:${EDITOR:-vim} <(kubectl logs --namespace {1} 
{2}) > /dev/tty' \
-        --preview-window up,follow \
-        --preview 'kubectl logs --follow --tail=100000 --namespace {1} {2}' 
"$@"
-)
-[ ${#tokens} -gt 1 ] &&
-  kubectl exec -it --namespace "${tokens[0]}" "${tokens[1]}" -- bash
+        --header $'??? Enter (kubectl exec) ??? CTRL-O (open log in editor) 
??? CTRL-R (reload) ???\n\n' \
+        --bind 'ctrl-/:change-preview-window(80%,border-bottom|hidden|)' \
+        --bind 'enter:execute:kubectl exec -it --namespace {1} {2} -- bash > 
/dev/tty' \
+        --bind 'ctrl-o:execute:${EDITOR:-vim} <(kubectl logs --all-containers 
--namespace {1} {2}) > /dev/tty' \
+        --bind 'ctrl-r:reload:$FZF_DEFAULT_COMMAND' \
+        --preview-window up:follow \
+        --preview 'kubectl logs --follow --all-containers --tail=10000 
--namespace {1} {2}' "$@"
+}
 ```
 
 
![image](https://user-images.githubusercontent.com/700826/113473547-1d7a4880-94a5-11eb-98ef-9aa6f0ed215a.png)
 
 - The preview window will *"log tail"* the pod
     - Holding on to a large amount of log will consume a lot of memory. So we
-      limited the initial log amount with `--tail=100000`.
-- With `execute` binding, you can press CTRL-O to open the log in your editor
-  without leaving fzf
-- Select a pod (with an enter key) to `kubectl exec` into it
+      limited the initial log amount with `--tail=10000`.
+- `execute` bindings allow you to run any command without leaving fzf
+    - Press enter key on a pod to `kubectl exec` into it
+    - Press CTRL-O to open the log in your editor
+- Press CTRL-R to reload the pod list
+- Press CTRL-/ repeatedly to to rotate through a different sets of preview
+  window options
+    1. `80%,border-bottom`
+    1. `hidden`
+    1. Empty string after `|` translates to the default options from 
`--preview-window`
 
 Key bindings for git objects
 ----------------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/CHANGELOG.md new/fzf-0.29.0/CHANGELOG.md
--- old/fzf-0.28.0/CHANGELOG.md 2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/CHANGELOG.md 2021-12-24 17:46:01.000000000 +0100
@@ -1,6 +1,19 @@
 CHANGELOG
 =========
 
+0.29.0
+------
+- Added `change-preview(...)` action to change the `--preview` command
+    - cf. `preview(...)` is a one-off action that doesn't change the default
+      preview command
+- Added `change-preview-window(...)` action
+    - You can rotate through the different options separated by `|`
+      ```sh
+      fzf --preview 'cat {}' --preview-window right:40% \
+          --bind 
'ctrl-/:change-preview-window(right,70%|down,40%,border-top|hidden|)'
+      ```
+- Fixed rendering of the prompt line when overflow occurs with `--info=inline`
+
 0.28.0
 ------
 - Added `--header-first` option to print header before the prompt line
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/Dockerfile new/fzf-0.29.0/Dockerfile
--- old/fzf-0.28.0/Dockerfile   2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/Dockerfile   2021-12-24 17:46:01.000000000 +0100
@@ -1,4 +1,4 @@
-FROM archlinux/base:latest
+FROM archlinux
 RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go 
make gcc
 RUN gem install --no-document -v 5.14.2 minitest
 RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/README.md new/fzf-0.29.0/README.md
--- old/fzf-0.28.0/README.md    2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/README.md    2021-12-24 17:46:01.000000000 +0100
@@ -86,7 +86,7 @@
 
 ### Using Homebrew
 
-You can use [Homebrew](http://brew.sh/) (on macOS or Linux)
+You can use [Homebrew](https://brew.sh/) (on macOS or Linux)
 to install fzf.
 
 ```sh
@@ -131,6 +131,8 @@
 >
 > Refer to the package documentation for more information. (e.g. `apt-cache 
 > show fzf`)
 
+[![Packaging 
status](https://repology.org/badge/vertical-allrepos/fzf.svg)](https://repology.org/project/fzf/versions)
+
 ### Windows
 
 Pre-built binaries for Windows can be downloaded [here][bin]. fzf is also
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/install new/fzf-0.29.0/install
--- old/fzf-0.28.0/install      2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/install      2021-12-24 17:46:01.000000000 +0100
@@ -2,7 +2,7 @@
 
 set -u
 
-version=0.28.0
+version=0.29.0
 auto_completion=
 key_bindings=
 update_config=2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/install.ps1 new/fzf-0.29.0/install.ps1
--- old/fzf-0.28.0/install.ps1  2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/install.ps1  2021-12-24 17:46:01.000000000 +0100
@@ -1,4 +1,4 @@
-$version="0.28.0"
+$version="0.29.0"
 
 $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/main.go new/fzf-0.29.0/main.go
--- old/fzf-0.28.0/main.go      2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/main.go      2021-12-24 17:46:01.000000000 +0100
@@ -5,7 +5,7 @@
        "github.com/junegunn/fzf/src/protector"
 )
 
-var version string = "0.28"
+var version string = "0.29"
 var revision string = "devel"
 
 func main() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/man/man1/fzf-tmux.1 
new/fzf-0.29.0/man/man1/fzf-tmux.1
--- old/fzf-0.28.0/man/man1/fzf-tmux.1  2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/man/man1/fzf-tmux.1  2021-12-24 17:46:01.000000000 +0100
@@ -21,7 +21,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf-tmux 1 "Nov 2021" "fzf 0.28.0" "fzf-tmux - open fzf in tmux split pane"
+.TH fzf-tmux 1 "Dec 2021" "fzf 0.29.0" "fzf-tmux - open fzf in tmux split pane"
 
 .SH NAME
 fzf-tmux - open fzf in tmux split pane
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/man/man1/fzf.1 
new/fzf-0.29.0/man/man1/fzf.1
--- old/fzf-0.28.0/man/man1/fzf.1       2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/man/man1/fzf.1       2021-12-24 17:46:01.000000000 +0100
@@ -21,7 +21,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf 1 "Nov 2021" "fzf 0.28.0" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Dec 2021" "fzf 0.29.0" "fzf - a command-line fuzzy finder"
 
 .SH NAME
 fzf - a command-line fuzzy finder
@@ -810,77 +810,79 @@
 A key or an event can be bound to one or more of the following actions.
 
   \fBACTION:                   DEFAULT BINDINGS (NOTES):
-    \fBabort\fR                     \fIctrl-c  ctrl-g  ctrl-q  esc\fR
-    \fBaccept\fR                    \fIenter   double-click\fR
-    \fBaccept-non-empty\fR          (same as \fBaccept\fR except that it 
prevents fzf from exiting without selection)
-    \fBbackward-char\fR             \fIctrl-b  left\fR
-    \fBbackward-delete-char\fR      \fIctrl-h  bspace\fR
-    \fBbackward-delete-char/eof\fR  (same as \fBbackward-delete-char\fR except 
aborts fzf if query is empty)
-    \fBbackward-kill-word\fR        \fIalt-bs\fR
-    \fBbackward-word\fR             \fIalt-b   shift-left\fR
-    \fBbeginning-of-line\fR         \fIctrl-a  home\fR
-    \fBcancel\fR                    (clear query string if not empty, abort 
fzf otherwise)
-    \fBchange-prompt(...)\fR        (change prompt to the given string)
-    \fBclear-screen\fR              \fIctrl-l\fR
-    \fBclear-selection\fR           (clear multi-selection)
-    \fBclose\fR                     (close preview window if open, abort fzf 
otherwise)
-    \fBclear-query\fR               (clear query string)
-    \fBdelete-char\fR               \fIdel\fR
-    \fBdelete-char/eof\fR           \fIctrl-d\fR (same as \fBdelete-char\fR 
except aborts fzf if query is empty)
+    \fBabort\fR                      \fIctrl-c  ctrl-g  ctrl-q  esc\fR
+    \fBaccept\fR                     \fIenter   double-click\fR
+    \fBaccept-non-empty\fR           (same as \fBaccept\fR except that it 
prevents fzf from exiting without selection)
+    \fBbackward-char\fR              \fIctrl-b  left\fR
+    \fBbackward-delete-char\fR       \fIctrl-h  bspace\fR
+    \fBbackward-delete-char/eof\fR   (same as \fBbackward-delete-char\fR 
except aborts fzf if query is empty)
+    \fBbackward-kill-word\fR         \fIalt-bs\fR
+    \fBbackward-word\fR              \fIalt-b   shift-left\fR
+    \fBbeginning-of-line\fR          \fIctrl-a  home\fR
+    \fBcancel\fR                     (clear query string if not empty, abort 
fzf otherwise)
+    \fBchange-preview(...)\fR        (change \fB--preview\fR option)
+    \fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; 
rotate through the multiple option sets separated by '|')
+    \fBchange-prompt(...)\fR         (change prompt to the given string)
+    \fBclear-screen\fR               \fIctrl-l\fR
+    \fBclear-selection\fR            (clear multi-selection)
+    \fBclose\fR                      (close preview window if open, abort fzf 
otherwise)
+    \fBclear-query\fR                (clear query string)
+    \fBdelete-char\fR                \fIdel\fR
+    \fBdelete-char/eof\fR            \fIctrl-d\fR (same as \fBdelete-char\fR 
except aborts fzf if query is empty)
     \fBdeselect\fR
-    \fBdeselect-all\fR              (deselect all matches)
-    \fBdisable-search\fR            (disable search functionality)
-    \fBdown\fR                      \fIctrl-j  ctrl-n  down\fR
-    \fBenable-search\fR             (enable search functionality)
-    \fBend-of-line\fR               \fIctrl-e  end\fR
-    \fBexecute(...)\fR              (see below for the details)
-    \fBexecute-silent(...)\fR       (see below for the details)
-    \fBfirst\fR                     (move to the first match)
-    \fBforward-char\fR              \fIctrl-f  right\fR
-    \fBforward-word\fR              \fIalt-f   shift-right\fR
+    \fBdeselect-all\fR               (deselect all matches)
+    \fBdisable-search\fR             (disable search functionality)
+    \fBdown\fR                       \fIctrl-j  ctrl-n  down\fR
+    \fBenable-search\fR              (enable search functionality)
+    \fBend-of-line\fR                \fIctrl-e  end\fR
+    \fBexecute(...)\fR               (see below for the details)
+    \fBexecute-silent(...)\fR        (see below for the details)
+    \fBfirst\fR                      (move to the first match)
+    \fBforward-char\fR               \fIctrl-f  right\fR
+    \fBforward-word\fR               \fIalt-f   shift-right\fR
     \fBignore\fR
-    \fBjump\fR                      (EasyMotion-like 2-keystroke movement)
-    \fBjump-accept\fR               (jump and accept)
+    \fBjump\fR                       (EasyMotion-like 2-keystroke movement)
+    \fBjump-accept\fR                (jump and accept)
     \fBkill-line\fR
-    \fBkill-word\fR                 \fIalt-d\fR
-    \fBlast\fR                      (move to the last match)
-    \fBnext-history\fR              (\fIctrl-n\fR on \fB--history\fR)
-    \fBpage-down\fR                 \fIpgdn\fR
-    \fBpage-up\fR                   \fIpgup\fR
+    \fBkill-word\fR                  \fIalt-d\fR
+    \fBlast\fR                       (move to the last match)
+    \fBnext-history\fR               (\fIctrl-n\fR on \fB--history\fR)
+    \fBpage-down\fR                  \fIpgdn\fR
+    \fBpage-up\fR                    \fIpgup\fR
     \fBhalf-page-down\fR
     \fBhalf-page-up\fR
-    \fBpreview(...)\fR              (see below for the details)
-    \fBpreview-down\fR              \fIshift-down\fR
-    \fBpreview-up\fR                \fIshift-up\fR
+    \fBpreview(...)\fR               (see below for the details)
+    \fBpreview-down\fR               \fIshift-down\fR
+    \fBpreview-up\fR                 \fIshift-up\fR
     \fBpreview-page-down\fR
     \fBpreview-page-up\fR
     \fBpreview-half-page-down\fR
     \fBpreview-half-page-up\fR
     \fBpreview-bottom\fR
     \fBpreview-top\fR
-    \fBprevious-history\fR          (\fIctrl-p\fR on \fB--history\fR)
-    \fBprint-query\fR               (print query and exit)
-    \fBput\fR                       (put the character to the prompt)
+    \fBprevious-history\fR           (\fIctrl-p\fR on \fB--history\fR)
+    \fBprint-query\fR                (print query and exit)
+    \fBput\fR                        (put the character to the prompt)
     \fBrefresh-preview\fR
-    \fBreload(...)\fR               (see below for the details)
-    \fBreplace-query\fR             (replace query string with the current 
selection)
+    \fBreload(...)\fR                (see below for the details)
+    \fBreplace-query\fR              (replace query string with the current 
selection)
     \fBselect\fR
-    \fBselect-all\fR                (select all matches)
-    \fBtoggle\fR                    (\fIright-click\fR)
-    \fBtoggle-all\fR                (toggle all matches)
-    \fBtoggle+down\fR               \fIctrl-i  (tab)\fR
-    \fBtoggle-in\fR                 (\fB--layout=reverse*\fR ? \fBtoggle+up\fR 
: \fBtoggle+down\fR)
-    \fBtoggle-out\fR                (\fB--layout=reverse*\fR ? 
\fBtoggle+down\fR : \fBtoggle+up\fR)
+    \fBselect-all\fR                 (select all matches)
+    \fBtoggle\fR                     (\fIright-click\fR)
+    \fBtoggle-all\fR                 (toggle all matches)
+    \fBtoggle+down\fR                \fIctrl-i  (tab)\fR
+    \fBtoggle-in\fR                  (\fB--layout=reverse*\fR ? 
\fBtoggle+up\fR : \fBtoggle+down\fR)
+    \fBtoggle-out\fR                 (\fB--layout=reverse*\fR ? 
\fBtoggle+down\fR : \fBtoggle+up\fR)
     \fBtoggle-preview\fR
     \fBtoggle-preview-wrap\fR
-    \fBtoggle-search\fR             (toggle search functionality)
+    \fBtoggle-search\fR              (toggle search functionality)
     \fBtoggle-sort\fR
-    \fBtoggle+up\fR                 \fIbtab    (shift-tab)\fR
-    \fBunbind(...)\fR               (unbind bindings)
-    \fBunix-line-discard\fR         \fIctrl-u\fR
-    \fBunix-word-rubout\fR          \fIctrl-w\fR
-    \fBup\fR                        \fIctrl-k  ctrl-p  up\fR
-    \fByank\fR                      \fIctrl-y\fR
+    \fBtoggle+up\fR                  \fIbtab    (shift-tab)\fR
+    \fBunbind(...)\fR                (unbind bindings)
+    \fBunix-line-discard\fR          \fIctrl-u\fR
+    \fBunix-word-rubout\fR           \fIctrl-w\fR
+    \fBup\fR                         \fIctrl-k  ctrl-p  up\fR
+    \fByank\fR                       \fIctrl-y\fR
 
 .SS ACTION COMPOSITION
 
@@ -970,7 +972,6 @@
 option.
 
 e.g.
-
      # Default preview command with an extra preview binding
      fzf --preview 'file {}' --bind '?:preview:cat {}'
 
@@ -981,6 +982,22 @@
      # Preview window hidden by default, it appears when you first hit '?'
      fzf --bind '?:preview:cat {}' --preview-window hidden
 
+.SS CHANGE PREVIEW WINDOW ATTRIBUTES
+
+\fBchange-preview-window\fR action can be used to change the properties of the
+preview window. Unlike the \fB--preview-window\fR option, you can specify
+multiple sets of options separated by '|' characters.
+
+e.g.
+     # Rotate through the options using CTRL-/
+     fzf --preview 'cat {}' --bind 
'ctrl-/:change-preview-window(right,70%|down,40%,border-horizontal|hidden|right)'
+
+     # The default properties given by `--preview-window` are inherited, so an 
empty string in the list is interpreted as the default
+     fzf --preview 'cat {}' --preview-window 'right,40%,border-left' --bind 
'ctrl-/:change-preview-window(70%|down,border-top|hidden|)'
+
+     # This is equivalent to toggle-preview action
+     fzf --preview 'cat {}' --bind 'ctrl-/:change-preview-window(hidden|)'
+
 .SH AUTHOR
 Junegunn Choi (\fijunegun...@gmail.com\fR)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/plugin/fzf.vim 
new/fzf-0.29.0/plugin/fzf.vim
--- old/fzf-0.28.0/plugin/fzf.vim       2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/plugin/fzf.vim       2021-12-24 17:46:01.000000000 +0100
@@ -444,6 +444,12 @@
   return [shell, shellslash, shellcmdflag, shellxquote]
 endfunction
 
+function! s:writefile(...)
+  if call('writefile', a:000) == -1
+    throw 'Failed to write temporary file. Check if you can write to the path 
tempname() returns.'
+  endif
+endfunction
+
 function! fzf#run(...) abort
 try
   let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
@@ -471,7 +477,7 @@
       let source_command = source
     elseif type == 3
       let temps.input = s:fzf_tempname()
-      call writefile(source, temps.input)
+      call s:writefile(source, temps.input)
       let source_command = (s:is_win ? 'type ' : 'cat 
').fzf#shellescape(temps.input)
     else
       throw 'Invalid source type'
@@ -515,7 +521,7 @@
   call s:callback(dict, lines)
   return lines
 finally
-  if len(source_command)
+  if exists('source_command') && len(source_command)
     if len(prev_default_command)
       let $FZF_DEFAULT_COMMAND = prev_default_command
     else
@@ -660,7 +666,7 @@
   endif
   if s:is_win
     let batchfile = s:fzf_tempname().'.bat'
-    call writefile(s:wrap_cmds(command), batchfile)
+    call s:writefile(s:wrap_cmds(command), batchfile)
     let command = batchfile
     let a:temps.batchfile = batchfile
     if has('nvim')
@@ -678,7 +684,7 @@
     endif
   elseif has('win32unix') && $TERM !=# 'cygwin'
     let shellscript = s:fzf_tempname()
-    call writefile([command], shellscript)
+    call s:writefile([command], shellscript)
     let command = 'cmd.exe /C '.fzf#shellescape('set "TERM=" & start /WAIT sh 
-c '.shellscript)
     let a:temps.shellscript = shellscript
   endif
@@ -877,7 +883,7 @@
     call s:pushd(a:dict)
     if s:is_win
       let fzf.temps.batchfile = s:fzf_tempname().'.bat'
-      call writefile(s:wrap_cmds(a:command), fzf.temps.batchfile)
+      call s:writefile(s:wrap_cmds(a:command), fzf.temps.batchfile)
       let command = fzf.temps.batchfile
     else
       let command = a:command
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/shell/key-bindings.bash 
new/fzf-0.29.0/shell/key-bindings.bash
--- old/fzf-0.28.0/shell/key-bindings.bash      2021-11-03 17:05:07.000000000 
+0100
+++ new/fzf-0.29.0/shell/key-bindings.bash      2021-12-24 17:46:01.000000000 
+0100
@@ -41,7 +41,7 @@
   local cmd dir
   cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' 
-o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' 
\\) -prune \
     -o -type d -print 2> /dev/null | cut -b3-"}"
-  dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} 
--reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) 
+m) && printf 'cd %q' "$dir"
+  dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} 
--reverse --bind=ctrl-z:ignore $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) 
+m) && printf 'cd -- %q' "$dir"
 }
 
 __fzf_history__() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/shell/key-bindings.fish 
new/fzf-0.29.0/shell/key-bindings.fish
--- old/fzf-0.28.0/shell/key-bindings.fish      2021-11-03 17:05:07.000000000 
+0100
+++ new/fzf-0.29.0/shell/key-bindings.fish      2021-12-24 17:46:01.000000000 
+0100
@@ -87,7 +87,7 @@
       eval "$FZF_ALT_C_COMMAND | "(__fzfcmd)' +m --query "'$fzf_query'"' | 
read -l result
 
       if [ -n "$result" ]
-        cd $result
+        cd -- $result
 
         # Remove last token from commandline.
         commandline -t ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/shell/key-bindings.zsh 
new/fzf-0.29.0/shell/key-bindings.zsh
--- old/fzf-0.28.0/shell/key-bindings.zsh       2021-11-03 17:05:07.000000000 
+0100
+++ new/fzf-0.29.0/shell/key-bindings.zsh       2021-12-24 17:46:01.000000000 
+0100
@@ -79,7 +79,7 @@
     return 0
   fi
   zle push-line # Clear buffer. Auto-restored on next prompt.
-  BUFFER="cd ${(q)dir}"
+  BUFFER="cd -- ${(q)dir}"
   zle accept-line
   local ret=$?
   unset dir # ensure this doesn't end up appearing in prompt expansion
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/src/options.go 
new/fzf-0.29.0/src/options.go
--- old/fzf-0.28.0/src/options.go       2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/src/options.go       2021-12-24 17:46:01.000000000 +0100
@@ -176,6 +176,14 @@
        headerLines int
 }
 
+func (a previewOpts) sameLayout(b previewOpts) bool {
+       return a.size == b.size && a.position == b.position && a.border == 
b.border && a.hidden == b.hidden
+}
+
+func (a previewOpts) sameContentLayout(b previewOpts) bool {
+       return a.wrap == b.wrap && a.headerLines == b.headerLines
+}
+
 // Options stores the values of command-line options
 type Options struct {
        Fuzzy       bool
@@ -216,7 +224,7 @@
        Filter      *string
        ToggleSort  bool
        Expect      map[tui.Event]string
-       Keymap      map[tui.Event][]action
+       Keymap      map[tui.Event][]*action
        Preview     previewOpts
        PrintQuery  bool
        ReadZero    bool
@@ -279,7 +287,7 @@
                Filter:      nil,
                ToggleSort:  false,
                Expect:      make(map[tui.Event]string),
-               Keymap:      make(map[tui.Event][]action),
+               Keymap:      make(map[tui.Event][]*action),
                Preview:     defaultPreviewOpts(""),
                PrintQuery:  false,
                ReadZero:    false,
@@ -787,10 +795,10 @@
        // Backreferences are not supported.
        // "~!@#$%^&*;/|".each_char.map { |c| Regexp.escape(c) }.map { |c| 
"#{c}[^#{c}]*#{c}" }.join('|')
        executeRegexp = regexp.MustCompile(
-               
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
+               
`(?si)[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind):.+|[:+](execute(?:-multi|-silent)?|reload|preview|change-prompt|change-preview-window|change-preview|unbind)(\([^)]*\)|\[[^\]]*\]|~[^~]*~|![^!]*!|@[^@]*@|\#[^\#]*\#|\$[^\$]*\$|%[^%]*%|\^[^\^]*\^|&[^&]*&|\*[^\*]*\*|;[^;]*;|/[^/]*/|\|[^\|]*\|)`)
 }
 
-func parseKeymap(keymap map[tui.Event][]action, str string) {
+func parseKeymap(keymap map[tui.Event][]*action, str string) {
        masked := executeRegexp.ReplaceAllStringFunc(str, func(src string) 
string {
                symbol := ":"
                if strings.HasPrefix(src, "+") {
@@ -799,6 +807,10 @@
                prefix := symbol + "execute"
                if strings.HasPrefix(src[1:], "reload") {
                        prefix = symbol + "reload"
+               } else if strings.HasPrefix(src[1:], "change-preview-window") {
+                       prefix = symbol + "change-preview-window"
+               } else if strings.HasPrefix(src[1:], "change-preview") {
+                       prefix = symbol + "change-preview"
                } else if strings.HasPrefix(src[1:], "preview") {
                        prefix = symbol + "preview"
                } else if strings.HasPrefix(src[1:], "unbind") {
@@ -842,7 +854,7 @@
 
                idx2 := len(pair[0]) + 1
                specs := strings.Split(pair[1], "+")
-               actions := make([]action, 0, len(specs))
+               actions := make([]*action, 0, len(specs))
                appendAction := func(types ...actionType) {
                        actions = append(actions, toActions(types...)...)
                }
@@ -1002,6 +1014,10 @@
                                                offset = len("reload")
                                        case actPreview:
                                                offset = len("preview")
+                                       case actChangePreviewWindow:
+                                               offset = 
len("change-preview-window")
+                                       case actChangePreview:
+                                               offset = len("change-preview")
                                        case actChangePrompt:
                                                offset = len("change-prompt")
                                        case actUnbind:
@@ -1017,17 +1033,22 @@
                                        if spec[offset] == ':' {
                                                if specIndex == len(specs)-1 {
                                                        actionArg = 
spec[offset+1:]
-                                                       actions = 
append(actions, action{t: t, a: actionArg})
+                                                       actions = 
append(actions, &action{t: t, a: actionArg})
                                                } else {
                                                        prevSpec = spec + "+"
                                                        continue
                                                }
                                        } else {
                                                actionArg = spec[offset+1 : 
len(spec)-1]
-                                               actions = append(actions, 
action{t: t, a: actionArg})
+                                               actions = append(actions, 
&action{t: t, a: actionArg})
                                        }
                                        if t == actUnbind {
                                                parseKeyChords(actionArg, 
"unbind target required")
+                                       } else if t == actChangePreviewWindow {
+                                               opts := previewOpts{}
+                                               for _, arg := range 
strings.Split(actionArg, "|") {
+                                                       
parsePreviewWindow(&opts, arg)
+                                               }
                                        }
                                }
                        }
@@ -1053,6 +1074,10 @@
                return actUnbind
        case "preview":
                return actPreview
+       case "change-preview-window":
+               return actChangePreviewWindow
+       case "change-preview":
+               return actChangePreview
        case "change-prompt":
                return actChangePrompt
        case "execute":
@@ -1065,7 +1090,7 @@
        return actIgnore
 }
 
-func parseToggleSort(keymap map[tui.Event][]action, str string) {
+func parseToggleSort(keymap map[tui.Event][]*action, str string) {
        keys := parseKeyChords(str, "key name required")
        if len(keys) != 1 {
                errorExit("multiple keys specified")
@@ -1633,10 +1658,28 @@
        // Extend the default key map
        keymap := defaultKeymap()
        for key, actions := range opts.Keymap {
+               var lastChangePreviewWindow *action
                for _, act := range actions {
-                       if act.t == actToggleSort {
+                       switch act.t {
+                       case actToggleSort:
+                               // To display "+S"/"-S" on info line
                                opts.ToggleSort = true
+                       case actChangePreviewWindow:
+                               lastChangePreviewWindow = act
+                       }
+               }
+               // Re-organize actions so that we only keep the last 
change-preview-window
+               // and it comes first in the list.
+               //  *  change-preview-window(up,+10)+preview(sleep 3; cat 
{})+change-preview-window(up,+20)
+               //  -> change-preview-window(up,+20)+preview(sleep 3; cat {})
+               if lastChangePreviewWindow != nil {
+                       reordered := []*action{lastChangePreviewWindow}
+                       for _, act := range actions {
+                               if act.t != actChangePreviewWindow {
+                                       reordered = append(reordered, act)
+                               }
                        }
+                       actions = reordered
                }
                keymap[key] = actions
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/src/terminal.go 
new/fzf-0.29.0/src/terminal.go
--- old/fzf-0.28.0/src/terminal.go      2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/src/terminal.go      2021-12-24 17:46:01.000000000 +0100
@@ -4,6 +4,7 @@
        "bufio"
        "fmt"
        "io/ioutil"
+       "math"
        "os"
        "os/signal"
        "regexp"
@@ -104,88 +105,89 @@
 
 // Terminal represents terminal input/output
 type Terminal struct {
-       initDelay    time.Duration
-       infoStyle    infoStyle
-       spinner      []string
-       prompt       func()
-       promptLen    int
-       pointer      string
-       pointerLen   int
-       pointerEmpty string
-       marker       string
-       markerLen    int
-       markerEmpty  string
-       queryLen     [2]int
-       layout       layoutType
-       fullscreen   bool
-       keepRight    bool
-       hscroll      bool
-       hscrollOff   int
-       scrollOff    int
-       wordRubout   string
-       wordNext     string
-       cx           int
-       cy           int
-       offset       int
-       xoffset      int
-       yanked       []rune
-       input        []rune
-       multi        int
-       sort         bool
-       toggleSort   bool
-       delimiter    Delimiter
-       expect       map[tui.Event]string
-       keymap       map[tui.Event][]action
-       pressed      string
-       printQuery   bool
-       history      *History
-       cycle        bool
-       headerFirst  bool
-       headerLines  int
-       header       []string
-       header0      []string
-       ansi         bool
-       tabstop      int
-       margin       [4]sizeSpec
-       padding      [4]sizeSpec
-       strong       tui.Attr
-       unicode      bool
-       borderShape  tui.BorderShape
-       cleanExit    bool
-       paused       bool
-       border       tui.Window
-       window       tui.Window
-       pborder      tui.Window
-       pwindow      tui.Window
-       count        int
-       progress     int
-       reading      bool
-       running      bool
-       failed       *string
-       jumping      jumpMode
-       jumpLabels   string
-       printer      func(string)
-       printsep     string
-       merger       *Merger
-       selected     map[int32]selectedItem
-       version      int64
-       reqBox       *util.EventBox
-       previewOpts  previewOpts
-       previewer    previewer
-       previewed    previewed
-       previewBox   *util.EventBox
-       eventBox     *util.EventBox
-       mutex        sync.Mutex
-       initFunc     func()
-       prevLines    []itemLine
-       suppress     bool
-       sigstop      bool
-       startChan    chan bool
-       killChan     chan int
-       slab         *util.Slab
-       theme        *tui.ColorTheme
-       tui          tui.Renderer
-       executing    *util.AtomicBool
+       initDelay          time.Duration
+       infoStyle          infoStyle
+       spinner            []string
+       prompt             func()
+       promptLen          int
+       pointer            string
+       pointerLen         int
+       pointerEmpty       string
+       marker             string
+       markerLen          int
+       markerEmpty        string
+       queryLen           [2]int
+       layout             layoutType
+       fullscreen         bool
+       keepRight          bool
+       hscroll            bool
+       hscrollOff         int
+       scrollOff          int
+       wordRubout         string
+       wordNext           string
+       cx                 int
+       cy                 int
+       offset             int
+       xoffset            int
+       yanked             []rune
+       input              []rune
+       multi              int
+       sort               bool
+       toggleSort         bool
+       delimiter          Delimiter
+       expect             map[tui.Event]string
+       keymap             map[tui.Event][]*action
+       pressed            string
+       printQuery         bool
+       history            *History
+       cycle              bool
+       headerFirst        bool
+       headerLines        int
+       header             []string
+       header0            []string
+       ansi               bool
+       tabstop            int
+       margin             [4]sizeSpec
+       padding            [4]sizeSpec
+       strong             tui.Attr
+       unicode            bool
+       borderShape        tui.BorderShape
+       cleanExit          bool
+       paused             bool
+       border             tui.Window
+       window             tui.Window
+       pborder            tui.Window
+       pwindow            tui.Window
+       count              int
+       progress           int
+       reading            bool
+       running            bool
+       failed             *string
+       jumping            jumpMode
+       jumpLabels         string
+       printer            func(string)
+       printsep           string
+       merger             *Merger
+       selected           map[int32]selectedItem
+       version            int64
+       reqBox             *util.EventBox
+       initialPreviewOpts previewOpts
+       previewOpts        previewOpts
+       previewer          previewer
+       previewed          previewed
+       previewBox         *util.EventBox
+       eventBox           *util.EventBox
+       mutex              sync.Mutex
+       initFunc           func()
+       prevLines          []itemLine
+       suppress           bool
+       sigstop            bool
+       startChan          chan bool
+       killChan           chan int
+       slab               *util.Slab
+       theme              *tui.ColorTheme
+       tui                tui.Renderer
+       executing          *util.AtomicBool
 }
 
 type selectedItem struct {
@@ -216,6 +218,7 @@
        reqRefresh
        reqReinit
        reqRedraw
+       reqFullRedraw
        reqClose
        reqPrintQuery
        reqPreviewEnqueue
@@ -286,6 +289,8 @@
        actTogglePreview
        actTogglePreviewWrap
        actPreview
+       actChangePreview
+       actChangePreviewWindow
        actPreviewTop
        actPreviewBottom
        actPreviewUp
@@ -324,9 +329,10 @@
 }
 
 type previewRequest struct {
-       template string
-       pwindow  tui.Window
-       list     []*Item
+       template     string
+       pwindow      tui.Window
+       scrollOffset int
+       list         []*Item
 }
 
 type previewResult struct {
@@ -336,16 +342,16 @@
        spinner string
 }
 
-func toActions(types ...actionType) []action {
-       actions := make([]action, len(types))
+func toActions(types ...actionType) []*action {
+       actions := make([]*action, len(types))
        for idx, t := range types {
-               actions[idx] = action{t: t, a: ""}
+               actions[idx] = &action{t: t, a: ""}
        }
        return actions
 }
 
-func defaultKeymap() map[tui.Event][]action {
-       keymap := make(map[tui.Event][]action)
+func defaultKeymap() map[tui.Event][]*action {
+       keymap := make(map[tui.Event][]*action)
        add := func(e tui.EventType, a actionType) {
                keymap[e.AsEvent()] = toActions(a)
        }
@@ -416,7 +422,7 @@
 func hasPreviewAction(opts *Options) bool {
        for _, actions := range opts.Keymap {
                for _, action := range actions {
-                       if action.t == actPreview {
+                       if action.t == actPreview || action.t == 
actChangePreview {
                                return true
                        }
                }
@@ -496,72 +502,73 @@
                wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
        }
        t := Terminal{
-               initDelay:   delay,
-               infoStyle:   opts.InfoStyle,
-               spinner:     makeSpinner(opts.Unicode),
-               queryLen:    [2]int{0, 0},
-               layout:      opts.Layout,
-               fullscreen:  fullscreen,
-               keepRight:   opts.KeepRight,
-               hscroll:     opts.Hscroll,
-               hscrollOff:  opts.HscrollOff,
-               scrollOff:   opts.ScrollOff,
-               wordRubout:  wordRubout,
-               wordNext:    wordNext,
-               cx:          len(input),
-               cy:          0,
-               offset:      0,
-               xoffset:     0,
-               yanked:      []rune{},
-               input:       input,
-               multi:       opts.Multi,
-               sort:        opts.Sort > 0,
-               toggleSort:  opts.ToggleSort,
-               delimiter:   opts.Delimiter,
-               expect:      opts.Expect,
-               keymap:      opts.Keymap,
-               pressed:     "",
-               printQuery:  opts.PrintQuery,
-               history:     opts.History,
-               margin:      opts.Margin,
-               padding:     opts.Padding,
-               unicode:     opts.Unicode,
-               borderShape: opts.BorderShape,
-               cleanExit:   opts.ClearOnExit,
-               paused:      opts.Phony,
-               strong:      strongAttr,
-               cycle:       opts.Cycle,
-               headerFirst: opts.HeaderFirst,
-               headerLines: opts.HeaderLines,
-               header:      header,
-               header0:     header,
-               ansi:        opts.Ansi,
-               tabstop:     opts.Tabstop,
-               reading:     true,
-               running:     true,
-               failed:      nil,
-               jumping:     jumpDisabled,
-               jumpLabels:  opts.JumpLabels,
-               printer:     opts.Printer,
-               printsep:    opts.PrintSep,
-               merger:      EmptyMerger,
-               selected:    make(map[int32]selectedItem),
-               reqBox:      util.NewEventBox(),
-               previewOpts: opts.Preview,
-               previewer:   previewer{0, []string{}, 0, showPreviewWindow, 
false, true, false, ""},
-               previewed:   previewed{0, 0, 0, false},
-               previewBox:  previewBox,
-               eventBox:    eventBox,
-               mutex:       sync.Mutex{},
-               suppress:    true,
-               sigstop:     false,
-               slab:        util.MakeSlab(slab16Size, slab32Size),
-               theme:       opts.Theme,
-               startChan:   make(chan bool, 1),
-               killChan:    make(chan int),
-               tui:         renderer,
-               initFunc:    func() { renderer.Init() },
-               executing:   util.NewAtomicBool(false)}
+               initDelay:          delay,
+               infoStyle:          opts.InfoStyle,
+               spinner:            makeSpinner(opts.Unicode),
+               queryLen:           [2]int{0, 0},
+               layout:             opts.Layout,
+               fullscreen:         fullscreen,
+               keepRight:          opts.KeepRight,
+               hscroll:            opts.Hscroll,
+               hscrollOff:         opts.HscrollOff,
+               scrollOff:          opts.ScrollOff,
+               wordRubout:         wordRubout,
+               wordNext:           wordNext,
+               cx:                 len(input),
+               cy:                 0,
+               offset:             0,
+               xoffset:            0,
+               yanked:             []rune{},
+               input:              input,
+               multi:              opts.Multi,
+               sort:               opts.Sort > 0,
+               toggleSort:         opts.ToggleSort,
+               delimiter:          opts.Delimiter,
+               expect:             opts.Expect,
+               keymap:             opts.Keymap,
+               pressed:            "",
+               printQuery:         opts.PrintQuery,
+               history:            opts.History,
+               margin:             opts.Margin,
+               padding:            opts.Padding,
+               unicode:            opts.Unicode,
+               borderShape:        opts.BorderShape,
+               cleanExit:          opts.ClearOnExit,
+               paused:             opts.Phony,
+               strong:             strongAttr,
+               cycle:              opts.Cycle,
+               headerFirst:        opts.HeaderFirst,
+               headerLines:        opts.HeaderLines,
+               header:             header,
+               header0:            header,
+               ansi:               opts.Ansi,
+               tabstop:            opts.Tabstop,
+               reading:            true,
+               running:            true,
+               failed:             nil,
+               jumping:            jumpDisabled,
+               jumpLabels:         opts.JumpLabels,
+               printer:            opts.Printer,
+               printsep:           opts.PrintSep,
+               merger:             EmptyMerger,
+               selected:           make(map[int32]selectedItem),
+               reqBox:             util.NewEventBox(),
+               initialPreviewOpts: opts.Preview,
+               previewOpts:        opts.Preview,
+               previewer:          previewer{0, []string{}, 0, 
showPreviewWindow, false, true, false, ""},
+               previewed:          previewed{0, 0, 0, false},
+               previewBox:         previewBox,
+               eventBox:           eventBox,
+               mutex:              sync.Mutex{},
+               suppress:           true,
+               sigstop:            false,
+               slab:               util.MakeSlab(slab16Size, slab32Size),
+               theme:              opts.Theme,
+               startChan:          make(chan bool, 1),
+               killChan:           make(chan int),
+               tui:                renderer,
+               initFunc:           func() { renderer.Init() },
+               executing:          util.NewAtomicBool(false)}
        t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
        t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0)
        t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0)
@@ -703,7 +710,7 @@
 }
 
 func (t *Terminal) displayWidth(runes []rune) int {
-       width, _ := util.RunesWidth(runes, 0, t.tabstop, 0)
+       width, _ := util.RunesWidth(runes, 0, t.tabstop, math.MaxInt32)
        return width
 }
 
@@ -1191,7 +1198,7 @@
 
 func (t *Terminal) trimRight(runes []rune, width int) ([]rune, bool) {
        // We start from the beginning to handle tab characters
-       width, overflowIdx := util.RunesWidth(runes, 0, t.tabstop, width)
+       _, overflowIdx := util.RunesWidth(runes, 0, t.tabstop, width)
        if overflowIdx >= 0 {
                return runes[:overflowIdx], true
        }
@@ -1642,9 +1649,14 @@
                template, t.ansi, t.delimiter, t.printsep, forcePlus, input, 
list)
 }
 
-func (t *Terminal) evaluateScrollOffset(list []*Item, height int) int {
+func (t *Terminal) evaluateScrollOffset() int {
+       if t.pwindow == nil {
+               return 0
+       }
+
+       // We only need the current item to calculate the scroll offset
        offsetExpr := offsetTrimCharsRegex.ReplaceAllString(
-               t.replacePlaceholder(t.previewOpts.scroll, false, "", list), "")
+               t.replacePlaceholder(t.previewOpts.scroll, false, "", 
[]*Item{t.currentItem(), nil}), "")
 
        atoi := func(s string) int {
                n, e := strconv.Atoi(s)
@@ -1655,20 +1667,21 @@
        }
 
        base := -1
+       height := util.Max(0, t.pwindow.Height()-t.previewOpts.headerLines)
        for _, component := range 
offsetComponentRegex.FindAllString(offsetExpr, -1) {
                if strings.HasPrefix(component, "-/") {
                        component = component[1:]
                }
                if component[0] == '/' {
                        denom := atoi(component[1:])
-                       if denom == 0 {
-                               return base
+                       if denom != 0 {
+                               base -= height / denom
                        }
-                       return base - height/denom
+                       break
                }
                base += atoi(component)
        }
-       return base
+       return util.Max(0, base)
 }
 
 func replacePlaceholder(template string, stripAnsi bool, delimiter Delimiter, 
printsep string, forcePlus bool, query string, allItems []*Item) string {
@@ -1767,8 +1780,10 @@
        })
 }
 
-func (t *Terminal) redraw() {
-       t.tui.Clear()
+func (t *Terminal) redraw(clear bool) {
+       if clear {
+               t.tui.Clear()
+       }
        t.tui.Refresh()
        t.printAll()
 }
@@ -1788,7 +1803,7 @@
                t.tui.Pause(true)
                cmd.Run()
                t.tui.Resume(true, false)
-               t.redraw()
+               t.redraw(true)
                t.refresh()
        } else {
                t.tui.Pause(false)
@@ -1933,7 +1948,7 @@
                go func() {
                        for {
                                <-resizeChan
-                               t.reqBox.Set(reqRedraw, nil)
+                               t.reqBox.Set(reqFullRedraw, nil)
                        }
                }()
 
@@ -1972,12 +1987,14 @@
                                var items []*Item
                                var commandTemplate string
                                var pwindow tui.Window
+                               initialOffset := 0
                                t.previewBox.Wait(func(events *util.Events) {
                                        for req, value := range *events {
                                                switch req {
                                                case reqPreviewEnqueue:
                                                        request := 
value.(previewRequest)
                                                        commandTemplate = 
request.template
+                                                       initialOffset = 
request.scrollOffset
                                                        items = request.list
                                                        pwindow = 
request.pwindow
                                                }
@@ -1989,11 +2006,9 @@
                                if items[0] != nil {
                                        _, query := t.Input()
                                        command := 
t.replacePlaceholder(commandTemplate, false, string(query), items)
-                                       initialOffset := 0
                                        cmd := util.ExecCommand(command, true)
                                        if pwindow != nil {
                                                height := pwindow.Height()
-                                               initialOffset = util.Max(0, 
t.evaluateScrollOffset(items, util.Max(0, height-t.previewOpts.headerLines)))
                                                env := os.Environ()
                                                lines := 
fmt.Sprintf("LINES=%d", height)
                                                columns := 
fmt.Sprintf("COLUMNS=%d", pwindow.Width())
@@ -2128,7 +2143,7 @@
                if len(command) > 0 && t.isPreviewEnabled() {
                        _, list := t.buildPlusList(command, false)
                        t.cancelPreview()
-                       t.previewBox.Set(reqPreviewEnqueue, 
previewRequest{command, t.pwindow, list})
+                       t.previewBox.Set(reqPreviewEnqueue, 
previewRequest{command, t.pwindow, t.evaluateScrollOffset(), list})
                }
        }
 
@@ -2183,9 +2198,11 @@
                                                t.suppress = false
                                        case reqReinit:
                                                t.tui.Resume(t.fullscreen, 
t.sigstop)
-                                               t.redraw()
+                                               t.redraw(true)
                                        case reqRedraw:
-                                               t.redraw()
+                                               t.redraw(false)
+                                       case reqFullRedraw:
+                                               t.redraw(true)
                                        case reqClose:
                                                exit(func() int {
                                                        if t.output() {
@@ -2253,13 +2270,15 @@
                                }
                        }
                }
-               togglePreview := func(enabled bool) {
+               togglePreview := func(enabled bool) bool {
                        if t.previewer.enabled != enabled {
                                t.previewer.enabled = enabled
-                               t.tui.Clear()
+                               // We need to immediately update t.pwindow so 
we don't use reqRedraw
                                t.resizeWindows()
                                req(reqPrompt, reqList, reqInfo, reqHeader)
+                               return true
                        }
+                       return false
                }
                toggle := func() bool {
                        current := t.currentItem()
@@ -2296,12 +2315,12 @@
                        }
                }
 
-               actionsFor := func(eventType tui.EventType) []action {
+               actionsFor := func(eventType tui.EventType) []*action {
                        return t.keymap[eventType.AsEvent()]
                }
 
-               var doAction func(action) bool
-               doActions := func(actions []action) bool {
+               var doAction func(*action) bool
+               doActions := func(actions []*action) bool {
                        for _, action := range actions {
                                if !doAction(action) {
                                        return false
@@ -2309,7 +2328,7 @@
                        }
                        return true
                }
-               doAction = func(a action) bool {
+               doAction = func(a *action) bool {
                        switch a.t {
                        case actIgnore:
                        case actExecute, actExecuteSilent:
@@ -2327,7 +2346,7 @@
                                                if valid {
                                                        t.cancelPreview()
                                                        
t.previewBox.Set(reqPreviewEnqueue,
-                                                               
previewRequest{t.previewOpts.command, t.pwindow, list})
+                                                               
previewRequest{t.previewOpts.command, t.pwindow, t.evaluateScrollOffset(), 
list})
                                                }
                                        }
                                }
@@ -2489,14 +2508,14 @@
                                }
                        case actToggleIn:
                                if t.layout != layoutDefault {
-                                       return doAction(action{t: actToggleUp})
+                                       return doAction(&action{t: actToggleUp})
                                }
-                               return doAction(action{t: actToggleDown})
+                               return doAction(&action{t: actToggleDown})
                        case actToggleOut:
                                if t.layout != layoutDefault {
-                                       return doAction(action{t: 
actToggleDown})
+                                       return doAction(&action{t: 
actToggleDown})
                                }
-                               return doAction(action{t: actToggleUp})
+                               return doAction(&action{t: actToggleUp})
                        case actToggleDown:
                                if t.multi > 0 && t.merger.Length() > 0 && 
toggle() {
                                        t.vmove(-1, true)
@@ -2520,7 +2539,7 @@
                                        req(reqClose)
                                }
                        case actClearScreen:
-                               req(reqRedraw)
+                               req(reqFullRedraw)
                        case actClearQuery:
                                t.input = []rune{}
                                t.cx = 0
@@ -2707,6 +2726,45 @@
                                for key := range keys {
                                        delete(t.keymap, key)
                                }
+                       case actChangePreview:
+                               if t.previewOpts.command != a.a {
+                                       togglePreview(len(a.a) > 0)
+                                       t.previewOpts.command = a.a
+                                       refreshPreview(t.previewOpts.command)
+                               }
+                       case actChangePreviewWindow:
+                               currentPreviewOpts := t.previewOpts
+
+                               // Reset preview options and apply the 
additional options
+                               t.previewOpts = t.initialPreviewOpts
+
+                               // Split window options
+                               tokens := strings.Split(a.a, "|")
+                               parsePreviewWindow(&t.previewOpts, tokens[0])
+                               if len(tokens) > 1 {
+                                       a.a = strings.Join(append(tokens[1:], 
tokens[0]), "|")
+                               }
+
+                               if t.previewOpts.hidden {
+                                       togglePreview(false)
+                               } else {
+                                       // Full redraw
+                                       if 
!currentPreviewOpts.sameLayout(t.previewOpts) {
+                                               if togglePreview(true) {
+                                                       
refreshPreview(t.previewOpts.command)
+                                               } else {
+                                                       req(reqRedraw)
+                                               }
+                                       } else if 
!currentPreviewOpts.sameContentLayout(t.previewOpts) {
+                                               t.previewed.version = 0
+                                               req(reqPreviewRefresh)
+                                       }
+
+                                       // Adjust scroll offset
+                                       if t.hasPreviewWindow() && 
currentPreviewOpts.scroll != t.previewOpts.scroll {
+                                               
scrollPreviewTo(t.evaluateScrollOffset())
+                                       }
+                               }
                        }
                        return true
                }
@@ -2714,7 +2772,7 @@
                if t.jumping == jumpDisabled {
                        actions := t.keymap[event.Comparable()]
                        if len(actions) == 0 && event.Type == tui.Rune {
-                               doAction(action{t: actRune})
+                               doAction(&action{t: actRune})
                        } else if !doActions(actions) {
                                continue
                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/src/util/util.go 
new/fzf-0.29.0/src/util/util.go
--- old/fzf-0.28.0/src/util/util.go     2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/src/util/util.go     2021-12-24 17:46:01.000000000 +0100
@@ -26,7 +26,7 @@
                        w = runewidth.StringWidth(s) + strings.Count(s, "\n")
                }
                width += w
-               if limit > 0 && width > limit {
+               if width > limit {
                        return width, idx
                }
                idx += len(rs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/src/util/util_test.go 
new/fzf-0.29.0/src/util/util_test.go
--- old/fzf-0.28.0/src/util/util_test.go        2021-11-03 17:05:07.000000000 
+0100
+++ new/fzf-0.29.0/src/util/util_test.go        2021-12-24 17:46:01.000000000 
+0100
@@ -38,3 +38,19 @@
                t.Error("Expected: false")
        }
 }
+
+func TestRunesWidth(t *testing.T) {
+       for _, args := range [][]int{
+               {100, 5, -1},
+               {3, 4, 3},
+               {0, 1, 0},
+       } {
+               width, overflowIdx := RunesWidth([]rune("hello"), 0, 0, args[0])
+               if width != args[1] {
+                       t.Errorf("Expected width: %d, actual: %d", args[1], 
width)
+               }
+               if overflowIdx != args[2] {
+                       t.Errorf("Expected overflow index: %d, actual: %d", 
args[2], overflowIdx)
+               }
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.28.0/test/test_go.rb 
new/fzf-0.29.0/test/test_go.rb
--- old/fzf-0.28.0/test/test_go.rb      2021-11-03 17:05:07.000000000 +0100
+++ new/fzf-0.29.0/test/test_go.rb      2021-12-24 17:46:01.000000000 +0100
@@ -2142,6 +2142,72 @@
       assert_equal expected.chomp, lines.take(6).join("\n")
     end
   end
+
+  def test_change_preview_window
+    tmux.send_keys "seq 1000 | #{FZF} --preview 'echo [[{}]]' --preview-window 
border-none --bind '" \
+      'a:change-preview(echo __{}__),' \
+      'b:change-preview-window(down)+change-preview(echo 
=={}==)+change-preview-window(up),' \
+      'c:change-preview(),d:change-preview-window(hidden),' \
+      "e:preview(printf 
::%${FZF_PREVIEW_COLUMNS}s{})+change-preview-window(up),f:change-preview-window(up,wrap)'",
 :Enter
+    tmux.until { |lines| assert_equal 1000, lines.item_count }
+    tmux.until { |lines| assert_includes lines[0], '[[1]]' }
+
+    # change-preview action permanently changes the preview command set by 
--preview
+    tmux.send_keys 'a'
+    tmux.until { |lines| assert_includes lines[0], '__1__' }
+    tmux.send_keys :Up
+    tmux.until { |lines| assert_includes lines[0], '__2__' }
+
+    # When multiple change-preview-window actions are bound to a single key,
+    # the last one wins and the updated options are immediately applied to the 
new preview
+    tmux.send_keys 'b'
+    tmux.until { |lines| assert_equal '==2==', lines[0] }
+    tmux.send_keys :Up
+    tmux.until { |lines| assert_equal '==3==', lines[0] }
+
+    # change-preview with an empty preview command closes the preview window
+    tmux.send_keys 'c'
+    tmux.until { |lines| refute_includes lines[0], '==' }
+
+    # change-preview again to re-open the preview window
+    tmux.send_keys 'a'
+    tmux.until { |lines| assert_equal '__3__', lines[0] }
+
+    # Hide the preview window with hidden flag
+    tmux.send_keys 'd'
+    tmux.until { |lines| refute_includes lines[0], '__3__' }
+
+    # One-off preview
+    tmux.send_keys 'e'
+    tmux.until do |lines|
+      assert_equal '::', lines[0]
+      refute_includes lines[1], '3'
+    end
+
+    # Wrapped
+    tmux.send_keys 'f'
+    tmux.until do |lines|
+      assert_equal '::', lines[0]
+      assert_equal '  3', lines[1]
+    end
+  end
+
+  def test_change_preview_window_rotate
+    tmux.send_keys "seq 100 | #{FZF} --preview-window left,border-none 
--preview 'echo hello' --bind '" \
+      "a:change-preview-window(right|down|up|hidden|)'", :Enter
+    3.times do
+      tmux.until { |lines| lines[0].start_with?('hello') }
+      tmux.send_keys 'a'
+      tmux.until { |lines| lines[0].end_with?('hello') }
+      tmux.send_keys 'a'
+      tmux.until { |lines| lines[-1].start_with?('hello') }
+      tmux.send_keys 'a'
+      tmux.until { |lines| assert_equal 'hello', lines[0] }
+      tmux.send_keys 'a'
+      tmux.until { |lines| refute_includes lines[0], 'hello' }
+      tmux.send_keys 'a'
+    end
+  end
 end
 
 module TestShell

++++++ vendor.tar.xz ++++++

Reply via email to