m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-14 22:33:47 -05:00

Compare commits

...

16 Commits

Author SHA1 Message Date
Junegunn Choi
2023012408 0.37.0 2023-01-24 22:11:14 +09:00
Junegunn Choi
95a7661bb1 Sanitize input strings that should be a single line 2023-01-24 19:35:29 +09:00
Junegunn Choi
618d317803 Support custom separator of inline info
Close #2030
Close #3084
2023-01-24 17:55:06 +09:00
Junegunn Choi
ae897c8cdb No need to touch mouse flag if it's already false 2023-01-24 12:45:07 +09:00
Junegunn Choi
d0a0f3c052 Temporarily disable mouse mode when switching to an external command 2023-01-24 12:41:40 +09:00
Junegunn Choi
91b9591b10 Reenable mouse mode when coming back from an external program
Close #3141
2023-01-24 12:00:41 +09:00
Junegunn Choi
aa7361337d Make test case pass on 32-bit platforms
Close #3127
2023-01-23 18:30:36 +09:00
Junegunn Choi
284d77fe2e Add 'focus' event
Can we find a better name? I have considered the followings.

* 'point', because "the pointer" points to the current item.
* 'shift', 'switch', 'move', etc. These are not technically correct
  because the current item can change without cursor movement (--tac,
  reload, search update)
* 'change' is already taken. 'change-current' feels a bit wordy and
  sounds wrong, 'current-changed' is wordy and doesn't go well with the
  other event names
* 'target', not straightforward

