Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fzf for openSUSE:Factory checked in 
at 2026-09-21 12:21:39
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fzf (Old)
 and      /work/SRC/openSUSE:Factory/.fzf.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fzf"

Mon Sep 21 12:21:39 2026 rev:86 rq:1379326 version:0.74.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/fzf/fzf.changes  2026-08-18 16:37:39.038437211 
+0200
+++ /work/SRC/openSUSE:Factory/.fzf.new.383539/fzf.changes      2026-09-21 
12:21:41.510461242 +0200
@@ -1,0 +2,33 @@
+Sun Sep 20 22:27:24 UTC 2026 - Avindra Goolcharan <[email protected]>
+
+- Update to version 0.74.4:
+  * Fixed an escape sequence split across reads being parsed as a
+    fragment, which leaked the rest into the query (#4899)
+  * Fixed --tiebreak=pathname not detecting the last path
+    separator when the line contains a non-ASCII character before it (#4902)
+  * Fixed progress in the --listen status payload staying at 100
+    while a new search was running, which made a snapshot with a
+    new query and the previous result set look complete (#4903)
+  * Fixed adaptive height not reserving a line for the divider of
+    an inline header or footer border, so the list came up one
+    line short for each of them (#4904)
+  * Fixed fzf erasing the line the prompt was on when it exits,
+    which made the last line of the prompt flicker in fish, bash,
+    and nushell (#4913)
+  * Fixed fzf exiting with status 2 while waiting for a key, when
+    --listen is used and 100+ signals interrupt the wait (#4917)
+  * vim: fzf no longer blocks the editor, so live previews keep
+    working while fzf is open
+  * vim: The popup layout now works under Zellij
+  * vim: Added popup as a synonym of the tmux layout key
+  * vim: fzf now opens in a tmux or Zellij floating pane by
+    default, so the window it was started from stays visible and
+    can be used while fzf is running
+  * fish: Fixed custom CTRL-T command not using the prefixed
+    target directory in some cases (#4498)
+  * fish: Optimized description alignment of completion items (#4910)
+  * nushell: Added key bindings for Helix editing modes, on
+    nushell 0.115.0 or above (#4914)
+  * nushell: Fixed CTRL-T inserting the selected paths unquoted
+
+-------------------------------------------------------------------

Old:
----
  fzf-0.74.3.tar.gz

New:
----
  fzf-0.74.4.tar.gz

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

Other differences:
------------------
++++++ fzf.spec ++++++
--- /var/tmp/diff_new_pack.7GHMbK/_old  2026-09-21 12:21:42.251492226 +0200
+++ /var/tmp/diff_new_pack.7GHMbK/_new  2026-09-21 12:21:42.256492435 +0200
@@ -19,7 +19,7 @@
 
 %global _lto_cflags %{nil}
 Name:           fzf
-Version:        0.74.3
+Version:        0.74.4
 Release:        0
 Summary:        A command-line fuzzy finder
 License:        MIT

++++++ fzf-0.74.3.tar.gz -> fzf-0.74.4.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/.github/workflows/linux.yml 
new/fzf-0.74.4/.github/workflows/linux.yml
--- old/fzf-0.74.3/.github/workflows/linux.yml  2026-08-17 05:05:05.000000000 
+0200
+++ new/fzf-0.74.4/.github/workflows/linux.yml  2026-09-12 02:21:00.000000000 
+0200
@@ -38,7 +38,9 @@
         wget -qO- https://apt.fury.io/nushell/gpg.key | sudo gpg --dearmor -o 
/etc/apt/keyrings/fury-nushell.gpg
         echo "deb [signed-by=/etc/apt/keyrings/fury-nushell.gpg] 
https://apt.fury.io/nushell/ /" | sudo tee 
/etc/apt/sources.list.d/fury-nushell.list
         sudo apt-get update
-        sudo apt-get install --yes zsh fish tmux shfmt nushell
+        # Pinned: 0.115.0 runs external completers on a background thread, so
+        # fzf never receives keystrokes. 
https://github.com/nushell/nushell/issues/18771
+        sudo apt-get install --yes zsh fish tmux shfmt nushell=0.114.1-0
 
     - name: Install Ruby gems
       run: bundle install
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/CHANGELOG.md new/fzf-0.74.4/CHANGELOG.md
--- old/fzf-0.74.3/CHANGELOG.md 2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/CHANGELOG.md 2026-09-12 02:21:00.000000000 +0200
@@ -1,6 +1,36 @@
 CHANGELOG
 =========
 
+0.74.4
+------
+- Fixed an escape sequence split across reads being parsed as a fragment, 
which leaked the rest into the query (#4899)
+    - e.g. A terminal answering the startup `DECRQM` query late left 
`?2004;2$y`, CTRL-UP left `5A`, and SGR mouse input left `0;1;1M`
+- Fixed `--tiebreak=pathname` not detecting the last path separator when the 
line contains a non-ASCII character before it (#4902)
+- Fixed `progress` in the `--listen` status payload staying at 100 while a new 
search was running, which made a snapshot with a new query and the previous 
result set look complete (#4903)
+    - It is now reset when a search starts and reaches 100 on the final 
result, so `progress` of 100 means the matches belong to the query reported 
next to them
+- Fixed adaptive height not reserving a line for the divider of an inline 
header or footer border, so the list came up one line short for each of them 
(#4904)
+    - e.g. `seq 10 | fzf --height=~100% --list-border --header-lines=1 
--header-lines-border=inline`
+- Fixed fzf erasing the line the prompt was on when it exits, which made the 
last line of the prompt flicker in fish, bash, and nushell (#4913)
+- Fixed fzf exiting with status 2 while waiting for a key, when `--listen` is 
used and 100+ signals interrupt the wait (#4917)
+- Vim plugin
+    - fzf no longer blocks the editor, so live previews keep working while fzf 
is open
+        - `fzf#run` returns an empty list when it runs fzf asynchronously. Use 
`sink`, `sinklist`, or `exit` to get the result
+    - The popup layout now works under Zellij
+    - Added `popup` as a synonym of the `tmux` layout key
+      ```vim
+      let g:fzf_layout = { 'popup': '90%,70%' }
+      ```
+    - fzf now opens in a tmux or Zellij floating pane by default, so the 
window it was started from stays visible and can be used while fzf is running
+        - Requires tmux 3.7+ or Zellij 0.44+
+        - Set `g:fzf_layout` to pick a different layout
+- fish
+    - Fixed custom CTRL-T command not using the prefixed target directory in 
some cases (#4498) (@bitraid)
+    - Optimized description alignment of completion items (#4910) (@bitraid)
+- nushell
+    - Added key bindings for Helix editing modes, on nushell 0.115.0 or above 
(#4914) (@sim590)
+    - Fixed CTRL-T inserting the selected paths unquoted
+        - p4p3r (@P4P3R-HAK) reported the security vulnerability and suggested 
the fix
+
 0.74.3
 ------
 - Performance optimizations for non-ASCII input
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/README-VIM.md new/fzf-0.74.4/README-VIM.md
--- old/fzf-0.74.3/README-VIM.md        2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/README-VIM.md        2026-09-12 02:21:00.000000000 +0200
@@ -1,6 +1,26 @@
 FZF Vim integration
 ===================
 
+<!-- vim-markdown-toc GFM -->
+
+* [Installation](#installation)
+* [Summary](#summary)
+* [`:FZF[!]`](#fzf)
+    * [Configuration](#configuration)
+        * [Examples](#examples)
+            * [Explanation of `g:fzf_colors`](#explanation-of-gfzf_colors)
+* [`fzf#run`](#fzfrun)
+* [`fzf#wrap`](#fzfwrap)
+    * [Global options supported by 
`fzf#wrap`](#global-options-supported-by-fzfwrap)
+* [Tips](#tips)
+    * [fzf inside terminal buffer](#fzf-inside-terminal-buffer)
+    * [Starting fzf in a Vim popup window](#starting-fzf-in-a-vim-popup-window)
+    * [Starting fzf in a tmux/Zellij popup 
window](#starting-fzf-in-a-tmuxzellij-popup-window)
+    * [Hide statusline](#hide-statusline)
+* [License](#license)
+
+<!-- vim-markdown-toc -->
+
 Installation
 ------------
 
@@ -133,19 +153,35 @@
   \ 'ctrl-v': 'vsplit' }
 
 " Default fzf layout
-" - Popup window (center of the screen)
-let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+if exists('$TMUX') || exists('$ZELLIJ')
+  " The Vim plugin will try to open fzf in a tmux or Zellij popup
+  " if possible (requires recent fzf and tmux/zellij) using --popup option,
+  " with the following argument:
+  let g:fzf_layout = { 'popup': '90%,60%' }
+else
+  " If --popup option is not available, it will open in a popup window inside
+  " Vim (center of the screen)
+  let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+endif
 
-" - Popup window (center of the current window)
+" Here are some more layout examples:
+
+" - Tmux or Zellij popup at the bottom 40%
+let g:fzf_layout = { 'popup': 'bottom,40%' }
+
+" - Tmux or Zellij popup at the top with a different size
+let g:fzf_layout = { 'popup': 'top,90%,40%' }
+
+" - Vim popup window: at the center of the current window (relative)
 let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': 
v:true } }
 
-" - Popup window (anchored to the bottom of the current window)
+" - Vim popup window: anchored to the bottom of the current window
 let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': 
v:true, 'yoffset': 1.0 } }
 
-" - down / up / left / right
+" - Vim split window: down / up / left / right
 let g:fzf_layout = { 'down': '40%' }
 
-" - Window using a Vim command
+" - Vim window using a Vim command
 let g:fzf_layout = { 'window': 'enew' }
 let g:fzf_layout = { 'window': '-tabnew' }
 let g:fzf_layout = { 'window': '10new' }
@@ -296,7 +332,7 @@
 | `options`                  | string/list   | Options to fzf                  
                                      |
 | `dir`                      | string        | Working directory               
                                      |
 | `up`/`down`/`left`/`right` | number/string | (Layout) Window position and 
size (e.g. `20`, `50%`)                  |
-| `tmux`                     | string        | (Layout) `--tmux` options (e.g. 
`90%,70%`)                            |
+| `popup`/`tmux`             | string        | (Layout) `--popup` options 
(e.g. `90%,70%`)                           |
 | `window` (Vim 8 / Neovim)  | string        | (Layout) Command to open fzf 
window (e.g. `vertical aboveleft 30new`) |
 | `window` (Vim 8 / Neovim)  | dict          | (Layout) Popup window settings 
(e.g. `{'width': 0.9, 'height': 0.6}`) |
 
@@ -408,10 +444,10 @@
 
 ### fzf inside terminal buffer
 
-On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
-If you find the default ANSI colors to be different, consider configuring the
-colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
-in Neovim.
+When fzf is configured to start in a terminal buffer inside Vim or Neovim, you
+may find the default ANSI colors to be different. In that case, configure the
+colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x` in
+Neovim.
 
 ```vim
 " Terminal colors for seoul256 color scheme
@@ -442,7 +478,10 @@
 endif
 ```
 
-### Starting fzf in a popup window
+### Starting fzf in a Vim popup window
+
+You can configure fzf to start in a Vim popup window by setting the `window` 
key
+in `g:fzf_layout`.
 
 ```vim
 " Required:
@@ -458,16 +497,32 @@
 let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
 ```
 
-Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
-or above) by putting `--tmux` option value in `tmux` key.
+### Starting fzf in a tmux/Zellij popup window
 
-```vim
-" See `--tmux` option in `man fzf` for available options
-" [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
-if exists('$TMUX')
-  let g:fzf_layout = { 'tmux': '90%,70%' }
+fzf can also start in a popup of the multiplexer instead of a window inside
+Vim, by putting a `--popup` option value in the `popup` key. `tmux` is
+accepted as a synonym, just as `--tmux` is an alias of `--popup`.
+
+The layout works on tmux 3.3 or above, or on Zellij 0.44 or above with fzf
+0.71.0 or above. It is the default on tmux 3.7 or above with fzf 0.74.0 or
+above, and on Zellij, where the pane is not modal: Vim keeps the window fzf
+was started from visible, and you can switch to it while fzf is open. Below
+those versions tmux gives a popup that cannot be left, so a window inside Vim
+is the default there. On tmux, an explicit `--border` style also gives a modal
+popup rather than a floating pane, because the native border of a tmux
+floating pane cannot be removed. Drop `--border` to keep the floating pane and
+its native border. Zellij keeps the floating pane either way, and hides its
+native border when fzf draws one. Set `g:fzf_layout` yourself to choose either
+one.
+
+```vim
+if exists('$TMUX') || exists('$ZELLIJ')
+  " See `--popup` option in `man fzf` for available options
+  " [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
+  let g:fzf_layout = { 'popup': '90%,70%' }
 else
-  let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+  " Configure the Vim popup window in case not on the multiplexer
+  let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.7 } }
 endif
 ```
 
@@ -475,7 +530,8 @@
 
 When fzf starts in a terminal buffer, the file type of the buffer is set to
 `fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
-the window.
+the window. This applies to the layouts that open inside Vim, not to the tmux
+or Zellij pane the default uses, which is not a buffer.
 
 For example, if you open fzf on the bottom on the screen (e.g. `{'down':
 '40%'}`), you might want to temporarily disable the statusline for a cleaner
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/RELEASE.md new/fzf-0.74.4/RELEASE.md
--- old/fzf-0.74.3/RELEASE.md   2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/RELEASE.md   2026-09-12 02:21:00.000000000 +0200
@@ -18,7 +18,7 @@
 2. Verify file consistency, sign the tag, and push the tag.
 
     ```sh
-    make tag VERSION=0.74.3
+    make tag VERSION=0.74.4
     ```
 
     `make tag` runs `prerelease` first (checks that the version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/doc/fzf.txt new/fzf-0.74.4/doc/fzf.txt
--- old/fzf-0.74.3/doc/fzf.txt  2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/doc/fzf.txt  2026-09-12 02:21:00.000000000 +0200
@@ -14,7 +14,8 @@
       Global options supported by fzf#wrap  
|fzf-global-options-supported-by-fzf#wrap|
     Tips                                    |fzf-tips|
       fzf inside terminal buffer            |fzf-inside-terminal-buffer|
-      Starting fzf in a popup window        
|fzf-starting-fzf-in-a-popup-window|
+      Starting fzf in a Vim popup window    
|fzf-starting-fzf-in-a-vim-popup-window|
+      Starting fzf in a tmux/Zellij popup window  
|fzf-starting-fzf-in-a-tmuxzellij-popup-window|
       Hide statusline                       |fzf-hide-statusline|
     License                                 |fzf-license|
 
@@ -161,19 +162,35 @@
       \ 'ctrl-v': 'vsplit' }
 
     " Default fzf layout
-    " - Popup window (center of the screen)
-    let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+    if exists('$TMUX') || exists('$ZELLIJ')
+      " The Vim plugin will try to open fzf in a tmux or Zellij popup
+      " if possible (requires recent fzf and tmux/zellij) using --popup option,
+      " with the following argument:
+      let g:fzf_layout = { 'popup': '90%,60%' }
+    else
+      " If --popup option is not available, it will open in a popup window 
inside
+      " Vim (center of the screen)
+      let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+    endif
+
+    " Here are some more layout examples:
+
+    " - Tmux or Zellij popup at the bottom 40%
+    let g:fzf_layout = { 'popup': 'bottom,40%' }
 
-    " - Popup window (center of the current window)
+    " - Tmux or Zellij popup at the top with a different size
+    let g:fzf_layout = { 'popup': 'top,90%,40%' }
+
+    " - Vim popup window: at the center of the current window (relative)
     let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': 
v:true } }
 
-    " - Popup window (anchored to the bottom of the current window)
+    " - Vim popup window: anchored to the bottom of the current window
     let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6, 'relative': 
v:true, 'yoffset': 1.0 } }
 
-    " - down / up / left / right
+    " - Vim split window: down / up / left / right
     let g:fzf_layout = { 'down': '40%' }
 
-    " - Window using a Vim command
+    " - Vim window using a Vim command
     let g:fzf_layout = { 'window': 'enew' }
     let g:fzf_layout = { 'window': '-tabnew' }
     let g:fzf_layout = { 'window': '10new' }
@@ -310,7 +327,7 @@
   `options`                   | string/list   | Options to fzf
   `dir`                       | string        | Working directory
   `up` / `down` / `left` / `right`  | number/string | (Layout) Window position 
and size (e.g.  `20` ,  `50%` )
-  `tmux`                      | string        | (Layout) `--tmux` options 
(e.g.  `90%,70%` )
+  `popup` / `tmux`              | string        | (Layout) `--popup` options 
(e.g.  `90%,70%` )
   `window`  (Vim 8 / Neovim)  | string        | (Layout) Command to open fzf 
window (e.g.  `vertical aboveleft 30new` )
   `window`  (Vim 8 / Neovim)  | dict          | (Layout) Popup window settings 
(e.g.  `{'width': 0.9, 'height': 0.6}` )
  
---------------------------+---------------+----------------------------------------------------------------------
@@ -418,8 +435,8 @@
                                                     
*fzf-inside-terminal-buffer*
 
 
-On the latest versions of Vim and Neovim, fzf will start in a terminal buffer.
-If you find the default ANSI colors to be different, consider configuring the
+When fzf is configured to start in a terminal buffer inside Vim or Neovim, you
+may find the default ANSI colors to be different. In that case, configure the
 colors using `g:terminal_ansi_colors` in regular Vim or `g:terminal_color_x`
 in Neovim.
 
@@ -452,8 +469,11 @@
     endif
 <
 
-< Starting fzf in a popup window >____________________________________________~
-                                            
*fzf-starting-fzf-in-a-popup-window*
+< Starting fzf in a Vim popup window >________________________________________~
+                                        
*fzf-starting-fzf-in-a-vim-popup-window*
+
+You can configure fzf to start in a Vim popup window by setting the `window`
+key in `g:fzf_layout`.
 >
     " Required:
     " - width [float range [0 ~ 1]] or [integer range [8 ~ ]]
@@ -467,15 +487,33 @@
     "   - 'rounded' / 'sharp' / 'horizontal' / 'vertical' / 'top' / 'bottom' / 
'left' / 'right'
     let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
 <
-Alternatively, you can make fzf open in a tmux popup window (requires tmux 3.2
-or above) by putting `--tmux` options in `tmux` key.
->
-    " See `--tmux` option in `man fzf` for available options
-    " [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
-    if exists('$TMUX')
-      let g:fzf_layout = { 'tmux': '90%,70%' }
+
+< Starting fzf in a tmux/Zellij popup window >________________________________~
+                                 
*fzf-starting-fzf-in-a-tmuxzellij-popup-window*
+
+fzf can also start in a popup of the multiplexer instead of a window inside
+Vim, by putting a `--popup` option value in the `popup` key. `tmux` is
+accepted as a synonym, just as `--tmux` is an alias of `--popup`.
+
+The layout works on tmux 3.3 or above, or on Zellij 0.44 or above with fzf
+0.71.0 or above. It is the default on tmux 3.7 or above with fzf 0.74.0 or
+above, and on Zellij, where the pane is not modal: Vim keeps the window fzf
+was started from visible, and you can switch to it while fzf is open. Below
+those versions tmux gives a popup that cannot be left, so a window inside Vim
+is the default there. On tmux, an explicit `--border` style also gives a modal
+popup rather than a floating pane, because the native border of a tmux
+floating pane cannot be removed. Drop `--border` to keep the floating pane and
+its native border. Zellij keeps the floating pane either way, and hides its
+native border when fzf draws one. Set `g:fzf_layout` yourself to choose either
+one.
+>
+    if exists('$TMUX') || exists('$ZELLIJ')
+      " See `--popup` option in `man fzf` for available options
+      " [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
+      let g:fzf_layout = { 'popup': '90%,70%' }
     else
-      let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+      " Configure the Vim popup window in case not on the multiplexer
+      let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.7 } }
     endif
 <
 
@@ -484,7 +522,8 @@
 
 When fzf starts in a terminal buffer, the file type of the buffer is set to
 `fzf`. So you can set up `FileType fzf` autocmd to customize the settings of
-the window.
+the window. This applies to the layouts that open inside Vim, not to the tmux
+or Zellij pane the default uses, which is not a buffer.
 
 For example, if you open fzf on the bottom on the screen (e.g. `{'down':
 '40%'}`), you might want to temporarily disable the statusline for a cleaner
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/install new/fzf-0.74.4/install
--- old/fzf-0.74.3/install      2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/install      2026-09-12 02:21:00.000000000 +0200
@@ -2,7 +2,7 @@
 
 set -u
 
-version=0.74.3
+version=0.74.4
 auto_completion=
 key_bindings=
 update_config=2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/install.ps1 new/fzf-0.74.4/install.ps1
--- old/fzf-0.74.3/install.ps1  2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/install.ps1  2026-09-12 02:21:00.000000000 +0200
@@ -1,4 +1,4 @@
-$version="0.74.3"
+$version="0.74.4"
 
 $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/man/man1/fzf-tmux.1 
new/fzf-0.74.4/man/man1/fzf-tmux.1
--- old/fzf-0.74.3/man/man1/fzf-tmux.1  2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/man/man1/fzf-tmux.1  2026-09-12 02:21:00.000000000 +0200
@@ -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 "Aug 2026" "fzf 0.74.3" "fzf\-tmux - open fzf in tmux split 
pane"
+.TH fzf\-tmux 1 "Sep 2026" "fzf 0.74.4" "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.74.3/man/man1/fzf.1 
new/fzf-0.74.4/man/man1/fzf.1
--- old/fzf-0.74.3/man/man1/fzf.1       2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/man/man1/fzf.1       2026-09-12 02:21:00.000000000 +0200
@@ -21,7 +21,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ..
-.TH fzf 1 "Aug 2026" "fzf 0.74.3" "fzf - a command-line fuzzy finder"
+.TH fzf 1 "Sep 2026" "fzf 0.74.4" "fzf - a command-line fuzzy finder"
 
 .SH NAME
 fzf - a command-line fuzzy finder
@@ -408,6 +408,9 @@
 * Cannot be used with top/bottom margin and padding given in percent size
 .br
 * It will not find the right size when there are multi-line items
+.br
+* fzf cannot start until the input ends or exceeds the height, so a stream
+that stays below it will delay the first render
 
 .TP
 .BI "\-\-min\-height=" "HEIGHT[+]"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/plugin/fzf.vim 
new/fzf-0.74.4/plugin/fzf.vim
--- old/fzf-0.74.3/plugin/fzf.vim       2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/plugin/fzf.vim       2026-09-12 02:21:00.000000000 +0200
@@ -24,7 +24,8 @@
 if exists('g:loaded_fzf')
   finish
 endif
-let g:loaded_fzf = 1
+" Bumped when the plugin gains behavior a caller may need to detect
+let g:loaded_fzf = 20260821
 
 let s:is_win = has('win32') || has('win64')
 if s:is_win && &shellslash
@@ -127,7 +128,7 @@
   return s:fzf_call('tempname')
 endfunction
 
-let s:layout_keys = ['window', 'tmux', 'up', 'down', 'left', 'right']
+let s:layout_keys = ['window', 'popup', 'tmux', 'up', 'down', 'left', 'right']
 let s:fzf_go = s:base_dir.'/bin/fzf'
 let s:fzf_tmux = s:base_dir.'/bin/fzf-tmux'
 
@@ -139,6 +140,15 @@
 endfunction
 
 function! s:default_layout()
+  " A floating pane leaves the window fzf was started from visible and
+  " reachable while fzf is open. A popup covers it, inside Vim or not.
+  " Without a job, s:execute_tmux() blocks and freezes Vim. fzf also wants a
+  " tmux window of at least 3x3, and Vim's pane is never larger than the
+  " window, so asking Vim is free and survives a resize
+  if (has('nvim') || has('job')) && &columns >= 3 && &lines >= 3
+        \ && s:tmux_enabled() && get(s:, 'tmux_floating', 0)
+    return { 'tmux': '90%,60%' }
+  endif
   return s:popup_support()
         \ ? { 'window' : { 'width': 0.9, 'height': 0.6 } }
         \ : { 'down': '~40%' }
@@ -164,6 +174,12 @@
   if v:shell_error
     throw 'Failed to download fzf: '.script
   endif
+
+  " A new binary invalidates the chosen executable and everything derived from
+  " its version, including whether fzf opens a floating pane. fzf#install() is
+  " also the vim-plug 'do' hook, so this can run long after the first fzf call
+  let [s:versions, s:checked] = [{}, {}]
+  unlet! s:exec s:tmux s:tmux_floating
 endfunction
 
 let s:versions = {}
@@ -255,8 +271,24 @@
   return s:exec
 endfunction
 
+" Path to the fzf-tmux script, or an empty string if it is not available. Only
+" the legacy options still need it. --popup is handled by fzf itself.
+function! s:fzf_tmux_script()
+  if !executable(s:fzf_tmux)
+    if !executable('fzf-tmux')
+      return ''
+    endif
+    let s:fzf_tmux = 'fzf-tmux'
+  endif
+  return s:fzf_tmux
+endfunction
+
 function! s:tmux_enabled()
-  if has('gui_running') || !exists('$TMUX')
+  if has('gui_running')
+    return 0
+  endif
+
+  if empty($TMUX) && empty($ZELLIJ)
     return 0
   endif
 
@@ -264,17 +296,42 @@
     return s:tmux
   endif
 
-  let s:tmux = 0
-  if !executable(s:fzf_tmux)
-    if executable('fzf-tmux')
-      let s:fzf_tmux = 'fzf-tmux'
-    else
-      return 0
-    endif
+  let [s:tmux, s:tmux_floating] = [0, 0]
+
+  " --popup covers Zellij as well, where the fzf-tmux script and the tmux
+  " version are irrelevant. fzf learned it in 0.71.0, and the floating pane
+  " options it passes need Zellij 0.44 or above. fzf checks tmux first, so
+  " this branch is Zellij without tmux. Both non-empty means tmux wins.
+  " empty(), not exists(), to match how fzf reads the two variables
+  if empty($TMUX)
+    let s:tmux =
+          \ s:compare_versions(s:get_version(s:fzf_binary()), '0.71.0') >= 0
+          \ && s:compare_versions(s:zellij_version(), '0.44') >= 0
+    let s:tmux_floating = s:tmux
+    return s:tmux
   endif
 
   let output = system('tmux -V')
-  let s:tmux = !v:shell_error && output >= 'tmux 1.7'
+  if v:shell_error
+    return s:tmux
+  endif
+  " e.g. 'tmux 3.7b', 'tmux next-3.8'
+  let ver = matchstr(output, '\d\+\.\d\+')
+
+  " --popup requires tmux 3.3 or above, and needs no fzf-tmux script. The
+  " default layout wants a floating pane, which also needs fzf 0.74.0 or
+  " above. fzf opens a modal popup otherwise. The version here only skips the
+  " probe for servers too old to answer it
+  if s:compare_versions(ver, '3.3') >= 0
+    let s:tmux = 1
+    let s:tmux_floating = s:compare_versions(ver, '3.7') >= 0
+          \ && s:tmux_floating_pane_support()
+          \ && s:compare_versions(s:get_version(s:fzf_binary()), '0.74.0') >= 0
+    return s:tmux
+  endif
+
+  " Older versions still go through the script
+  let s:tmux = !empty(s:fzf_tmux_script()) && s:compare_versions(ver, '1.7') 
>= 0
   return s:tmux
 endfunction
 
@@ -422,7 +479,10 @@
     if !exists('g:fzf_layout') && exists('g:fzf_height')
       let opts.down = g:fzf_height
     else
-      let opts = extend(opts, s:validate_layout(get(g:, 'fzf_layout', 
s:default_layout())))
+      " Not get(), which would evaluate s:default_layout() and run its version
+      " checks even when g:fzf_layout makes the answer irrelevant
+      let opts = extend(opts, s:validate_layout(
+            \ exists('g:fzf_layout') ? g:fzf_layout : s:default_layout()))
     endif
   endif
 
@@ -502,6 +562,11 @@
   let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()
 
   let dict   = exists('a:1') ? copy(a:1) : {}
+  " 'popup' and 'tmux' are synonyms, as --popup and --tmux are. Normalize here
+  " so that the rest of the function only has to know about one of them.
+  if has_key(dict, 'popup')
+    let dict.tmux = remove(dict, 'popup')
+  endif
   let temps  = { 'result': s:fzf_tempname() }
   let optstr = s:evaluate_opts(get(dict, 'options', ''))
   try
@@ -539,16 +604,19 @@
         \ executable('tput') && filereadable('/dev/tty')
   let has_vim8_term = has('terminal') && has('patch-8.0.995')
   let has_nvim_term = has('nvim-0.2.1') || has('nvim') && !s:is_win
-  let use_term = has_nvim_term || has_vim8_term
-    \ && !s:need_cmd_window
-    \ && (has('gui_running') || s:is_win || s:present(dict, 'down', 'up', 
'left', 'right', 'window'))
+  let use_term = (has_nvim_term || has_vim8_term) && !s:need_cmd_window
   let use_tmux = (has_key(dict, 'tmux') || (!use_height && !use_term || 
prefer_tmux) && !has('win32unix') && s:splittable(dict)) && s:tmux_enabled()
   if prefer_tmux && use_tmux
     let use_height = 0
     let use_term = 0
   endif
   if use_term
-    let optstr .= ' --no-height --no-tmux'
+    let optstr .= ' --no-height'
+    " Cancel a --popup from $FZF_DEFAULT_OPTS only when the spec asks for a Vim
+    " window. Without a layout option, respect the user's preference.
+    if s:present(dict, 'window', 'up', 'down', 'left', 'right')
+      let optstr .= ' --no-tmux'
+    endif
   elseif use_height
     let height = s:calc_size(&lines, dict.down, dict)
     let optstr .= ' --no-tmux --height='.height
@@ -568,8 +636,12 @@
     return s:execute_term(dict, command, temps)
   endif
 
-  let lines = use_tmux ? s:execute_tmux(dict, command, temps)
-                 \ : s:execute(dict, command, use_height, temps)
+  " s:execute_tmux may run fzf asynchronously, so it calls s:callback itself
+  if use_tmux
+    return s:execute_tmux(dict, command, temps)
+  endif
+
+  let lines = s:execute(dict, command, use_height, temps)
   call s:callback(dict, lines)
   return lines
 finally
@@ -586,6 +658,44 @@
   return 0
 endfunction
 
+" The binary fzf#exec() would choose, without its prompting or installing.
+" Layout selection runs before fzf#exec() has resolved one
+function! s:fzf_binary()
+  if exists('s:exec')
+    return s:exec
+  endif
+  let bins = filter(['fzf', s:fzf_go], 'executable(v:val)')
+  if empty(bins)
+    return ''
+  endif
+  return len(bins) > 1 ? sort(bins, 's:compare_binary_versions')[-1] : bins[0]
+endfunction
+
+function! s:zellij_version()
+  if !exists('s:zellij_ver')
+    let output = systemlist('zellij --version')
+    let s:zellij_ver = v:shell_error || empty(output)
+          \ ? '' : matchstr(output[0], '[0-9.]\+')
+  endif
+  return s:zellij_ver
+endfunction
+
+" Whether the running server can put fzf in a floating pane. fzf decides on
+" the server, not on the version the tmux client reports, so ask it the same
+" question rather than predicting the answer. See tmuxFloatingPaneInfo in
+" src/tmux.go, which also requires the tmux window to be at least 3x3; that
+" one changes with a resize, so s:default_layout() reads it from Vim instead
+" of caching it here
+function! s:tmux_floating_pane_support()
+  " fzf does not use a floating pane when it was not started from a pane
+  if empty($TMUX_PANE)
+    return 0
+  endif
+  let out = system('tmux list-commands new-pane')
+  " A server that does not know the command exits normally with no output
+  return !v:shell_error && out =~# 'new-pane'
+endfunction
+
 function! s:fzf_tmux(dict)
   let size = get(a:dict, 'tmux', '')
   if empty(size)
@@ -597,10 +707,15 @@
     endfor
   endif
 
-  " Legacy fzf-tmux options
-  if size =~ '-'
+  " Legacy fzf-tmux options are flags. A --tmux value never starts with a dash,
+  " but may contain one, as in '90%,60%,border-native'
+  if size =~ '^-'
+    let script = s:fzf_tmux_script()
+    if empty(script)
+      throw 'fzf-tmux not found, required for the legacy option: ' . size
+    endif
     return printf('LINES=%d COLUMNS=%d %s %s %s --',
-          \ &lines, &columns, fzf#shellescape(s:fzf_tmux), size, 
(has_key(a:dict, 'source') ? '' : '-'))
+          \ &lines, &columns, fzf#shellescape(script), size, (has_key(a:dict, 
'source') ? '' : '-'))
   end
 
   " Using native --tmux option
@@ -742,6 +857,42 @@
   return s:exit_handler(a:dict, exit_status, command) < 2 ? lines : []
 endfunction
 
+" Returns 0 if the job could not be started
+function! s:start_popup_job(dict, command, temps) abort
+  let fzf = { 'dict': a:dict, 'temps': a:temps, 'command': a:command }
+  " Vim passes (job, status) and Nvim (id, status, event)
+  function! fzf.on_exit(id, code, ...) abort
+    redraw!
+    let lines = s:collect(self.temps)
+    if s:exit_handler(self.dict, a:code, self.command, 1) >= 2
+      return
+    endif
+    call s:pushd(self.dict)
+    call s:callback(self.dict, lines)
+  endfunction
+
+  if has('nvim')
+    return jobstart([&shell, &shellcmdflag, a:command], fzf) > 0
+  endif
+
+  " The command redirects to the result file, so no stream is of interest
+  let opts = { 'exit_cb': function(fzf.on_exit),
+        \ 'in_io': 'null', 'out_io': 'null', 'err_io': 'null' }
+
+  " job_start() gives the child $TERM=dumb, and the popup inherits the
+  " environment from here, so fzf would lose its colors. 'env' was added in
+  " 8.0.902, so set it in the command itself on older versions.
+  let command = a:command
+  if has('patch-8.0.902')
+    let opts.env = { 'TERM': $TERM }
+  elseif !s:is_win
+    let command = join(['export TERM=' . fzf#shellescape($TERM) . ';', 
command])
+  endif
+
+  let job = job_start([&shell, &shellcmdflag, command], opts)
+  return job_status(job) !=# 'fail'
+endfunction
+
 function! s:execute_tmux(dict, command, temps) abort
   let command = a:command
   let cwd = s:pushd(a:dict)
@@ -750,11 +901,24 @@
     let command = join(['cd', fzf#shellescape(cwd), '&&', command])
   endif
 
+  " fzf draws in a pane of its own, so the process we start here only waits for
+  " it. Hold it with a job instead of system() so that Vim keeps processing its
+  " event loop, and nothing has to be displayed for it. Fall back to the
+  " blocking path when the job cannot be started, or the sink would never run
+  " and the temporary files would be left behind.
+  if (has('nvim') || has('job')) && s:start_popup_job(a:dict, command, a:temps)
+    " Restore the working directory while fzf runs. on_exit pushes it again
+    call s:dopopd()
+    return []
+  endif
+
   call system(command)
   let exit_status = v:shell_error
   redraw!
   let lines = s:collect(a:temps)
-  return s:exit_handler(a:dict, exit_status, command) < 2 ? lines : []
+  let lines = s:exit_handler(a:dict, exit_status, command) < 2 ? lines : []
+  call s:callback(a:dict, lines)
+  return lines
 endfunction
 
 function! s:calc_size(max, val, dict)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/shell/completion.fish 
new/fzf-0.74.4/shell/completion.fish
--- old/fzf-0.74.3/shell/completion.fish        2026-08-17 05:05:05.000000000 
+0200
+++ new/fzf-0.74.4/shell/completion.fish        2026-09-12 02:21:00.000000000 
+0200
@@ -124,10 +124,8 @@
 
       # Determine the tabstop length for description alignment
       set -l -- max_columns (math $COLUMNS - 40)
-      for i in $list[1..500]
-        set -l -- item (string split -f 1 -- \t $i)
-        and set -l -- len (string length -V -- $item)
-        and test "$len" -gt "$tabstop" -a "$len" -lt "$max_columns"
+      for len in (string match -r -- '^[^\\t]*(?=\\t)' $list[1..500] | string 
length -V)
+        test "$len" -gt "$tabstop" -a "$len" -lt "$max_columns"
         and set -- tabstop $len
       end
       set -- tabstop (math $tabstop + 4)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/shell/key-bindings.fish 
new/fzf-0.74.4/shell/key-bindings.fish
--- old/fzf-0.74.3/shell/key-bindings.fish      2026-08-17 05:05:05.000000000 
+0200
+++ new/fzf-0.74.4/shell/key-bindings.fish      2026-09-12 02:21:00.000000000 
+0200
@@ -104,7 +104,7 @@
   # Store current token in $dir as root for the 'find' command
   function fzf-file-widget -d "List files and folders"
     set -l commandline (__fzf_parse_commandline)
-    set -lx dir $commandline[1]
+    set -l dir $commandline[1]
     set -l fzf_query $commandline[2]
     set -l prefix $commandline[3]
 
@@ -112,10 +112,17 @@
       "--reverse --walker=file,dir,follow,hidden --scheme=path" \
       "--multi $FZF_CTRL_T_OPTS --print0")
 
-    set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
     set -lx FZF_DEFAULT_OPTS_FILE
 
-    set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | 
string split0)
+    set -lx FZF_DEFAULT_COMMAND
+
+    if test -n "$FZF_CTRL_T_COMMAND"
+      set -f result (eval $FZF_CTRL_T_COMMAND \| (__fzfcmd) --query=$fzf_query 
| string split0)
+    else
+      set -f result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | 
string split0)
+    end
+
+    test -n "$result"
     and commandline -rt -- (string join -- ' ' $prefix(string escape -n -- 
$result))' '
 
     commandline -f repaint
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/shell/key-bindings.nu 
new/fzf-0.74.4/shell/key-bindings.nu
--- old/fzf-0.74.3/shell/key-bindings.nu        2026-08-17 05:05:05.000000000 
+0200
+++ new/fzf-0.74.4/shell/key-bindings.nu        2026-09-12 02:21:00.000000000 
+0200
@@ -47,6 +47,20 @@
   ['fzf']
 }
 
+# Keybinding modes to activate. The Helix modes only exist since Nushell
+# 0.115.0, so they are only included when running a version that supports
+# them (major > 0 covers a hypothetical 1.0+ where the minor resets).
+def __fzf_modes []: nothing -> list<string> {
+  let v = version
+  let major = ($v.major | into int)
+  let minor = ($v.minor | into int)
+  if ($major > 0) or ($minor >= 115) {
+    ['emacs', 'vi_normal', 'vi_insert', 'helix_normal', 'helix_select', 
'helix_insert']
+  } else {
+    ['emacs', 'vi_normal', 'vi_insert']
+  }
+}
+
 
 export-env {
   $env.FZF_CTRL_T_OPTS     = $env.FZF_CTRL_T_OPTS?     | default ""
@@ -55,11 +69,11 @@
 }
 
 # Directories
-const alt_c = {
+let alt_c = {
     name: fzf_dirs
     modifier: alt
     keycode: char_c
-    mode: [emacs, vi_normal, vi_insert]
+    mode: (__fzf_modes)
     event: [
       {
         send: executehostcommand
@@ -82,11 +96,11 @@
 }
 
 # History
-const ctrl_r = {
+let ctrl_r = {
   name: fzf_history
   modifier: control
   keycode: char_r
-  mode: [emacs, vi_insert, vi_normal]
+  mode: (__fzf_modes)
   event: [
     {
       send: executehostcommand
@@ -115,16 +129,16 @@
 }
 
 # Files
-const ctrl_t =  {
+let ctrl_t =  {
     name: fzf_files
     modifier: control
     keycode: char_t
-    mode: [emacs, vi_normal, vi_insert]
+    mode: (__fzf_modes)
     event: [
       {
         send: executehostcommand
         cmd: "
-          let fzf_opts = (__fzf_defaults '--reverse 
--walker=file,dir,follow,hidden --scheme=path' $'($env.FZF_CTRL_T_OPTS) -m');
+          let fzf_opts = (__fzf_defaults '--reverse 
--walker=file,dir,follow,hidden --scheme=path' $'($env.FZF_CTRL_T_OPTS) -m 
--print0');
           let fzfcmd = (__fzfcmd);
           let fzf_args = ($fzfcmd | skip 1);
           let ctrl_t_cmd = ($env.FZF_CTRL_T_COMMAND? | default null);
@@ -135,9 +149,19 @@
             let sh_cmd = [$ctrl_t_cmd '|' $fzf_cmd_str] | str join ' ';
             with-env { FZF_DEFAULT_OPTS: $fzf_opts, FZF_DEFAULT_OPTS_FILE: '' 
} { ^sh -c $sh_cmd }
           };
-          let result = ($result | str replace --all (char newline) ' ' | str 
trim);
-          commandline edit --append $result;
-          commandline set-cursor --end
+          # Serialize each path as a Nushell string literal, so that syntax
+          # in a file name is not evaluated when the line is executed.
+          let result = (
+            $result
+            | split row (char nul)
+            | where {|path| $path != ''}
+            | each {|path| $path | to nuon}
+            | str join ' '
+          );
+          if ($result | is-not-empty) {
+            commandline edit --append $'($result) ';
+            commandline set-cursor --end
+          }
         "
       }
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/result.go new/fzf-0.74.4/src/result.go
--- old/fzf-0.74.3/src/result.go        2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/src/result.go        2026-09-12 02:21:00.000000000 +0200
@@ -82,10 +82,10 @@
                        val = item.TrimLength()
                case byPathname:
                        if validOffsetFound {
+                               // Rune index, to be comparable with minBegin
                                lastDelim := -1
-                               s := item.text.ToString()
-                               for i := len(s) - 1; i >= 0; i-- {
-                                       if s[i] == '/' || s[i] == '\\' {
+                               for i := numChars - 1; i >= 0; i-- {
+                                       if r := item.text.Get(i); r == '/' || r 
== '\\' {
                                                lastDelim = i
                                                break
                                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/result_test.go 
new/fzf-0.74.4/src/result_test.go
--- old/fzf-0.74.3/src/result_test.go   2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/src/result_test.go   2026-09-12 02:21:00.000000000 +0200
@@ -272,3 +272,28 @@
                }
        }
 }
+
+func TestPathnameTiebreak(t *testing.T) {
+       // FIXME global
+       sortCriteria = []criterion{byScore, byPathname}
+
+       score := 100
+       test := func(input string, offset Offset, expected uint16) {
+               for _, chars := range []util.Chars{util.ToChars([]byte(input)), 
util.RunesToChars([]rune(input))} {
+                       item := buildResult(withIndex(&Item{text: chars}, 1), 
[]Offset{offset}, score)
+                       if item.points[3] != math.MaxUint16-uint16(score) || 
item.points[2] != expected {
+                               t.Error(input, item.points, expected)
+                       }
+               }
+       }
+
+       // Match in the file name
+       test("x/foo/foo.txt", Offset{6, 9}, 1)
+       // Match in the directory path
+       test("x/foo/aa.txt", Offset{2, 5}, math.MaxUint16)
+
+       // Offsets are rune indexes, so a multi-byte character before the last
+       // delimiter must not shift the delimiter position
+       test("一x/foo/foo.txt", Offset{7, 10}, 1)
+       test("一x/foo/aa.txt", Offset{3, 6}, math.MaxUint16)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/terminal.go 
new/fzf-0.74.4/src/terminal.go
--- old/fzf-0.74.3/src/terminal.go      2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/src/terminal.go      2026-09-12 02:21:00.000000000 +0200
@@ -1547,6 +1547,14 @@
 
 // Extra number of lines needed to display fzf
 func (t *Terminal) extraLines() int {
+       // borderLines() reports zero for BorderInline, but addInline() still
+       // reserves a divider line for it
+       sectionLines := func(shape tui.BorderShape) int {
+               if shape == tui.BorderInline {
+                       return 1
+               }
+               return borderLines(shape)
+       }
        extra := 0
        if !t.inputless {
                extra++
@@ -1562,16 +1570,16 @@
        }
        if t.headerVisible {
                if t.hasHeaderWindow() {
-                       extra += borderLines(t.headerBorderShape)
+                       extra += sectionLines(t.headerBorderShape)
                }
                extra += len(t.header0)
                if w, shape := t.determineHeaderLinesShape(); w {
-                       extra += borderLines(shape)
+                       extra += sectionLines(shape)
                }
                extra += t.headerLines
        }
        if len(t.footer) > 0 {
-               extra += borderLines(t.footerBorderShape)
+               extra += sectionLines(t.footerBorderShape)
                extra += len(t.footer)
        }
        return extra
@@ -2051,7 +2059,9 @@
                prevIndex = t.targetIndex
                t.targetIndex = minItem.Index()
        }
-       t.progress = 100
+       if result.final() {
+               t.progress = 100
+       }
        t.merger = merger
        t.resultMerger = merger
        t.passMerger = result.passMerger
@@ -8589,6 +8599,7 @@
                reload := changed || newCommand != nil
                if reload {
                        t.wait.searching = true
+                       t.progress = 0
                }
                var reloadRequest *searchRequest
                if reload {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/terminal_test.go 
new/fzf-0.74.4/src/terminal_test.go
--- old/fzf-0.74.3/src/terminal_test.go 2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/src/terminal_test.go 2026-09-12 02:21:00.000000000 +0200
@@ -29,6 +29,9 @@
 }
 
 func TestReplacePlaceholder(t *testing.T) {
+       // Pin $SHELL so the quoting style doesn't depend on the test runner's 
shell
+       t.Setenv("SHELL", "cmd")
+
        item1 := newItem("  foo'bar \x1b[31mbaz\x1b[m")
        items1 := [3][]*Item{{item1}, {item1}, nil}
        items2 := [3][]*Item{
@@ -255,6 +258,9 @@
        unixStyle := quotes{``, `'`, `'\''`, `"`, `\`, `\`}
        windowsStyle := quotes{`^`, `^"`, `'`, `\^"`, `\\`, `\`}
        var effectiveStyle quotes
+
+       // Pin $SHELL so the quoting style doesn't depend on the test runner's 
shell
+       t.Setenv("SHELL", "cmd")
        exec := util.NewExecutor("")
 
        if util.IsWindows() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/tui/light.go 
new/fzf-0.74.4/src/tui/light.go
--- old/fzf-0.74.3/src/tui/light.go     2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/src/tui/light.go     2026-09-12 02:21:00.000000000 +0200
@@ -26,7 +26,7 @@
        offsetPollTries = 10
        queryTimeout    = 500 * time.Millisecond
        maxInputBuffer  = 1024 * 1024
-       maxSelectTries  = 100
+       escapeLookback  = 256
 )
 
 const DefaultTtyDevice string = "/dev/tty"
@@ -175,6 +175,7 @@
        width         int
        height        int
        yoffset       int
+       xoffset       int
        tabstop       int
        escDelay      int
        fullscreen    bool
@@ -277,6 +278,7 @@
                // increased and we're left with unwanted extra new line.
                if x > 0 && r.clearOnExit {
                        r.upOneLine = true
+                       r.xoffset = x
                        r.makeSpace()
                }
                // We assume that --no-clear is used for repetitive relaunching 
of fzf.
@@ -338,6 +340,45 @@
        return atoi(env, defaultValue)
 }
 
+// Bytes of a CSI sequence: parameter and intermediate bytes continue it, a
+// final byte ends it. Order is not enforced. Strictness would only make fzf
+// give up on a sequence it could have framed.
+//
+// https://vt100.net/emu/dec_ansi_parser
+func csiContinues(b byte) bool { return b >= 0x20 && b <= 0x3f }
+func csiFinal(b byte) bool     { return b >= 0x40 && b <= 0x7e }
+
+// incompleteEscape reports whether the buffer ends in an escape sequence that
+// has not been terminated yet. The read loop keeps waiting in that case, so 
the
+// parser is never handed a fragment to guess at.
+func incompleteEscape(buffer []byte) bool {
+       // Only the tail can hold a sequence still arriving. This runs once per 
byte
+       // read, so scanning all of a large paste would make the read quadratic.
+       tail := buffer
+       if len(tail) > escapeLookback {
+               tail = tail[len(tail)-escapeLookback:]
+       }
+       start := bytes.LastIndexByte(tail, Esc.Byte())
+       if start < 0 || len(tail)-start < 2 {
+               return false
+       }
+       switch tail[start+1] {
+       case '[':
+               for _, b := range tail[start+2:] {
+                       if csiFinal(b) {
+                               return false
+                       }
+                       if !csiContinues(b) {
+                               return false // malformed, do not wait for a 
terminator
+                       }
+               }
+               return true
+       case 'O':
+               return len(tail)-start < 3
+       }
+       return false
+}
+
 func (r *LightRenderer) getBytes(cancellable bool) ([]byte, getCharResult, 
error) {
        return r.getBytesInternal(cancellable, r.buffer, false)
 }
@@ -378,6 +419,13 @@
                        retries = 0
                }
                buffer = append(buffer, byte(c))
+               // Keep waiting while a sequence is still arriving. Dropping 
the budget
+               // after every byte left fzf parsing whatever the read happened 
to end on.
+               // Past the introducer this is not the ESC key, so the wait 
costs no
+               // Escape latency and ESCDELAY=0 must not reduce it to nothing.
+               if retries == 0 && incompleteEscape(buffer) {
+                       retries = max(r.escDelay, defaultEscDelay) / 
escPollInterval
+               }
                pc = c
 
                // This should never happen under normal conditions,
@@ -1132,10 +1180,13 @@
                        r.rmcup()
                } else {
                        r.origin()
+                       // Erase our own area first, then step back onto the 
line the
+                       // prompt was on, so nothing on that line is touched
+                       r.csi("J")
                        if r.upOneLine {
                                r.csi("A")
+                               r.csi(fmt.Sprintf("%dG", r.xoffset+1))
                        }
-                       r.csi("J")
                }
        } else if !r.fullscreen {
                r.stderr("\x1b8") // DECRC: restore cursor position
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/tui/light_escape_test.go 
new/fzf-0.74.4/src/tui/light_escape_test.go
--- old/fzf-0.74.3/src/tui/light_escape_test.go 1970-01-01 01:00:00.000000000 
+0100
+++ new/fzf-0.74.4/src/tui/light_escape_test.go 2026-09-12 02:21:00.000000000 
+0200
@@ -0,0 +1,53 @@
+package tui
+
+import (
+       "strings"
+       "testing"
+)
+
+func TestIncompleteEscape(t *testing.T) {
+       for _, c := range []struct {
+               buffer string
+               want   bool
+       }{
+               // Complete sequences: nothing to wait for
+               {"\x1b[A", false},
+               {"\x1bOA", false},
+               {"\x1b[1;5A", false},
+               {"\x1b[200~", false},
+               {"\x1b[<0;1;1M", false},
+               {"\x1b[12;34R", false},
+               {"\x1b[?2004;2$y", false},
+               {"\x1b[?1;2c", false},
+
+               // Fragments: keep waiting
+               {"\x1b[", true},
+               {"\x1b[?", true},
+               {"\x1b[1;", true},
+               {"\x1b[?2004;2$", true},
+               {"\x1bO", true},
+               {"\x1b[<0;1;", true},
+
+               // Only the trailing sequence matters
+               {"ab\x1b[?2004;2$", true},
+               {"\x1b[A\x1b[", true},
+               {"\x1b[A\x1b[B", false},
+
+               // Long buffers: only the tail is scanned, so an introducer 
further
+               // back than escapeLookback is not waited for
+               {strings.Repeat("a", 100000), false},
+               {"\x1b[" + strings.Repeat("a", 100000), false},
+               {strings.Repeat("a", 100000) + "\x1b[1;", true},
+
+               // Not a sequence fzf waits on
+               {"", false},
+               {"abc", false},
+               {"\x1b", false},      // lone ESC, handled by the existing 
escDelay branch
+               {"\x1ba", false},     // ALT-a
+               {"\x1b[\x01", false}, // malformed, do not stall on it
+       } {
+               if got := incompleteEscape([]byte(c.buffer)); got != c.want {
+                       t.Errorf("incompleteEscape(%q) = %v, want %v", 
c.buffer, got, c.want)
+               }
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/src/tui/light_unix.go 
new/fzf-0.74.4/src/tui/light_unix.go
--- old/fzf-0.74.3/src/tui/light_unix.go        2026-08-17 05:05:05.000000000 
+0200
+++ new/fzf-0.74.4/src/tui/light_unix.go        2026-09-12 02:21:00.000000000 
+0200
@@ -240,7 +240,7 @@
        }()
 
        cancelFd := int(rpipe.Fd())
-       for range maxSelectTries {
+       for {
                var rfds unix.FdSet
                limit := len(rfds.Bits) * unix.NFDBITS
                if fd >= limit || cancelFd >= limit {
@@ -251,6 +251,8 @@
                rfds.Set(cancelFd)
                _, err := unix.Select(max(fd, cancelFd)+1, &rfds, nil, nil, nil)
                if err != nil {
+                       // An interrupted wait is not a failed read, so it must 
not count
+                       // against anything. Retry until the fd is ready or the 
wait fails.
                        if err == syscall.EINTR {
                                continue
                        }
@@ -265,7 +267,6 @@
                        return getter()
                }
        }
-       return 0, getCharError
 }
 
 func (r *LightRenderer) Size() TermSize {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/test/test_layout.rb 
new/fzf-0.74.4/test/test_layout.rb
--- old/fzf-0.74.3/test/test_layout.rb  2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/test/test_layout.rb  2026-09-12 02:21:00.000000000 +0200
@@ -882,6 +882,28 @@
     tmux.until { assert_block(block, it) }
   end
 
+  def test_adaptive_height_with_inline_sections
+    tmux.send_keys %(seq 10 | #{FZF} --height=~100% --list-border 
--header-lines=1 --header-lines-border=inline), :Enter
+    block = <<~BLOCK
+      ╭──────
+      │   10
+      │   9
+      │   8
+      │   7
+      │   6
+      │   5
+      │   4
+      │   3
+      │ > 2
+      ├──────
+      │   1
+      ╰──────
+          9/9
+        >
+    BLOCK
+    tmux.until { assert_block(block, it) }
+  end
+
   def test_style_full_adaptive_height
     tmux.send_keys %(seq 1| #{FZF} --style=full:rounded --height=~100% 
--header-lines=1 --info=default), :Enter
     block = <<~BLOCK
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/test/test_server.rb 
new/fzf-0.74.4/test/test_server.rb
--- old/fzf-0.74.3/test/test_server.rb  2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/test/test_server.rb  2026-09-12 02:21:00.000000000 +0200
@@ -55,6 +55,29 @@
     end
   end
 
+  def test_listen_progress
+    tmux.send_keys "seq 10 | #{FZF} --listen 6266", :Enter
+    tmux.until { |lines| assert_equal 10, lines.match_count }
+    uri = URI('http://localhost:6266')
+    state = -> { JSON.parse(Net::HTTP.get(uri), symbolize_names: true) }
+
+    # Idle: the last search is complete
+    assert_equal 100, state.call[:progress]
+
+    # While a search is running, progress is not left at 100 from the
+    # previous one, so it can tell a settled snapshot from a stale one
+    Net::HTTP.post(uri, 'reload(sleep 1; seq 100)')
+    tmux.until { assert_equal 0, state.call[:progress] }
+
+    # Settled: matches belong to the snapshot that reports 100
+    tmux.until { |lines| assert_equal 100, lines.match_count }
+    tmux.until do
+      st = state.call
+      assert_equal 100, st[:progress]
+      assert_equal 100, st[:matchCount]
+    end
+  end
+
   def test_listen_with_api_key
     uri = URI('http://localhost:6266')
     tmux.send_keys 'seq 10 | FZF_API_KEY=123abc fzf --listen 6266', :Enter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/test/test_shell_integration.rb 
new/fzf-0.74.4/test/test_shell_integration.rb
--- old/fzf-0.74.3/test/test_shell_integration.rb       2026-08-17 
05:05:05.000000000 +0200
+++ new/fzf-0.74.4/test/test_shell_integration.rb       2026-09-12 
02:21:00.000000000 +0200
@@ -1273,6 +1273,43 @@
     FileUtils.rm_rf('/tmp/fzf-test')
   end
 
+  # Override: paths are inserted as Nushell string literals, so the
+  # selections appear quoted on the command line.
+  def test_ctrl_t
+    set_var('FZF_CTRL_T_COMMAND', 'seq 100')
+
+    tmux.prepare
+    tmux.send_keys 'C-t'
+    tmux.until { |lines| assert_equal 100, lines.match_count }
+    tmux.send_keys :Tab, :Tab, :Tab
+    tmux.until { |lines| assert lines.any_include?(' (3)') }
+    tmux.send_keys :Enter
+    tmux.until { |lines| assert lines.any_include?('"1" "2" "3"') }
+    tmux.send_keys 'C-c'
+  end
+
+  # A path is inserted as a Nushell string literal, so that syntax in a file
+  # name is not evaluated and each path stays a single argument.
+  def test_ctrl_t_quoting
+    marker = "#{tempname}-marker"
+    FileUtils.rm_f(marker)
+    writelines(["fzf-inject$(touch #{marker}).txt", 'fzf-inject space.txt'])
+    set_var('FZF_CTRL_T_COMMAND', "cat #{tempname}")
+
+    tmux.prepare
+    tmux.send_keys '^printf "%s\n" ', 'C-t'
+    tmux.until { |lines| assert_equal 2, lines.match_count }
+    tmux.send_keys :Tab, :Tab
+    tmux.until { |lines| assert_equal 2, lines.select_count }
+    tmux.send_keys :Enter
+    tmux.until { |lines| assert_includes lines[-1].to_s, '"fzf-inject$(touch' }
+    tmux.send_keys :Enter
+    tmux.until do |lines|
+      assert_equal ["fzf-inject$(touch #{marker}).txt", 'fzf-inject 
space.txt'], lines[-2..]
+    end
+    refute_path_exists marker
+  end
+
   # Nushell does not support multiline command recall the same way
   # as bash/zsh/fish, so test_ctrl_r_multiline is omitted.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fzf-0.74.3/test/vim/fzf.vader 
new/fzf-0.74.4/test/vim/fzf.vader
--- old/fzf-0.74.3/test/vim/fzf.vader   2026-08-17 05:05:05.000000000 +0200
+++ new/fzf-0.74.4/test/vim/fzf.vader   2026-09-12 02:21:00.000000000 +0200
@@ -4,15 +4,44 @@
   Log 'Test directory: ' . g:dir
   Save &acd
 
+  " fzf#run runs fzf in a terminal buffer and returns immediately, so the
+  " assertions have to wait for the run to finish. 'exit' is called from the
+  " same handler that runs the sink, just before it, so once it has fired the
+  " sink has run too. It also fires when nothing is selected, unlike the sink.
+  function! g:FzfExit(code)
+    let g:fzf_done = 1
+  endfunction
+
+  " Collects the output without displacing a 'sink' the spec already has.
+  " s:callback runs both
+  function! g:FzfCollect(lines)
+    call extend(g:fzf_lines, a:lines)
+  endfunction
+
+  function! g:FzfRun(spec) abort
+    let g:fzf_done = 0
+    let g:fzf_lines = []
+    call fzf#run(extend(copy(a:spec),
+    \ { 'exit': function('g:FzfExit'), 'sinklist': function('g:FzfCollect') }))
+    let started = reltime()
+    while !g:fzf_done && reltimefloat(reltime(started)) < 10
+      sleep 10m
+    endwhile
+    if !g:fzf_done
+      throw 'Timed out waiting for fzf#run'
+    endif
+    return g:fzf_lines
+  endfunction
+
 Execute (fzf#run with dir option):
   let cwd = getcwd()
-  let result = fzf#run({ 'source': 'git ls-files', 'options': '--filter=vdr', 
'dir': g:dir })
+  let result = g:FzfRun({ 'source': 'git ls-files', 'options': '--filter=vdr', 
'dir': g:dir })
   AssertEqual ['fzf.vader'], result
   AssertEqual 0, haslocaldir()
   AssertEqual getcwd(), cwd
 
   execute 'lcd' fnameescape(cwd)
-  let result = sort(fzf#run({ 'source': 'git ls-files', 'options': '--filter 
e', 'dir': g:dir }))
+  let result = sort(g:FzfRun({ 'source': 'git ls-files', 'options': '--filter 
e', 'dir': g:dir }))
   AssertEqual ['fzf.vader'], result
   AssertEqual 1, haslocaldir()
   AssertEqual getcwd(), cwd
@@ -22,32 +51,32 @@
   function! g:FzfTest(e)
     call add(g:ret, a:e)
   endfunction
-  let result = sort(fzf#run({ 'source': 'git ls-files', 'sink': 
function('g:FzfTest'), 'options': '--filter e', 'dir': g:dir }))
+  let result = sort(g:FzfRun({ 'source': 'git ls-files', 'sink': 
function('g:FzfTest'), 'options': '--filter e', 'dir': g:dir }))
   AssertEqual ['fzf.vader'], result
   AssertEqual ['fzf.vader'], sort(g:ret)
 
 Execute (fzf#run with string source):
-  let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
+  let result = sort(g:FzfRun({ 'source': 'echo hi', 'options': '-f i' }))
   AssertEqual ['hi'], result
 
 Execute (fzf#run with list source):
-  let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f e' 
}))
+  let result = sort(g:FzfRun({ 'source': ['hello', 'world'], 'options': '-f e' 
}))
   AssertEqual ['hello'], result
-  let result = sort(fzf#run({ 'source': ['hello', 'world'], 'options': '-f o' 
}))
+  let result = sort(g:FzfRun({ 'source': ['hello', 'world'], 'options': '-f o' 
}))
   AssertEqual ['hello', 'world'], result
 
 Execute (fzf#run with string source):
-  let result = sort(fzf#run({ 'source': 'echo hi', 'options': '-f i' }))
+  let result = sort(g:FzfRun({ 'source': 'echo hi', 'options': '-f i' }))
   AssertEqual ['hi'], result
 
 Execute (fzf#run with dir option and noautochdir):
   set noacd
   let cwd = getcwd()
-  call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': 
'-1'})
+  call g:FzfRun({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': 
'-1'})
   " No change in working directory
   AssertEqual cwd, getcwd()
 
-  call fzf#run({'source': ['/foobar'], 'sink': 'tabe', 'dir': '/tmp', 
'options': '-1'})
+  call g:FzfRun({'source': ['/foobar'], 'sink': 'tabe', 'dir': '/tmp', 
'options': '-1'})
   AssertEqual cwd, getcwd()
   tabclose
   AssertEqual cwd, getcwd()
@@ -55,13 +84,13 @@
 Execute (Incomplete fzf#run with dir option and autochdir):
   set acd
   let cwd = getcwd()
-  call fzf#run({'source': [], 'sink': 'e', 'dir': '/tmp', 'options': '-0'})
+  call g:FzfRun({'source': [], 'sink': 'e', 'dir': '/tmp', 'options': '-0'})
   " No change in working directory even if &acd is set
   AssertEqual cwd, getcwd()
 
 Execute (FIXME: fzf#run with dir option and autochdir):
   set acd
-  call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': 
'-1'})
+  call g:FzfRun({'source': ['/foobar'], 'sink': 'e', 'dir': '/tmp', 'options': 
'-1'})
   " Working directory changed due to &acd
   AssertEqual '/foobar', expand('%')
   AssertEqual '/', getcwd()
@@ -69,13 +98,40 @@
 Execute (fzf#run with dir option and autochdir when final cwd is same as dir):
   set acd
   cd /tmp
-  call fzf#run({'source': ['/foobar'], 'sink': 'e', 'dir': '/', 'options': 
'-1'})
+  call g:FzfRun({'source': ['/foobar'], 'sink': 'e', 'dir': '/', 'options': 
'-1'})
   " Working directory changed due to &acd
   AssertEqual '/', getcwd()
 
+Execute (Default layout):
+  unlet! g:fzf_layout g:fzf_height
+  let layout_keys = ['window', 'popup', 'tmux', 'up', 'down', 'left', 'right']
+
+  let opts = fzf#wrap('foobar')
+  Log opts
+  let found = filter(copy(layout_keys), 'has_key(opts, v:val)')
+  AssertEqual 1, len(found)
+  if found[0] ==# 'tmux'
+    " Only where fzf opens a floating pane, which can be left while fzf runs
+    Assert !empty($TMUX) || !empty($ZELLIJ)
+    AssertEqual '90%,60%', opts.tmux
+  elseif found[0] ==# 'window'
+    AssertEqual 0.9, opts.window.width
+  else
+    " No popup support in this build
+    AssertEqual '~40%', opts.down
+  endif
+
+  " Fullscreen strips it, whichever it was
+  let opts = fzf#wrap('foobar', {}, 1)
+  Log opts
+  AssertEqual [], filter(copy(layout_keys), 'has_key(opts, v:val)')
+
 Execute (fzf#wrap):
   AssertThrows fzf#wrap({'foo': 'bar'})
 
+  " Pin the layout so the assertions do not depend on the environment
+  let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
+
   let opts = fzf#wrap('foobar')
   Log opts
   AssertEqual 0.9, opts.window.width
@@ -150,6 +206,30 @@
   let opts = fzf#wrap({})
   Assert opts.options =~ '--color=fg:'
 
+Execute (popup is a synonym of tmux):
+  unlet! g:fzf_layout
+
+  " Treated as a layout option, so g:fzf_layout is not applied on top
+  let opts = fzf#wrap('foobar', {'popup': '90%,70%'})
+  AssertEqual '90%,70%', opts.popup
+  Assert !has_key(opts, 'window')
+
+  " And stripped in fullscreen, like the others
+  let opts = fzf#wrap('foobar', {'popup': '90%,70%'}, 1)
+  Assert !has_key(opts, 'popup')
+
+  " Accepted in g:fzf_layout
+  let g:fzf_layout = {'popup': '90%,70%'}
+  let opts = fzf#wrap('foobar')
+  AssertEqual '90%,70%', opts.popup
+  unlet g:fzf_layout
+
+  " Listed as a valid key
+  let g:fzf_layout = {'bogus': 1}
+  AssertThrows call fzf#wrap('foobar')
+  Assert g:vader_exception =~ 'popup'
+  unlet g:fzf_layout
+
 Execute (fzf#shellescape with sh):
   AssertEqual '''''', fzf#shellescape('', 'sh')
   AssertEqual '''\''', fzf#shellescape('\', 'sh')

Reply via email to