Close #3053
2023-01-23 16:38:24 +09:00
Junegunn Choi
826178f1e2 Do not restore terminal state while running an external command 2023-01-23 02:24:13 +09:00
Junegunn Choi
acccf8a9b8 Fix TOC 2023-01-23 02:24:13 +09:00
Francesco Bigagnoli
57c066f0be Fix bat url in README (#3129) 2023-01-23 02:21:16 +09:00
Nachum Barcohen
e44f64ae92 Add Helix editor to bash autocompletion (#3137) 2023-01-23 02:21:04 +09:00
Junegunn Choi
d51980a3f5 Add 'transform-border-label' and 'transform-preview-label' 2023-01-22 02:18:19 +09:00
jpcrs
c3d73e7ecb Add change-border-label and change-preview-label actions, update man 2023-01-22 02:18:19 +09:00
Junegunn Choi
b077f6821d Action argument in enclosed form should allow new lines
Close #3138
2023-01-21 22:20:26 +09:00
Junegunn Choi
a79de11af7 README: Add FZF_TMUX_OPTS example for tmux popup 2023-01-19 13:25:08 +09:00
16 changed files with 372 additions and 191 deletions

View File

@@ -1,6 +1,33 @@
CHANGELOG CHANGELOG
========= =========
0.37.0
------
- Added a way to customize the separator of inline info
```sh
fzf --info 'inline: ' --prompt ' ' --color prompt:bright-yellow
```
- New event
- `focus` - Triggered when the focus changes due to a vertical cursor
movement or a search result update
```sh
fzf --bind 'focus:transform-preview-label:echo [ {} ]' --preview 'cat {}'
# Any action bound to the event runs synchronously and thus can make the interface sluggish
# e.g. lolcat isn't one of the fastest programs, and every cursor movement in
# fzf will be noticeably affected by its execution time
fzf --bind 'focus:transform-preview-label:echo [ {} ] | lolcat -f' --preview 'cat {}'
# Beware not to introduce an infinite loop
seq 10 | fzf --bind 'focus:up' --cycle
```
- New actions
- `change-border-label`
- `change-preview-label`
- `transform-border-label`
- `transform-preview-label`
- Bug fixes and improvements
0.36.0 0.36.0
------ ------
- Added `--listen=HTTP_PORT` option to start HTTP server. It allows external - Added `--listen=HTTP_PORT` option to start HTTP server. It allows external

View File

@@ -33,23 +33,23 @@ Table of Contents
* [Upgrading fzf](#upgrading-fzf) * [Upgrading fzf](#upgrading-fzf)
* [Building fzf](#building-fzf) * [Building fzf](#building-fzf)
* [Usage](#usage) * [Usage](#usage)
* [Using the finder](#using-the-finder) * [Using the finder](#using-the-finder)
* [Layout](#layout) * [Layout](#layout)
* [Search syntax](#search-syntax) * [Search syntax](#search-syntax)
* [Environment variables](#environment-variables) * [Environment variables](#environment-variables)
* [Options](#options) * [Options](#options)
* [Demo](#demo) * [Demo](#demo)
* [Examples](#examples) * [Examples](#examples)
* [`fzf-tmux` script](#fzf-tmux-script) * [`fzf-tmux` script](#fzf-tmux-script)
* [Key bindings for command-line](#key-bindings-for-command-line) * [Key bindings for command-line](#key-bindings-for-command-line)
* [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh) * [Fuzzy completion for bash and zsh](#fuzzy-completion-for-bash-and-zsh)
* [Files and directories](#files-and-directories) * [Files and directories](#files-and-directories)
* [Process IDs](#process-ids) * [Process IDs](#process-ids)
* [Host names](#host-names) * [Host names](#host-names)
* [Environment variables / Aliases](#environment-variables--aliases) * [Environment variables / Aliases](#environment-variables--aliases)
* [Settings](#settings) * [Settings](#settings)
* [Supported commands](#supported-commands) * [Supported commands](#supported-commands)
* [Custom fuzzy completion](#custom-fuzzy-completion) * [Custom fuzzy completion](#custom-fuzzy-completion)
* [Vim plugin](#vim-plugin) * [Vim plugin](#vim-plugin)
* [Advanced topics](#advanced-topics) * [Advanced topics](#advanced-topics)
* [Performance](#performance) * [Performance](#performance)
@@ -202,7 +202,7 @@ files excluding hidden ones. (You can override the default command with
vim $(fzf) vim $(fzf)
``` ```
#### Using the finder ### Using the finder
- `CTRL-K` / `CTRL-J` (or `CTRL-P` / `CTRL-N`) to move cursor up and down - `CTRL-K` / `CTRL-J` (or `CTRL-P` / `CTRL-N`) to move cursor up and down
- `Enter` key to select the item, `CTRL-C` / `CTRL-G` / `ESC` to exit - `Enter` key to select the item, `CTRL-C` / `CTRL-G` / `ESC` to exit
@@ -211,7 +211,7 @@ vim $(fzf)
- Mouse: scroll, click, double-click; shift-click and shift-scroll on - Mouse: scroll, click, double-click; shift-click and shift-scroll on
multi-select mode multi-select mode
#### Layout ### Layout
fzf by default starts in fullscreen mode, but you can make it start below the fzf by default starts in fullscreen mode, but you can make it start below the
cursor with `--height` option. cursor with `--height` option.
@@ -234,7 +234,7 @@ default. For example,
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border' export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'
``` ```
#### Search syntax ### Search syntax
Unless otherwise specified, fzf starts in "extended-search mode" where you can Unless otherwise specified, fzf starts in "extended-search mode" where you can
type in multiple search terms delimited by spaces. e.g. `^music .mp3$ sbtrkt type in multiple search terms delimited by spaces. e.g. `^music .mp3$ sbtrkt
@@ -262,7 +262,7 @@ or `py`.
^core go$ | rb$ | py$ ^core go$ | rb$ | py$
``` ```
#### Environment variables ### Environment variables
- `FZF_DEFAULT_COMMAND` - `FZF_DEFAULT_COMMAND`
- Default command to use when input is tty - Default command to use when input is tty
@@ -278,11 +278,11 @@ or `py`.
- Default options - Default options
- e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"` - e.g. `export FZF_DEFAULT_OPTS="--layout=reverse --inline-info"`
#### Options ### Options
See the man page (`man fzf`) for the full list of options. See the man page (`man fzf`) for the full list of options.
#### Demo ### Demo
If you learn by watching videos, check out this screencast by [@samoshkin](https://github.com/samoshkin) to explore `fzf` features. If you learn by watching videos, check out this screencast by [@samoshkin](https://github.com/samoshkin) to explore `fzf` features.
<a title="fzf - command-line fuzzy finder" href="https://www.youtube.com/watch?v=qgG5Jhi_Els"> <a title="fzf - command-line fuzzy finder" href="https://www.youtube.com/watch?v=qgG5Jhi_Els">
@@ -335,7 +335,7 @@ fish.
- Set `FZF_CTRL_T_COMMAND` to override the default command - Set `FZF_CTRL_T_COMMAND` to override the default command
- Set `FZF_CTRL_T_OPTS` to pass additional options to fzf - Set `FZF_CTRL_T_OPTS` to pass additional options to fzf
```sh ```sh
# Preview file content using bat (https://github.com/sharkdp/fd) # Preview file content using bat (https://github.com/sharkdp/bat)
export FZF_CTRL_T_OPTS=" export FZF_CTRL_T_OPTS="
--preview 'bat -n --color=always {}' --preview 'bat -n --color=always {}'
--bind 'ctrl-/:change-preview-window(down|hidden|)'" --bind 'ctrl-/:change-preview-window(down|hidden|)'"
@@ -363,7 +363,7 @@ fish.
``` ```
If you're on a tmux session, you can start fzf in a tmux split-pane or in If you're on a tmux session, you can start fzf in a tmux split-pane or in
a tmux popup window by setting `FZF_TMUX_OPTS` (e.g. `-d 40%`). a tmux popup window by setting `FZF_TMUX_OPTS` (e.g. `export FZF_TMUX_OPTS='-p80%,60%'`).
See `fzf-tmux --help` for available options. See `fzf-tmux --help` for available options.
More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings). More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/Configuring-shell-key-bindings).
@@ -371,7 +371,7 @@ More tips can be found on [the wiki page](https://github.com/junegunn/fzf/wiki/C
Fuzzy completion for bash and zsh Fuzzy completion for bash and zsh
--------------------------------- ---------------------------------
#### Files and directories ### Files and directories
Fuzzy completion for files and directories can be triggered if the word before Fuzzy completion for files and directories can be triggered if the word before
the cursor ends with the trigger sequence, which is by default `**`. the cursor ends with the trigger sequence, which is by default `**`.
@@ -400,7 +400,7 @@ cd **<TAB>
cd ~/github/fzf**<TAB> cd ~/github/fzf**<TAB>
``` ```
#### Process IDs ### Process IDs
Fuzzy completion for PIDs is provided for kill command. Fuzzy completion for PIDs is provided for kill command.
@@ -409,7 +409,7 @@ Fuzzy completion for PIDs is provided for kill command.
kill -9 **<TAB> kill -9 **<TAB>
``` ```
#### Host names ### Host names
For ssh and telnet commands, fuzzy completion for hostnames is provided. The For ssh and telnet commands, fuzzy completion for hostnames is provided. The
names are extracted from /etc/hosts and ~/.ssh/config. names are extracted from /etc/hosts and ~/.ssh/config.
@@ -419,7 +419,7 @@ ssh **<TAB>
telnet **<TAB> telnet **<TAB>
``` ```
#### Environment variables / Aliases ### Environment variables / Aliases
```sh ```sh
unset **<TAB> unset **<TAB>
@@ -427,7 +427,7 @@ export **<TAB>
unalias **<TAB> unalias **<TAB>
``` ```
#### Settings ### Settings
```sh ```sh
# Use ~~ as the trigger sequence instead of the default ** # Use ~~ as the trigger sequence instead of the default **
@@ -465,7 +465,7 @@ _fzf_comprun() {
} }
``` ```
#### Supported commands ### Supported commands
On bash, fuzzy completion is enabled only for a predefined set of commands On bash, fuzzy completion is enabled only for a predefined set of commands
(`complete | grep _fzf` to see the list). But you can enable it for other (`complete | grep _fzf` to see the list). But you can enable it for other
@@ -477,7 +477,7 @@ _fzf_setup_completion path ag git kubectl
_fzf_setup_completion dir tree _fzf_setup_completion dir tree
``` ```
#### Custom fuzzy completion ### Custom fuzzy completion
_**(Custom completion API is experimental and subject to change)**_ _**(Custom completion API is experimental and subject to change)**_

View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.36.0 version=0.37.0
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2

View File

@@ -1,4 +1,4 @@
$version="0.36.0" $version="0.37.0"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -5,7 +5,7 @@ import (
"github.com/junegunn/fzf/src/protector" "github.com/junegunn/fzf/src/protector"
) )
var version string = "0.36" var version string = "0.37"
var revision string = "devel" var revision string = "devel"
func main() { func main() {

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf-tmux 1 "Jan 2023" "fzf 0.36.0" "fzf-tmux - open fzf in tmux split pane" .TH fzf-tmux 1 "Jan 2023" "fzf 0.37.0" "fzf-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf-tmux - open fzf in tmux split pane fzf-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Jan 2023" "fzf 0.36.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Jan 2023" "fzf 0.37.0" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -337,11 +337,13 @@ e.g.
Determines the display style of finder info (match counters). Determines the display style of finder info (match counters).
.br .br
.BR default " Display on the next line to the prompt" .BR default " Display on the next line to the prompt"
.br .br
.BR inline " Display on the same line" .BR inline " Display on the same line with the default separator ' < '"
.br .br
.BR hidden " Do not display finder info" .BR inline:SEPARATOR " Display on the same line with a non-default separator"
.br
.BR hidden " Do not display finder info"
.br .br
.TP .TP
@@ -959,6 +961,22 @@ e.g.
\fB# Move cursor to the first entry whenever the query is changed \fB# Move cursor to the first entry whenever the query is changed
fzf --bind change:first\fR fzf --bind change:first\fR
.RE .RE
\fIfocus\fR
.RS
Triggered when the focus changes due to a vertical cursor movement or a search
result update.
e.g.
\fBfzf --bind 'focus:transform-preview-label:echo [ {} ]' --preview 'cat {}'
# Any action bound to the event runs synchronously and thus can make the interface sluggish
# e.g. lolcat isn't one of the fastest programs, and every cursor movement in
# fzf will be noticeably affected by its execution time
fzf --bind 'focus:transform-preview-label:echo [ {} ] | lolcat -f' --preview 'cat {}'
# Beware not to introduce an infinite loop
seq 10 | fzf --bind 'focus:up' --cycle\fR
.RE
\fIbackward-eof\fR \fIbackward-eof\fR
.RS .RS
@@ -972,89 +990,93 @@ e.g.
.SS AVAILABLE ACTIONS: .SS AVAILABLE ACTIONS:
A key or an event can be bound to one or more of the following actions. A key or an event can be bound to one or more of the following actions.
\fBACTION: DEFAULT BINDINGS (NOTES): \fBACTION: DEFAULT BINDINGS (NOTES):
\fBabort\fR \fIctrl-c ctrl-g ctrl-q esc\fR \fBabort\fR \fIctrl-c ctrl-g ctrl-q esc\fR
\fBaccept\fR \fIenter double-click\fR \fBaccept\fR \fIenter double-click\fR
\fBaccept-non-empty\fR (same as \fBaccept\fR except that it prevents fzf from exiting without selection) \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-char\fR \fIctrl-b left\fR
\fBbackward-delete-char\fR \fIctrl-h bspace\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-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-kill-word\fR \fIalt-bs\fR
\fBbackward-word\fR \fIalt-b shift-left\fR \fBbackward-word\fR \fIalt-b shift-left\fR
\fBbeginning-of-line\fR \fIctrl-a home\fR \fBbeginning-of-line\fR \fIctrl-a home\fR
\fBcancel\fR (clear query string if not empty, abort fzf otherwise) \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange-preview(...)\fR (change \fB--preview\fR option) \fBchange-border-label(...)\fR (change \fB--border-label\fR to the given string)
\fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|') \fBchange-preview(...)\fR (change \fB--preview\fR option)
\fBchange-prompt(...)\fR (change prompt to the given string) \fBchange-preview-label(...)\fR (change \fB--preview-label\fR to the given string)
\fBchange-query(...)\fR (change query string to the given string) \fBchange-preview-window(...)\fR (change \fB--preview-window\fR option; rotate through the multiple option sets separated by '|')
\fBclear-screen\fR \fIctrl-l\fR \fBchange-prompt(...)\fR (change prompt to the given string)
\fBclear-selection\fR (clear multi-selection) \fBchange-query(...)\fR (change query string to the given string)
\fBclose\fR (close preview window if open, abort fzf otherwise) \fBclear-screen\fR \fIctrl-l\fR
\fBclear-query\fR (clear query string) \fBclear-selection\fR (clear multi-selection)
\fBdelete-char\fR \fIdel\fR \fBclose\fR (close preview window if open, abort fzf otherwise)
\fBdelete-char/eof\fR \fIctrl-d\fR (same as \fBdelete-char\fR except aborts fzf if query is empty) \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\fR
\fBdeselect-all\fR (deselect all matches) \fBdeselect-all\fR (deselect all matches)
\fBdisable-search\fR (disable search functionality) \fBdisable-search\fR (disable search functionality)
\fBdown\fR \fIctrl-j ctrl-n down\fR \fBdown\fR \fIctrl-j ctrl-n down\fR
\fBenable-search\fR (enable search functionality) \fBenable-search\fR (enable search functionality)
\fBend-of-line\fR \fIctrl-e end\fR \fBend-of-line\fR \fIctrl-e end\fR
\fBexecute(...)\fR (see below for the details) \fBexecute(...)\fR (see below for the details)
\fBexecute-silent(...)\fR (see below for the details) \fBexecute-silent(...)\fR (see below for the details)
\fBfirst\fR (move to the first match; same as \fBpos(1)\fR) \fBfirst\fR (move to the first match; same as \fBpos(1)\fR)
\fBforward-char\fR \fIctrl-f right\fR \fBforward-char\fR \fIctrl-f right\fR
\fBforward-word\fR \fIalt-f shift-right\fR \fBforward-word\fR \fIalt-f shift-right\fR
\fBignore\fR \fBignore\fR
\fBjump\fR (EasyMotion-like 2-keystroke movement) \fBjump\fR (EasyMotion-like 2-keystroke movement)
\fBjump-accept\fR (jump and accept) \fBjump-accept\fR (jump and accept)
\fBkill-line\fR \fBkill-line\fR
\fBkill-word\fR \fIalt-d\fR \fBkill-word\fR \fIalt-d\fR
\fBlast\fR (move to the last match; same as \fBpos(-1)\fR) \fBlast\fR (move to the last match; same as \fBpos(-1)\fR)
\fBnext-history\fR (\fIctrl-n\fR on \fB--history\fR) \fBnext-history\fR (\fIctrl-n\fR on \fB--history\fR)
\fBnext-selected\fR (move to the next selected item) \fBnext-selected\fR (move to the next selected item)
\fBpage-down\fR \fIpgdn\fR \fBpage-down\fR \fIpgdn\fR
\fBpage-up\fR \fIpgup\fR \fBpage-up\fR \fIpgup\fR
\fBhalf-page-down\fR \fBhalf-page-down\fR
\fBhalf-page-up\fR \fBhalf-page-up\fR
\fBpos(...)\fR (move cursor to the numeric position; negative number to count from the end) \fBpos(...)\fR (move cursor to the numeric position; negative number to count from the end)
\fBprev-history\fR (\fIctrl-p\fR on \fB--history\fR) \fBprev-history\fR (\fIctrl-p\fR on \fB--history\fR)
\fBprev-selected\fR (move to the previous selected item) \fBprev-selected\fR (move to the previous selected item)
\fBpreview(...)\fR (see below for the details) \fBpreview(...)\fR (see below for the details)
\fBpreview-down\fR \fIshift-down\fR \fBpreview-down\fR \fIshift-down\fR
\fBpreview-up\fR \fIshift-up\fR \fBpreview-up\fR \fIshift-up\fR
\fBpreview-page-down\fR \fBpreview-page-down\fR
\fBpreview-page-up\fR \fBpreview-page-up\fR
\fBpreview-half-page-down\fR \fBpreview-half-page-down\fR
\fBpreview-half-page-up\fR \fBpreview-half-page-up\fR
\fBpreview-bottom\fR \fBpreview-bottom\fR
\fBpreview-top\fR \fBpreview-top\fR
\fBprint-query\fR (print query and exit) \fBprint-query\fR (print query and exit)
\fBput\fR (put the character to the prompt) \fBput\fR (put the character to the prompt)
\fBput(...)\fR (put the given string to the prompt) \fBput(...)\fR (put the given string to the prompt)
\fBrefresh-preview\fR \fBrefresh-preview\fR
\fBrebind(...)\fR (rebind bindings after \fBunbind\fR) \fBrebind(...)\fR (rebind bindings after \fBunbind\fR)
\fBreload(...)\fR (see below for the details) \fBreload(...)\fR (see below for the details)
\fBreload-sync(...)\fR (see below for the details) \fBreload-sync(...)\fR (see below for the details)
\fBreplace-query\fR (replace query string with the current selection) \fBreplace-query\fR (replace query string with the current selection)
\fBselect\fR \fBselect\fR
\fBselect-all\fR (select all matches) \fBselect-all\fR (select all matches)
\fBtoggle\fR (\fIright-click\fR) \fBtoggle\fR (\fIright-click\fR)
\fBtoggle-all\fR (toggle all matches) \fBtoggle-all\fR (toggle all matches)
\fBtoggle+down\fR \fIctrl-i (tab)\fR \fBtoggle+down\fR \fIctrl-i (tab)\fR
\fBtoggle-in\fR (\fB--layout=reverse*\fR ? \fBtoggle+up\fR : \fBtoggle+down\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-out\fR (\fB--layout=reverse*\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR)
\fBtoggle-preview\fR \fBtoggle-preview\fR
\fBtoggle-preview-wrap\fR \fBtoggle-preview-wrap\fR
\fBtoggle-search\fR (toggle search functionality) \fBtoggle-search\fR (toggle search functionality)
\fBtoggle-sort\fR \fBtoggle-sort\fR
\fBtoggle+up\fR \fIbtab (shift-tab)\fR \fBtoggle+up\fR \fIbtab (shift-tab)\fR
\fBtransform-prompt(...)\fR (transform prompt string using an external command) \fBtransform-border-label(...)\fR (transform border label using an external command)
\fBtransform-query(...)\fR (transform query string using an external command) \fBtransform-preview-label(...)\fR (transform preview label using an external command)
\fBunbind(...)\fR (unbind bindings) \fBtransform-prompt(...)\fR (transform prompt string using an external command)
\fBunix-line-discard\fR \fIctrl-u\fR \fBtransform-query(...)\fR (transform query string using an external command)
\fBunix-word-rubout\fR \fIctrl-w\fR \fBunbind(...)\fR (unbind bindings)
\fBup\fR \fIctrl-k ctrl-p up\fR \fBunix-line-discard\fR \fIctrl-u\fR
\fByank\fR \fIctrl-y\fR \fBunix-word-rubout\fR \fIctrl-w\fR
\fBup\fR \fIctrl-k ctrl-p up\fR
\fByank\fR \fIctrl-y\fR
.SS ACTION COMPOSITION .SS ACTION COMPOSITION

View File

@@ -310,7 +310,7 @@ complete -o default -F _fzf_opts_completion fzf-tmux
d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}" d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
a_cmds=" a_cmds="
awk cat diff diff3 awk cat diff diff3
emacs emacsclient ex file ftp g++ gcc gvim head hg java emacs emacsclient ex file ftp g++ gcc gvim head hg hx java
javac ld less more mvim nvim patch perl python ruby javac ld less more mvim nvim patch perl python ruby
sed sftp sort source tail tee uniq vi view vim wc xdg-open sed sftp sort source tail tee uniq vi view vim wc xdg-open
basename bunzip2 bzip2 chmod chown curl cp dirname du basename bunzip2 bzip2 chmod chown curl cp dirname du

View File

@@ -70,7 +70,7 @@ const usage = `usage: fzf [options]
(default: 0 or center) (default: 0 or center)
--margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L) --margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L)
--padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L) --padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L)
--info=STYLE Finder info style [default|inline|hidden] --info=STYLE Finder info style [default|hidden|inline|inline:SEPARATOR]
--separator=STR String to form horizontal separator on info line --separator=STR String to form horizontal separator on info line
--no-separator Hide info line separator --no-separator Hide info line separator
--scrollbar[=CHAR] Scrollbar character --scrollbar[=CHAR] Scrollbar character
@@ -125,6 +125,8 @@ const usage = `usage: fzf [options]
` `
const defaultInfoSep = " < "
// Case denotes case-sensitivity of search // Case denotes case-sensitivity of search
type Case int type Case int
@@ -246,6 +248,10 @@ func (a previewOpts) sameContentLayout(b previewOpts) bool {
return a.wrap == b.wrap && a.headerLines == b.headerLines return a.wrap == b.wrap && a.headerLines == b.headerLines
} }
func firstLine(s string) string {
return strings.SplitN(s, "\n", 2)[0]
}
// Options stores the values of command-line options // Options stores the values of command-line options
type Options struct { type Options struct {
Fuzzy bool Fuzzy bool
@@ -277,6 +283,7 @@ type Options struct {
ScrollOff int ScrollOff int
FileWord bool FileWord bool
InfoStyle infoStyle InfoStyle infoStyle
InfoSep string
Separator *string Separator *string
JumpLabels string JumpLabels string
Prompt string Prompt string
@@ -609,6 +616,8 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
add(tui.Start) add(tui.Start)
case "load": case "load":
add(tui.Load) add(tui.Load)
case "focus":
add(tui.Focus)
case "alt-enter", "alt-return": case "alt-enter", "alt-return":
chords[tui.CtrlAltKey('m')] = key chords[tui.CtrlAltKey('m')] = key
case "alt-space": case "alt-space":
@@ -912,7 +921,7 @@ const (
func init() { func init() {
executeRegexp = regexp.MustCompile( executeRegexp = regexp.MustCompile(
`(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`) `(?si)[:+](execute(?:-multi|-silent)?|reload(?:-sync)?|preview|(?:change|transform)-(?:query|prompt|border-label|preview-label)|change-preview-window|change-preview|(?:re|un)bind|pos|put)`)
splitRegexp = regexp.MustCompile("[,:]+") splitRegexp = regexp.MustCompile("[,:]+")
actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+") actionNameRegexp = regexp.MustCompile("(?i)^[a-z-]+")
} }
@@ -954,7 +963,7 @@ Loop:
ce = regexp.QuoteMeta(ce) ce = regexp.QuoteMeta(ce)
// @$ or @+ // @$ or @+
loc = regexp.MustCompile(fmt.Sprintf(`^%s.*?(%s[+,]|%s$)`, cs, ce, ce)).FindStringIndex(action) loc = regexp.MustCompile(fmt.Sprintf(`(?s)^%s.*?(%s[+,]|%s$)`, cs, ce, ce)).FindStringIndex(action)
if loc == nil { if loc == nil {
masked += action masked += action
break break
@@ -1220,6 +1229,10 @@ func isExecuteAction(str string) actionType {
return actRebind return actRebind
case "preview": case "preview":
return actPreview return actPreview
case "change-border-label":
return actChangeBorderLabel
case "change-preview-label":
return actChangePreviewLabel
case "change-preview-window": case "change-preview-window":
return actChangePreviewWindow return actChangePreviewWindow
case "change-preview": case "change-preview":
@@ -1238,6 +1251,10 @@ func isExecuteAction(str string) actionType {
return actExecuteMulti return actExecuteMulti
case "put": case "put":
return actPut return actPut
case "transform-border-label":
return actTransformBorderLabel
case "transform-preview-label":
return actTransformPreviewLabel
case "transform-prompt": case "transform-prompt":
return actTransformPrompt return actTransformPrompt
case "transform-query": case "transform-query":
@@ -1309,18 +1326,22 @@ func parseLayout(str string) layoutType {
return layoutDefault return layoutDefault
} }
func parseInfoStyle(str string) infoStyle { func parseInfoStyle(str string) (infoStyle, string) {
switch str { switch str {
case "default": case "default":
return infoDefault return infoDefault, ""
case "inline": case "inline":
return infoInline return infoInline, defaultInfoSep
case "hidden": case "hidden":
return infoHidden return infoHidden, ""
default: default:
errorExit("invalid info style (expected: default|inline|hidden)") prefix := "inline:"
if strings.HasPrefix(str, prefix) {
return infoInline, strings.ReplaceAll(str[len(prefix):], "\n", " ")
}
errorExit("invalid info style (expected: default|hidden|inline|inline:SEPARATOR)")
} }
return infoDefault return infoDefault, ""
} }
func parsePreviewWindow(opts *previewOpts, input string) { func parsePreviewWindow(opts *previewOpts, input string) {
@@ -1588,12 +1609,13 @@ func parseOptions(opts *Options, allArgs []string) {
case "--no-filepath-word": case "--no-filepath-word":
opts.FileWord = false opts.FileWord = false
case "--info": case "--info":
opts.InfoStyle = parseInfoStyle( opts.InfoStyle, opts.InfoSep = parseInfoStyle(
nextString(allArgs, &i, "info style required")) nextString(allArgs, &i, "info style required"))
case "--no-info": case "--no-info":
opts.InfoStyle = infoHidden opts.InfoStyle = infoHidden
case "--inline-info": case "--inline-info":
opts.InfoStyle = infoInline opts.InfoStyle = infoInline
opts.InfoSep = defaultInfoSep
case "--no-inline-info": case "--no-inline-info":
opts.InfoStyle = infoDefault opts.InfoStyle = infoDefault
case "--separator": case "--separator":
@@ -1640,10 +1662,10 @@ func parseOptions(opts *Options, allArgs []string) {
case "--prompt": case "--prompt":
opts.Prompt = nextString(allArgs, &i, "prompt string required") opts.Prompt = nextString(allArgs, &i, "prompt string required")
case "--pointer": case "--pointer":
opts.Pointer = nextString(allArgs, &i, "pointer sign string required") opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
validatePointer = true validatePointer = true
case "--marker": case "--marker":
opts.Marker = nextString(allArgs, &i, "selected sign string required") opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
validateMarker = true validateMarker = true
case "--sync": case "--sync":
opts.Sync = true opts.Sync = true
@@ -1758,10 +1780,10 @@ func parseOptions(opts *Options, allArgs []string) {
} else if match, value := optString(arg, "--prompt="); match { } else if match, value := optString(arg, "--prompt="); match {
opts.Prompt = value opts.Prompt = value
} else if match, value := optString(arg, "--pointer="); match { } else if match, value := optString(arg, "--pointer="); match {
opts.Pointer = value opts.Pointer = firstLine(value)
validatePointer = true validatePointer = true
} else if match, value := optString(arg, "--marker="); match { } else if match, value := optString(arg, "--marker="); match {
opts.Marker = value opts.Marker = firstLine(value)
validateMarker = true validateMarker = true
} else if match, value := optString(arg, "-n", "--nth="); match { } else if match, value := optString(arg, "-n", "--nth="); match {
opts.Nth = splitNth(value) opts.Nth = splitNth(value)
@@ -1778,7 +1800,7 @@ func parseOptions(opts *Options, allArgs []string) {
} else if match, value := optString(arg, "--layout="); match { } else if match, value := optString(arg, "--layout="); match {
opts.Layout = parseLayout(value) opts.Layout = parseLayout(value)
} else if match, value := optString(arg, "--info="); match { } else if match, value := optString(arg, "--info="); match {
opts.InfoStyle = parseInfoStyle(value) opts.InfoStyle, opts.InfoSep = parseInfoStyle(value)
} else if match, value := optString(arg, "--separator="); match { } else if match, value := optString(arg, "--separator="); match {
opts.Separator = &value opts.Separator = &value
} else if match, value := optString(arg, "--scrollbar="); match { } else if match, value := optString(arg, "--scrollbar="); match {

View File

@@ -268,7 +268,7 @@ func TestBind(t *testing.T) {
} }
parseKeymap(keymap, parseKeymap(keymap,
"ctrl-a:kill-line,ctrl-b:toggle-sort+up+down,c:page-up,alt-z:page-down,"+ "ctrl-a:kill-line,ctrl-b:toggle-sort+up+down,c:page-up,alt-z:page-down,"+
"f1:execute(ls {+})+abort+execute(echo {+})+select-all,f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+ "f1:execute(ls {+})+abort+execute(echo \n{+})+select-all,f2:execute/echo {}, {}, {}/,f3:execute[echo '({})'],f4:execute;less {};,"+
"alt-a:execute-Multi@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};,"+ "alt-a:execute-Multi@echo (,),[,],/,:,;,%,{}@,alt-b:execute;echo (,),[,],/,:,@,%,{};,"+
"x:Execute(foo+bar),X:execute/bar+baz/"+ "x:Execute(foo+bar),X:execute/bar+baz/"+
",f1:+first,f1:+top"+ ",f1:+first,f1:+top"+

View File

@@ -147,6 +147,7 @@ type labelPrinter func(tui.Window, int)
type Terminal struct { type Terminal struct {
initDelay time.Duration initDelay time.Duration
infoStyle infoStyle infoStyle infoStyle
infoSep string
separator labelPrinter separator labelPrinter
separatorLen int separatorLen int
spinner []string spinner []string
@@ -276,6 +277,8 @@ const (
reqRefresh reqRefresh
reqReinit reqReinit
reqFullRedraw reqFullRedraw
reqRedrawBorderLabel
reqRedrawPreviewLabel
reqClose reqClose
reqPrintQuery reqPrintQuery
reqPreviewEnqueue reqPreviewEnqueue
@@ -306,6 +309,8 @@ const (
actBackwardDeleteCharEOF actBackwardDeleteCharEOF
actBackwardWord actBackwardWord
actCancel actCancel
actChangeBorderLabel
actChangePreviewLabel
actChangePrompt actChangePrompt
actChangeQuery actChangeQuery
actClearScreen actClearScreen
@@ -347,6 +352,8 @@ const (
actToggleSort actToggleSort
actTogglePreview actTogglePreview
actTogglePreviewWrap actTogglePreviewWrap
actTransformBorderLabel
actTransformPreviewLabel
actTransformPrompt actTransformPrompt
actTransformQuery actTransformQuery
actPreview actPreview
@@ -573,6 +580,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
infoStyle: opts.InfoStyle, infoStyle: opts.InfoStyle,
infoSep: opts.InfoSep,
separator: nil, separator: nil,
spinner: makeSpinner(opts.Unicode), spinner: makeSpinner(opts.Unicode),
queryLen: [2]int{0, 0}, queryLen: [2]int{0, 0},
@@ -723,6 +731,7 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
} }
// Extract ANSI color codes // Extract ANSI color codes
str = firstLine(str)
text, colors, _ := extractColor(str, nil, nil) text, colors, _ := extractColor(str, nil, nil)
runes := []rune(text) runes := []rune(text)
@@ -777,6 +786,7 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
func (t *Terminal) parsePrompt(prompt string) (func(), int) { func (t *Terminal) parsePrompt(prompt string) (func(), int) {
var state *ansiState var state *ansiState
prompt = firstLine(prompt)
trimmed, colors, _ := extractColor(prompt, state, nil) trimmed, colors, _ := extractColor(prompt, state, nil)
item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors} item := &Item{text: util.ToChars([]byte(trimmed)), colors: colors}
@@ -1250,37 +1260,44 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
} }
// Print border label // Print border label
printLabel := func(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape) { t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, false)
if window == nil || render == nil { t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border, false)
return
}
switch borderShape {
case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
var col int
if opts.column == 0 {
col = util.Max(0, (window.Width()-length)/2)
} else if opts.column < 0 {
col = util.Max(0, window.Width()+opts.column+1-length)
} else {
col = util.Min(opts.column-1, window.Width()-length)
}
row := 0
if borderShape == tui.BorderBottom || opts.bottom {
row = window.Height() - 1
}
window.Move(row, col)
render(window, window.Width())
}
}
printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape)
printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border)
for i := 0; i < t.window.Height(); i++ { for i := 0; i < t.window.Height(); i++ {
t.window.MoveAndClear(i, 0) t.window.MoveAndClear(i, 0)
} }
} }
func (t *Terminal) printLabel(window tui.Window, render labelPrinter, opts labelOpts, length int, borderShape tui.BorderShape, redrawBorder bool) {
if window == nil {
return
}
switch borderShape {
case tui.BorderHorizontal, tui.BorderTop, tui.BorderBottom, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
if redrawBorder {
window.DrawHBorder()
}
if render == nil {
return
}
var col int
if opts.column == 0 {
col = util.Max(0, (window.Width()-length)/2)
} else if opts.column < 0 {
col = util.Max(0, window.Width()+opts.column+1-length)
} else {
col = util.Min(opts.column-1, window.Width()-length)
}
row := 0
if borderShape == tui.BorderBottom || opts.bottom {
row = window.Height() - 1
}
window.Move(row, col)
render(window, window.Width())
}
}
func (t *Terminal) move(y int, x int, clear bool) { func (t *Terminal) move(y int, x int, clear bool) {
h := t.window.Height() h := t.window.Height()
@@ -1380,16 +1397,21 @@ func (t *Terminal) printInfo() {
pos = 2 pos = 2
case infoInline: case infoInline:
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1 pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
if pos+len(" < ") > t.window.Width() { str := t.infoSep
return maxWidth := t.window.Width() - pos
width := runewidth.StringWidth(str)
if width > maxWidth {
trimmed, _ := t.trimRight([]rune(str), maxWidth)
str = string(trimmed)
width = maxWidth
} }
t.move(line, pos, t.separatorLen == 0) t.move(line, pos, t.separatorLen == 0)
if t.reading { if t.reading {
t.window.CPrint(tui.ColSpinner, " < ") t.window.CPrint(tui.ColSpinner, str)
} else { } else {
t.window.CPrint(tui.ColPrompt, " < ") t.window.CPrint(tui.ColPrompt, str)
} }
pos += len(" < ") pos += width
case infoHidden: case infoHidden:
return return
} }
@@ -2231,7 +2253,6 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
t.redraw() t.redraw()
t.refresh() t.refresh()
} else { } else {
t.tui.Pause(false)
if captureFirstLine { if captureFirstLine {
out, _ := cmd.StdoutPipe() out, _ := cmd.StdoutPipe()
reader := bufio.NewReader(out) reader := bufio.NewReader(out)
@@ -2242,7 +2263,6 @@ func (t *Terminal) executeCommand(template string, forcePlus bool, background bo
} else { } else {
cmd.Run() cmd.Run()
} }
t.tui.Resume(false, false)
} }
t.executing.Set(false) t.executing.Set(false)
cleanTemporaryFiles() cleanTemporaryFiles()
@@ -2608,6 +2628,11 @@ func (t *Terminal) Loop() {
} }
} }
var onFocus []*action
if actions, prs := t.keymap[tui.Focus.AsEvent()]; prs {
onFocus = actions
}
go func() { go func() {
var focusedIndex int32 = minItem.Index() var focusedIndex int32 = minItem.Index()
var version int64 = -1 var version int64 = -1
@@ -2643,7 +2668,11 @@ func (t *Terminal) Loop() {
if currentItem != nil { if currentItem != nil {
currentIndex = currentItem.Index() currentIndex = currentItem.Index()
} }
if focusedIndex != currentIndex || version != t.version { focusChanged := focusedIndex != currentIndex
if onFocus != nil && focusChanged {
t.serverChan <- onFocus
}
if focusChanged || version != t.version {
version = t.version version = t.version
focusedIndex = currentIndex focusedIndex = currentIndex
refreshPreview(t.previewOpts.command) refreshPreview(t.previewOpts.command)
@@ -2657,6 +2686,10 @@ func (t *Terminal) Loop() {
t.printHeader() t.printHeader()
case reqRefresh: case reqRefresh:
t.suppress = false t.suppress = false
case reqRedrawBorderLabel:
t.printLabel(t.border, t.borderLabel, t.borderLabelOpts, t.borderLabelLen, t.borderShape, true)
case reqRedrawPreviewLabel:
t.printLabel(t.pborder, t.previewLabel, t.previewLabelOpts, t.previewLabelLen, t.previewOpts.border, true)
case reqReinit: case reqReinit:
t.tui.Resume(t.fullscreen, t.sigstop) t.tui.Resume(t.fullscreen, t.sigstop)
t.redraw() t.redraw()
@@ -2909,6 +2942,28 @@ func (t *Terminal) Loop() {
case actChangeQuery: case actChangeQuery:
t.input = []rune(a.a) t.input = []rune(a.a)
t.cx = len(t.input) t.cx = len(t.input)
case actChangeBorderLabel:
if t.border != nil {
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(a.a, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel)
}
case actChangePreviewLabel:
if t.pborder != nil {
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(a.a, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel)
}
case actTransformBorderLabel:
if t.border != nil {
label := t.executeCommand(a.a, false, true, true)
t.borderLabel, t.borderLabelLen = t.ansiLabelPrinter(label, &tui.ColBorderLabel, false)
req(reqRedrawBorderLabel)
}
case actTransformPreviewLabel:
if t.pborder != nil {
label := t.executeCommand(a.a, false, true, true)
t.previewLabel, t.previewLabelLen = t.ansiLabelPrinter(label, &tui.ColPreviewLabel, false)
req(reqRedrawPreviewLabel)
}
case actChangePrompt: case actChangePrompt:
t.prompt, t.promptLen = t.parsePrompt(a.a) t.prompt, t.promptLen = t.parsePrompt(a.a)
req(reqPrompt) req(reqPrompt)

View File

@@ -174,11 +174,7 @@ func (r *LightRenderer) Init() {
} }
} }
if r.mouse { r.enableMouse()
r.csi("?1000h")
r.csi("?1002h")
r.csi("?1006h")
}
r.csi(fmt.Sprintf("%dA", r.MaxY()-1)) r.csi(fmt.Sprintf("%dA", r.MaxY()-1))
r.csi("G") r.csi("G")
r.csi("K") r.csi("K")
@@ -609,6 +605,7 @@ func (r *LightRenderer) rmcup() {
} }
func (r *LightRenderer) Pause(clear bool) { func (r *LightRenderer) Pause(clear bool) {
r.disableMouse()
r.restoreTerminal() r.restoreTerminal()
if clear { if clear {
if r.fullscreen { if r.fullscreen {
@@ -621,6 +618,22 @@ func (r *LightRenderer) Pause(clear bool) {
} }
} }
func (r *LightRenderer) enableMouse() {
if r.mouse {
r.csi("?1000h")
r.csi("?1002h")
r.csi("?1006h")
}
}
func (r *LightRenderer) disableMouse() {
if r.mouse {
r.csi("?1000l")
r.csi("?1002l")
r.csi("?1006l")
}
}
func (r *LightRenderer) Resume(clear bool, sigcont bool) { func (r *LightRenderer) Resume(clear bool, sigcont bool) {
r.setupTerminal() r.setupTerminal()
if clear { if clear {
@@ -629,14 +642,13 @@ func (r *LightRenderer) Resume(clear bool, sigcont bool) {
} else { } else {
r.rmcup() r.rmcup()
} }
r.enableMouse()
r.flush() r.flush()
} else if sigcont && !r.fullscreen && r.mouse { } else if sigcont && !r.fullscreen && r.mouse {
// NOTE: SIGCONT (Coming back from CTRL-Z): // NOTE: SIGCONT (Coming back from CTRL-Z):
// It's highly likely that the offset we obtained at the beginning is // It's highly likely that the offset we obtained at the beginning is
// no longer correct, so we simply disable mouse input. // no longer correct, so we simply disable mouse input.
r.csi("?1000l") r.disableMouse()
r.csi("?1002l")
r.csi("?1006l")
r.mouse = false r.mouse = false
} }
} }
@@ -678,11 +690,7 @@ func (r *LightRenderer) Close() {
} else if !r.fullscreen { } else if !r.fullscreen {
r.csi("u") r.csi("u")
} }
if r.mouse { r.disableMouse()
r.csi("?1000l")
r.csi("?1002l")
r.csi("?1006l")
}
r.flush() r.flush()
r.closePlatform() r.closePlatform()
r.restoreTerminal() r.restoreTerminal()
@@ -719,25 +727,38 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
w.fg = r.theme.Fg.Color w.fg = r.theme.Fg.Color
w.bg = r.theme.Bg.Color w.bg = r.theme.Bg.Color
} }
w.drawBorder() w.drawBorder(false)
return w return w
} }
func (w *LightWindow) drawBorder() { func (w *LightWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *LightWindow) drawBorder(onlyHorizontal bool) {
switch w.border.shape { switch w.border.shape {
case BorderRounded, BorderSharp, BorderBold, BorderDouble: case BorderRounded, BorderSharp, BorderBold, BorderDouble:
w.drawBorderAround() w.drawBorderAround(onlyHorizontal)
case BorderHorizontal: case BorderHorizontal:
w.drawBorderHorizontal(true, true) w.drawBorderHorizontal(true, true)
case BorderVertical: case BorderVertical:
if onlyHorizontal {
return
}
w.drawBorderVertical(true, true) w.drawBorderVertical(true, true)
case BorderTop: case BorderTop:
w.drawBorderHorizontal(true, false) w.drawBorderHorizontal(true, false)
case BorderBottom: case BorderBottom:
w.drawBorderHorizontal(false, true) w.drawBorderHorizontal(false, true)
case BorderLeft: case BorderLeft:
if onlyHorizontal {
return
}
w.drawBorderVertical(true, false) w.drawBorderVertical(true, false)
case BorderRight: case BorderRight:
if onlyHorizontal {
return
}
w.drawBorderVertical(false, true) w.drawBorderVertical(false, true)
} }
} }
@@ -779,23 +800,25 @@ func (w *LightWindow) drawBorderVertical(left, right bool) {
} }
} }
func (w *LightWindow) drawBorderAround() { func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
w.Move(0, 0) w.Move(0, 0)
color := ColBorder color := ColBorder
if w.preview { if w.preview {
color = ColPreviewBorder color = ColPreviewBorder
} }
hw := runewidth.RuneWidth(w.border.horizontal) hw := runewidth.RuneWidth(w.border.horizontal)
vw := runewidth.RuneWidth(w.border.vertical)
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight) tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight) bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
rem := (w.width - tcw) % hw rem := (w.width - tcw) % hw
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight)) w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
for y := 1; y < w.height-1; y++ { if !onlyHorizontal {
w.Move(y, 0) vw := runewidth.RuneWidth(w.border.vertical)
w.CPrint(color, string(w.border.vertical)) for y := 1; y < w.height-1; y++ {
w.CPrint(color, repeat(' ', w.width-vw*2)) w.Move(y, 0)
w.CPrint(color, string(w.border.vertical)) w.CPrint(color, string(w.border.vertical))
w.CPrint(color, repeat(' ', w.width-vw*2))
w.CPrint(color, string(w.border.vertical))
}
} }
w.Move(w.height-1, 0) w.Move(w.height-1, 0)
rem = (w.width - bcw) % hw rem = (w.width - bcw) % hw
@@ -1040,7 +1063,7 @@ func (w *LightWindow) FinishFill() {
} }
func (w *LightWindow) Erase() { func (w *LightWindow) Erase() {
w.drawBorder() w.drawBorder(false)
// We don't erase the window here to avoid flickering during scroll // We don't erase the window here to avoid flickering during scroll
w.Move(0, 0) w.Move(0, 0)
} }

View File

@@ -512,7 +512,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
height: height, height: height,
normal: normal, normal: normal,
borderStyle: borderStyle} borderStyle: borderStyle}
w.drawBorder() w.drawBorder(false)
return w return w
} }
@@ -670,7 +670,11 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn {
return w.fillString(str, NewColorPair(fg, bg, a)) return w.fillString(str, NewColorPair(fg, bg, a))
} }
func (w *TcellWindow) drawBorder() { func (w *TcellWindow) DrawHBorder() {
w.drawBorder(true)
}
func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
shape := w.borderStyle.shape shape := w.borderStyle.shape
if shape == BorderNone { if shape == BorderNone {
return return
@@ -718,17 +722,19 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style) _screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
} }
} }
switch shape { if !onlyHorizontal {
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderLeft: switch shape {
for y := top; y < bot; y++ { case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderLeft:
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style) for y := top; y < bot; y++ {
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
}
} }
} switch shape {
switch shape { case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight: vw := runewidth.RuneWidth(w.borderStyle.vertical)
vw := runewidth.RuneWidth(w.borderStyle.vertical) for y := top; y < bot; y++ {
for y := top; y < bot; y++ { _screen.SetContent(right-vw, y, w.borderStyle.vertical, nil, style)
_screen.SetContent(right-vw, y, w.borderStyle.vertical, nil, style) }
} }
} }
switch shape { switch shape {

View File

@@ -92,6 +92,7 @@ const (
BackwardEOF BackwardEOF
Start Start
Load Load
Focus
AltBS AltBS
@@ -426,6 +427,7 @@ type Window interface {
Width() int Width() int
Height() int Height() int
DrawHBorder()
Refresh() Refresh()
FinishFill() FinishFill()
Close() Close()

View File

@@ -115,7 +115,7 @@ func TestAsUint16(t *testing.T) {
if AsUint16(math.MinInt16) != 0 { if AsUint16(math.MinInt16) != 0 {
t.Error("Expected", 0) t.Error("Expected", 0)
} }
if AsUint16(math.MaxUint32) != math.MaxUint16 { if AsUint16(math.MaxUint16+1) != math.MaxUint16 {
t.Error("Expected", math.MaxUint16) t.Error("Expected", math.MaxUint16)
} }
} }

View File

@@ -1587,6 +1587,11 @@ class TestGoFZF < TestBase
tmux.until { |lines| assert_equal '> 1', lines[-2] } tmux.until { |lines| assert_equal '> 1', lines[-2] }
end end
def test_info_inline_separator
tmux.send_keys 'seq 10 | fzf --info=inline:___ --no-separator', :Enter
tmux.until { |lines| assert_equal '> ___10/10', lines[-1] }
end
def test_change_first_last def test_change_first_last
tmux.send_keys %(seq 1000 | #{FZF} --bind change:first,alt-Z:last), :Enter tmux.send_keys %(seq 1000 | #{FZF} --bind change:first,alt-Z:last), :Enter
tmux.until { |lines| assert_equal 1000, lines.match_count } tmux.until { |lines| assert_equal 1000, lines.match_count }
@@ -2473,12 +2478,31 @@ class TestGoFZF < TestBase
end end
end end
def test_focus_event
tmux.send_keys 'seq 100 | fzf --bind "focus:transform-prompt(echo [[{}]])"', :Enter
tmux.until { |lines| assert_includes(lines[-1], '[[1]]') }
tmux.send_keys :Up
tmux.until { |lines| assert_includes(lines[-1], '[[2]]') }
tmux.send_keys :X
tmux.until { |lines| assert_includes(lines[-1], '[[]]') }
end
def test_labels_center def test_labels_center
tmux.send_keys ': | fzf --border --border-label foobar --preview : --preview-label barfoo', :Enter tmux.send_keys 'echo x | fzf --border --border-label foobar --preview : --preview-label barfoo --bind "space:change-border-label(foobarfoo)+change-preview-label(barfoobar),enter:transform-border-label(echo foo{}foo)+transform-preview-label(echo bar{}bar)"', :Enter
tmux.until do tmux.until do
assert_includes(_1[0], '─foobar─') assert_includes(_1[0], '─foobar─')
assert_includes(_1[1], '─barfoo─') assert_includes(_1[1], '─barfoo─')
end end
tmux.send_keys :space
tmux.until do
assert_includes(_1[0], '─foobarfoo─')
assert_includes(_1[1], '─barfoobar─')
end
tmux.send_keys :Enter
tmux.until do
assert_includes(_1[0], '─fooxfoo─')
assert_includes(_1[1], '─barxbar─')
end
end end
def test_labels_left def test_labels_left