m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-10 12:23:48 -05:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Junegunn Choi
afc955771c Update CHANGELOG 2025-09-17 19:37:33 +09:00
Junegunn Choi
c134c6f898 Update man page 2025-09-17 19:35:43 +09:00
Junegunn Choi
95697b96f8 Doc 2025-09-16 21:40:24 +09:00
Junegunn Choi
3809ebb0c8 Use grey background color 2025-09-16 21:37:00 +09:00
Junegunn Choi
ca9b4d5a49 Use white foreground color 2025-09-16 21:23:24 +09:00
Junegunn Choi
04888f5b94 Make info yellow, no italics 2025-09-16 21:23:24 +09:00
Junegunn Choi
3072b46218 Adjust default themes
Motivation:

`--color 16` can be a better default than `dark` or `light`, since it uses
the colors defined by the current theme. This usually blends in more
naturally and works well in both light and dark modes.

However, some elements were previously hard-coded with white or black
foreground colors, which can cause rendering issues in certain terminal
themes.

* 16
  * Avoid using black or white foreground colors, so it works better with
    both dark and light themes
  * Display 'info' in italic to better separate it from the other parts
* dark / light
  * Display 'info' in italic for consistency
2025-09-16 21:23:24 +09:00
43 changed files with 950 additions and 2048 deletions

View File

@@ -1,20 +0,0 @@
root = true
[*.{sh,bash}]
indent_style = space
indent_size = 2
simplify = true
binary_next_line = false
switch_case_indent = true
space_redirects = true
function_next_line = false
# also bash scripts.
[{install,uninstall,bin/fzf-preview.sh,bin/fzf-tmux}]
indent_style = space
indent_size = 2
simplify = true
binary_next_line = false
switch_case_indent = true
space_redirects = true
function_next_line = false

View File

@@ -23,17 +23,17 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: "1.23" go-version: "1.23"
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
ruby-version: 3.4.6 ruby-version: 3.4.1
- name: Install packages - name: Install packages
run: sudo apt-get install --yes zsh fish tmux shfmt run: sudo apt-get install --yes zsh fish tmux
- name: Install Ruby gems - name: Install Ruby gems
run: bundle install run: bundle install

View File

@@ -20,7 +20,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v6 uses: actions/setup-go@v5
with: with:
go-version: "1.23" go-version: "1.23"
@@ -30,7 +30,7 @@ jobs:
ruby-version: 3.0.0 ruby-version: 3.0.0
- name: Install packages - name: Install packages
run: HOMEBREW_NO_INSTALL_CLEANUP=1 brew install fish zsh tmux shfmt run: HOMEBREW_NO_INSTALL_CLEANUP=1 brew install fish zsh tmux
- name: Install Ruby gems - name: Install Ruby gems
run: gem install --no-document minitest:5.14.2 rubocop:1.0.0 rubocop-minitest:0.10.1 rubocop-performance:1.8.1 run: gem install --no-document minitest:5.14.2 rubocop:1.0.0 rubocop-minitest:0.10.1 rubocop-performance:1.8.1

View File

@@ -1,3 +1,2 @@
golang 1.23 golang 1.23.12
ruby 3.4 ruby 3.4.1
shfmt 3.12

View File

@@ -1,318 +1,32 @@
CHANGELOG CHANGELOG
========= =========
0.67.0
------
- Added `--freeze-left=N` option to keep the leftmost N columns visible.
```sh
# Keeps the file name column fixed and always visible
git grep --line-number --color=always -- '' |
fzf --ansi --delimiter : --freeze-left 1
# Used with --keep-right
git grep --line-number --color=always -- '' |
fzf --ansi --delimiter : --freeze-left 1 --keep-right
```
- Also added `--freeze-right=N` option to keep the rightmost N columns visible.
```sh
fd | fzf --freeze-right 1 --delimiter /
fd | fzf --freeze-left 1 --freeze-right 1 --delimiter /
```
0.66.1
------
- Bug fixes
- Fixed a bug preventing 'ctrl-h' from being bound to an action (#4556)
- Fixed `--no-color` / `NO_COLOR` theme (#4561)
0.66.0 0.66.0
------ ------
- Style changes
### Quick summary - Updated `--color base16` (alias: `16`) theme so that it works better with both dark and light themes.
- Narrowed the gutter column by using the left-half block character (`▌`).
This version introduces many new features centered around the new "raw" mode. - Removed background colors from markers.
- Added `--gutter CHAR` option for customizing the gutter column. Some examples using [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_characters):
| Type | Class | Name | Description | ```sh
| :-- | :-- | :-- | :-- | # Right-aligned gutter
| New | Option | `--raw` | Enable raw mode by default | fzf --gutter '▐'
| New | Option | `--gutter CHAR` | Set the gutter column character |
| New | Option | `--gutter-raw CHAR` | Set the gutter column character in raw mode | # Even thinner gutter
| Enhancement | Option | `--listen SOCKET` | Added support for Unix domain sockets | fzf --gutter '▎'
| New | Action | `toggle-raw` | Toggle raw mode |
| New | Action | `enable-raw` | Enable raw mode | # Checker
| New | Action | `disable-raw` | Disable raw mode | fzf --gutter '▚'
| New | Action | `up-match` | Move up to the matching item |
| New | Action | `down-match` | Move down to the matching item | # Dotted
| New | Action | `best` | Move to the matching item with the best score | fzf --gutter '▖'
| New | Color | `nomatch` | Color for non-matching items in raw mode |
| New | Env Var | `FZF_RAW` | Matching status in raw mode (0, 1, or undefined) | # Full-width
| New | Env Var | `FZF_DIRECTION` | `up` or `down` depending on the layout | fzf --gutter '█'
| New | Env Var | `FZF_SOCK` | Path to the Unix domain socket fzf is listening on |
| Enhancement | Key | `CTRL-N` | `down` -> `down-match` | # No gutter
| Enhancement | Key | `CTRL-P` | `up` -> `up-match` | fzf --gutter ' '
| Enhancement | Shell | `CTRL-R` binding | Toggle raw mode with `ALT-R` | ```
| Enhancement | Shell | `CTRL-R` binding | Opt-out with an empty `FZF_CTRL_R_COMMAND` |
### 1. Introducing "raw" mode
![](https://github.com/user-attachments/assets/9640ae11-b5f7-43fb-95f1-c29307fc17c2)
This version introduces a new "raw" mode (named so because it shows the list
"unfiltered"). In raw mode, non-matching items stay in their original positions,
but appear dimmed. This allows you to see the surrounding items of a match and
better understand the context of it. You can enable raw mode by default with
`--raw`, but it's often more useful when toggled dynamically with the
`toggle-raw` action.
```sh
tree | fzf --reverse --bind alt-r:toggle-raw
```
While non-matching items are displayed in a dimmed color, they are treated just
like matching items, so you place the cursor on them and perform any action. If
you prefer to navigate only through matching items, use the `down-match` and
`up-match` actions, which are from now on bound to `CTRL-N` and `CTRL-P`
respectively, and also to `ALT-DOWN` and `ALT-UP`.
| Key | Action | With `--history` |
| :-- | :-- | :-- |
| `down` | `down` | |
| `up` | `up` | |
| `ctrl-j` | `down` | |
| `ctrl-k` | `up` | |
| `ctrl-n` | `down-match` | `next-history` |
| `ctrl-p` | `up-match` | `prev-history` |
| `alt-down` | `down-match` | |
| `alt-up` | `up-match` | |
> [!NOTE]
> `CTRL-N` and `CTRL-P` are bound to `next-history` and `prev-history` when
> `--history` option is enabled, so in that case, you'll need to manually bind
> them, or use `ALT-DOWN` and `ALT-UP` instead.
> [!TIP]
> `up-match` and `down-match` are equivalent to `up` and `down` when not in
> raw mode, so you can safely bind them to `up` and `arrow` keys if you prefer.
> ```sh
> fzf --bind up:up-match,down:down-match
> ```
#### Customizing the behavior
In raw mode, the input list is presented in its original order, unfiltered, and
your cursor will not move to the matching item automatically. Here are ways to
customize the behavior.
```sh
# When the result list is updated, move the cursor to the item with the best score
# (assuming sorting is not disabled)
fzf --raw --bind result:best
# Move to the first matching item in the original list
# - $FZF_RAW is set to 0 when raw mode is enabled and the current item is a non-match
# - $FZF_DIRECTION is set to either 'up' or 'down' depending on the layout direction
fzf --raw --bind 'result:first+transform:[[ $FZF_RAW = 0 ]] && echo $FZF_DIRECTION-match'
```
#### Customizing the look
##### Gutter
To make the mode visually distinct, the gutter column is rendered in a dashed
line using `` character. But you can customize it with the `--gutter-raw CHAR`
option.
```sh
# Use a thinner gutter instead of the default dashed line
fzf --bind alt-r:toggle-raw --gutter-raw ▎
```
##### Color and style of non-matching items
Non-matching items are displayed in a dimmed color by default, but you can
change it with the `--color nomatch:...` option.
```sh
fzf --raw --color nomatch:red
fzf --raw --color nomatch:red:dim
fzf --raw --color nomatch:red:dim:strikethrough
fzf --raw --color nomatch:red:dim:strikethrough:italic
```
For colored input, dimming alone may not be enough, and you may prefer to remove
colors entirely. For that case, a new special style attribute `strip` has been
added.
```sh
fd --color always | fzf --ansi --raw --color nomatch:dim:strip:strikethrough
```
#### Conditional actions for raw mode
You may want to perform different actions depending on whether the current item
is a match or not. For that, fzf now exports `$FZF_RAW` environment variable.
It's:
- Undefined if raw mode is disabled
- `1` if the current item is a match
- `0` otherwise
```sh
# Do not allow selecting non-matching items
fzf --raw --bind 'enter:transform:[[ ${FZF_RAW-1} = 1 ]] && echo accept || echo bell'
```
#### Leveraging raw mode in shell integration
The `CTRL-R` binding (command history) now lets you toggle raw mode with `ALT-R`.
### 2. Style changes
The screenshot on the right shows the updated gutter style:
![](https://github.com/user-attachments/assets/8ea7b5ef-c99e-4686-905b-22eb078b700a)
This version includes a few minor updates to fzf's classic visual style:
- The gutter column is now narrower, rendered with the left-half block character (``).
- Markers no longer use background colors.
- The `--color base16` theme (alias: `16`) has been updated for better compatibility with both dark and light themes.
### 3. `--listen` now supports Unix domain sockets
If an argument to `--listen` ends with `.sock`, fzf will listen on a Unix
domain socket at the specified path.
```sh
fzf --listen /tmp/fzf.sock --no-tmux
# GET
curl --unix-socket /tmp/fzf.sock http
# POST
curl --unix-socket /tmp/fzf.sock http -d up
```
Note that any existing file at the given path will be removed before creating
the socket, so avoid using an important file path.
### 4. Added options
#### `--gutter CHAR`
The gutter column can now be customized using `--gutter CHAR` and styled with
`--color gutter:...`. Examples:
```sh
# Right-aligned gutter
fzf --gutter '▐'
# Even thinner gutter
fzf --gutter '▎'
# Yellow checker pattern
fzf --gutter '▚' --color gutter:yellow
# Classic style
fzf --gutter ' ' --color gutter:reverse
```
#### `--gutter-raw CHAR`
As noted above, the `--gutter-raw CHAR` option was also added for customizing the gutter column in raw mode.
### 5. Added actions
The following actions were introduced to support working with raw mode:
| Action | Description |
| :-- | :-- |
| `toggle-raw` | Toggle raw mode |
| `enable-raw` | Enable raw mode |
| `disable-raw` | Disable raw mode |
| `up-match` | Move up to the matching item; identical to `up` if raw mode is disabled |
| `down-match` | Move down to the matching item; identical to `down` if raw mode is disabled |
| `best` | Move to the matching item with the best score; identical to `first` if raw mode is disabled |
### 6. Added environment variables
#### `$FZF_DIRECTION`
`$FZF_DIRECTION` is now exported to child processes, indicating the list direction of the current layout:
- `up` for the default layout
- `down` for `reverse` or `reverse-list`
This simplifies writing transform actions involving layout-dependent actions
like `{up,down}-match`, `{up,down}-selected`, and `toggle+{up,down}`.
```sh
fzf --raw --bind 'result:first+transform:[[ $FZF_RAW = 0 ]] && echo $FZF_DIRECTION-match'
```
#### `$FZF_SOCK`
When fzf is listening on a Unix domain socket using `--listen`, the path to the
socket is exported as `$FZF_SOCK`, analogous to `$FZF_PORT` for TCP sockets.
#### `$FZF_RAW`
As described above, `$FZF_RAW` is now exported to child processes in raw mode,
indicating whether the current item is a match (`1`) or not (`0`). It is not
defined when not in raw mode.
#### `$FZF_CTRL_R_COMMAND`
You can opt-out `CTRL-R` binding from the shell integration by setting
`FZF_CTRL_R_COMMAND` to an empty string. Setting it to any other value is not
supported and will result in a warning.
```sh
# Disable the CTRL-R binding from the shell integration
FZF_CTRL_R_COMMAND= eval "$(fzf --bash)"
```
### 7. Added key support for `--bind`
Pull request [#3996](https://github.com/junegunn/fzf/pull/3996) added support
for many additional keys for `--bind` option, such as `ctrl-backspace`.
### 8. Breaking changes
#### Hiding the gutter column
In the previous versions, the recommended way to hide the gutter column was to
set `--color gutter:-1`. That's because the gutter column was just a space
character, reversed. But now that it's using a visible character (``), applying
the default color is no longer enough to hide it. Instead, you can set it to
a space character.
```sh
# Hide the gutter column
fzf --gutter ' '
# Classic style
fzf --gutter ' ' --color gutter:reverse
```
#### `--color` option
In the previous versions, some elements had default style attributes applied and
you would have to explicitly unset them with `regular` attribute if you wanted
to reset them. This is no longer needed now, as the default style attributes
are applied only when you do not specify any color or style for that element.
```sh
# No 'dim', just red and italic.
fzf --ghost 'Type to search' --color ghost:red:italic
```
#### Compatibility changes
Starting with this release, fzf is built with Go 1.23. Support for some old OS versions has been dropped.
See https://go.dev/wiki/MinimumRequirements.
0.65.2 0.65.2
------ ------

View File

@@ -5,15 +5,6 @@ MAKEFILE := $(realpath $(lastword $(MAKEFILE_LIST)))
ROOT_DIR := $(shell dirname $(MAKEFILE)) ROOT_DIR := $(shell dirname $(MAKEFILE))
SOURCES := $(wildcard *.go src/*.go src/*/*.go shell/*sh man/man1/*.1) $(MAKEFILE) SOURCES := $(wildcard *.go src/*.go src/*/*.go shell/*sh man/man1/*.1) $(MAKEFILE)
BASH_SCRIPTS := $(ROOT_DIR)/bin/fzf-preview.sh \
$(ROOT_DIR)/bin/fzf-tmux \
$(ROOT_DIR)/install \
$(ROOT_DIR)/uninstall \
$(ROOT_DIR)/shell/common.sh \
$(ROOT_DIR)/shell/update.sh \
$(ROOT_DIR)/shell/completion.bash \
$(ROOT_DIR)/shell/key-bindings.bash
ifdef FZF_VERSION ifdef FZF_VERSION
VERSION := $(FZF_VERSION) VERSION := $(FZF_VERSION)
else else
@@ -97,14 +88,9 @@ itest:
bench: bench:
cd src && SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" -run=Bench -bench=. -benchmem cd src && SHELL=/bin/sh GOOS= $(GO) test -v -tags "$(TAGS)" -run=Bench -bench=. -benchmem
lint: $(SOURCES) test/*.rb test/lib/*.rb ${BASH_SCRIPTS} lint: $(SOURCES) test/*.rb test/lib/*.rb
[ -z "$$(gofmt -s -d src)" ] || (gofmt -s -d src; exit 1) [ -z "$$(gofmt -s -d src)" ] || (gofmt -s -d src; exit 1)
bundle exec rubocop -a --require rubocop-minitest --require rubocop-performance bundle exec rubocop -a --require rubocop-minitest --require rubocop-performance
shell/update.sh --check ${BASH_SCRIPTS}
fmt: $(SOURCES) $(BASH_SCRIPTS)
gofmt -s -w src
shell/update.sh ${BASH_SCRIPTS}
install: bin/fzf install: bin/fzf
@@ -203,4 +189,4 @@ update:
$(GO) get -u $(GO) get -u
$(GO) mod tidy $(GO) mod tidy
.PHONY: all generate build release test itest bench lint install clean docker docker-test update fmt .PHONY: all generate build release test itest bench lint install clean docker docker-test update

File diff suppressed because one or more lines are too long

View File

@@ -49,9 +49,9 @@ if [[ ! $type =~ image/ ]]; then
fi fi
dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES} dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}
if [[ $dim == x ]]; then if [[ $dim = x ]]; then
dim=$(stty size < /dev/tty | awk '{print $2 "x" $1}') dim=$(stty size < /dev/tty | awk '{print $2 "x" $1}')
elif ! [[ $KITTY_WINDOW_ID ]] && ((FZF_PREVIEW_TOP + FZF_PREVIEW_LINES == $(stty size < /dev/tty | awk '{print $1}'))); then elif ! [[ $KITTY_WINDOW_ID ]] && (( FZF_PREVIEW_TOP + FZF_PREVIEW_LINES == $(stty size < /dev/tty | awk '{print $1}') )); then
# Avoid scrolling issue when the Sixel image touches the bottom of the screen # Avoid scrolling issue when the Sixel image touches the bottom of the screen
# * https://github.com/junegunn/fzf/issues/2544 # * https://github.com/junegunn/fzf/issues/2544
dim=${FZF_PREVIEW_COLUMNS}x$((FZF_PREVIEW_LINES - 1)) dim=${FZF_PREVIEW_COLUMNS}x$((FZF_PREVIEW_LINES - 1))

View File

@@ -8,7 +8,7 @@ fail() {
} }
fzf="$(command which fzf)" || fzf="$(dirname "$0")/fzf" fzf="$(command which fzf)" || fzf="$(dirname "$0")/fzf"
[[ -x $fzf ]] || fail 'fzf executable not found' [[ -x "$fzf" ]] || fail 'fzf executable not found'
args=() args=()
opt="" opt=""
@@ -16,8 +16,8 @@ skip=""
swap="" swap=""
close="" close=""
term="" term=""
[[ -n $LINES ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}") [[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}")
[[ -n $COLUMNS ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}") [[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
tmux_version=$(tmux -V | sed 's/[^0-9.]//g') tmux_version=$(tmux -V | sed 's/[^0-9.]//g')
tmux_32=$(awk '{print ($1 >= 3.2)}' <<< "$tmux_version" 2> /dev/null || bc -l <<< "$tmux_version >= 3.2") tmux_32=$(awk '{print ($1 >= 3.2)}' <<< "$tmux_version" 2> /dev/null || bc -l <<< "$tmux_version >= 3.2")
@@ -47,7 +47,7 @@ help() {
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
arg="$1" arg="$1"
shift shift
[[ -z $skip ]] && case "$arg" in [[ -z "$skip" ]] && case "$arg" in
-) -)
term=1 term=1
;; ;;
@@ -58,19 +58,19 @@ while [[ $# -gt 0 ]]; do
echo "fzf-tmux (with fzf $("$fzf" --version))" echo "fzf-tmux (with fzf $("$fzf" --version))"
exit exit
;; ;;
-p* | -w* | -h* | -x* | -y* | -d* | -u* | -r* | -l*) -p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
if [[ $arg =~ ^-[pwhxy] ]]; then if [[ "$arg" =~ ^-[pwhxy] ]]; then
[[ $opt =~ "-E" ]] || opt="-E" [[ "$opt" =~ "-E" ]] || opt="-E"
elif [[ $arg =~ ^.[lr] ]]; then elif [[ "$arg" =~ ^.[lr] ]]; then
opt="-h" opt="-h"
if [[ $arg =~ ^.l ]]; then if [[ "$arg" =~ ^.l ]]; then
opt="$opt -d" opt="$opt -d"
swap="; swap-pane -D ; select-pane -L" swap="; swap-pane -D ; select-pane -L"
close="; tmux swap-pane -D" close="; tmux swap-pane -D"
fi fi
else else
opt="" opt=""
if [[ $arg =~ ^.u ]]; then if [[ "$arg" =~ ^.u ]]; then
opt="$opt -d" opt="$opt -d"
swap="; swap-pane -D ; select-pane -U" swap="; swap-pane -D ; select-pane -U"
close="; tmux swap-pane -D" close="; tmux swap-pane -D"
@@ -79,7 +79,7 @@ while [[ $# -gt 0 ]]; do
if [[ ${#arg} -gt 2 ]]; then if [[ ${#arg} -gt 2 ]]; then
size="${arg:2}" size="${arg:2}"
else else
if [[ $1 =~ ^[0-9%,]+$ ]] || [[ $1 =~ ^[A-Z]$ ]]; then if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
size="$1" size="$1"
shift shift
else else
@@ -87,37 +87,37 @@ while [[ $# -gt 0 ]]; do
fi fi
fi fi
if [[ $arg =~ ^-p ]]; then if [[ "$arg" =~ ^-p ]]; then
if [[ -n $size ]]; then if [[ -n "$size" ]]; then
w=${size%%,*} w=${size%%,*}
h=${size##*,} h=${size##*,}
opt="$opt -w$w -h$h" opt="$opt -w$w -h$h"
fi fi
elif [[ $arg =~ ^-[whxy] ]]; then elif [[ "$arg" =~ ^-[whxy] ]]; then
opt="$opt ${arg:0:2}$size" opt="$opt ${arg:0:2}$size"
elif [[ $size =~ %$ ]]; then elif [[ "$size" =~ %$ ]]; then
size=${size:0:${#size}-1} size=${size:0:((${#size}-1))}
if [[ $tmux_32 == 1 ]]; then if [[ $tmux_32 = 1 ]]; then
if [[ -n $swap ]]; then if [[ -n "$swap" ]]; then
opt="$opt -l $((100 - size))%" opt="$opt -l $(( 100 - size ))%"
else else
opt="$opt -l $size%" opt="$opt -l $size%"
fi fi
else else
if [[ -n $swap ]]; then if [[ -n "$swap" ]]; then
opt="$opt -p $((100 - size))" opt="$opt -p $(( 100 - size ))"
else else
opt="$opt -p $size" opt="$opt -p $size"
fi fi
fi fi
else else
if [[ -n $swap ]]; then if [[ -n "$swap" ]]; then
if [[ $arg =~ ^.l ]]; then if [[ "$arg" =~ ^.l ]]; then
max=$columns max=$columns
else else
max=$lines max=$lines
fi fi
size=$((max - size)) size=$(( max - size ))
[[ $size -lt 0 ]] && size=0 [[ $size -lt 0 ]] && size=0
opt="$opt -l $size" opt="$opt -l $size"
else else
@@ -135,10 +135,10 @@ while [[ $# -gt 0 ]]; do
args+=("$arg") args+=("$arg")
;; ;;
esac esac
[[ -n $skip ]] && args+=("$arg") [[ -n "$skip" ]] && args+=("$arg")
done done
if [[ -z $TMUX ]]; then if [[ -z "$TMUX" ]]; then
"$fzf" "${args[@]}" "$fzf" "${args[@]}"
exit $? exit $?
fi fi
@@ -149,7 +149,7 @@ fi
args=("${args[@]}" "--no-height" "--bind=ctrl-z:ignore" "--no-tmux") args=("${args[@]}" "--no-height" "--bind=ctrl-z:ignore" "--no-tmux")
# Handle zoomed tmux pane without popup options by moving it to a temp window # Handle zoomed tmux pane without popup options by moving it to a temp window
if [[ ! $opt =~ "-E" ]] && tmux list-panes -F '#F' | grep -q Z; then if [[ ! "$opt" =~ "-E" ]] && tmux list-panes -F '#F' | grep -q Z; then
zoomed_without_popup=1 zoomed_without_popup=1
original_window=$(tmux display-message -p "#{window_id}") original_window=$(tmux display-message -p "#{window_id}")
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'") tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c fzf-tmux is running\\r\"; done; done'")
@@ -165,22 +165,22 @@ fifo1="${TMPDIR:-/tmp}/fzf-fifo1-$id"
fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id" fifo2="${TMPDIR:-/tmp}/fzf-fifo2-$id"
fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id" fifo3="${TMPDIR:-/tmp}/fzf-fifo3-$id"
if tmux_win_opts=$(tmux show-options -p remain-on-exit \; show-options -p synchronize-panes 2> /dev/null); then if tmux_win_opts=$(tmux show-options -p remain-on-exit \; show-options -p synchronize-panes 2> /dev/null); then
tmux_win_opts=($(sed '/ off/d; s/synchronize-panes/set-option -p synchronize-panes/; s/remain-on-exit/set-option -p remain-on-exit/; s/$/ \\;/' <<< "$tmux_win_opts")) tmux_win_opts=( $(sed '/ off/d; s/synchronize-panes/set-option -p synchronize-panes/; s/remain-on-exit/set-option -p remain-on-exit/; s/$/ \\;/' <<< "$tmux_win_opts") )
tmux_off_opts='; set-option -p synchronize-panes off ; set-option -p remain-on-exit off' tmux_off_opts='; set-option -p synchronize-panes off ; set-option -p remain-on-exit off'
else else
tmux_win_opts=($(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/')) tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
tmux_off_opts='; set-window-option synchronize-panes off ; set-window-option remain-on-exit off' tmux_off_opts='; set-window-option synchronize-panes off ; set-window-option remain-on-exit off'
fi fi
cleanup() { cleanup() {
\rm -f $argsf $fifo1 $fifo2 $fifo3 \rm -f $argsf $fifo1 $fifo2 $fifo3
# Restore tmux window options # Restore tmux window options
if [[ ${#tmux_win_opts[@]} -gt 1 ]]; then if [[ "${#tmux_win_opts[@]}" -gt 1 ]]; then
eval "tmux ${tmux_win_opts[*]}" eval "tmux ${tmux_win_opts[*]}"
fi fi
# Remove temp window if we were zoomed without popup options # Remove temp window if we were zoomed without popup options
if [[ -n $zoomed_without_popup ]]; then if [[ -n "$zoomed_without_popup" ]]; then
tmux display-message -p "#{window_id}" > /dev/null tmux display-message -p "#{window_id}" > /dev/null
tmux swap-pane -t $original_window \; \ tmux swap-pane -t $original_window \; \
select-window -t $original_window \; \ select-window -t $original_window \; \
@@ -197,10 +197,10 @@ trap 'cleanup 1' SIGUSR1
trap 'cleanup' EXIT trap 'cleanup' EXIT
envs="export TERM=$TERM " envs="export TERM=$TERM "
if [[ $opt =~ "-E" ]]; then if [[ "$opt" =~ "-E" ]]; then
if [[ $tmux_version == 3.2 ]]; then if [[ $tmux_version = 3.2 ]]; then
FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS" FZF_DEFAULT_OPTS="--margin 0,1 $FZF_DEFAULT_OPTS"
elif [[ $tmux_32 == 1 ]]; then elif [[ $tmux_32 = 1 ]]; then
FZF_DEFAULT_OPTS="--border $FZF_DEFAULT_OPTS" FZF_DEFAULT_OPTS="--border $FZF_DEFAULT_OPTS"
opt="-B $opt" opt="-B $opt"
else else
@@ -211,8 +211,8 @@ fi
envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")" envs="$envs FZF_DEFAULT_COMMAND=$(printf %q "$FZF_DEFAULT_COMMAND")"
envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")" envs="$envs FZF_DEFAULT_OPTS=$(printf %q "$FZF_DEFAULT_OPTS")"
envs="$envs FZF_DEFAULT_OPTS_FILE=$(printf %q "$FZF_DEFAULT_OPTS_FILE")" envs="$envs FZF_DEFAULT_OPTS_FILE=$(printf %q "$FZF_DEFAULT_OPTS_FILE")"
[[ -n $RUNEWIDTH_EASTASIAN ]] && envs="$envs RUNEWIDTH_EASTASIAN=$(printf %q "$RUNEWIDTH_EASTASIAN")" [[ -n "$RUNEWIDTH_EASTASIAN" ]] && envs="$envs RUNEWIDTH_EASTASIAN=$(printf %q "$RUNEWIDTH_EASTASIAN")"
[[ -n $BAT_THEME ]] && envs="$envs BAT_THEME=$(printf %q "$BAT_THEME")" [[ -n "$BAT_THEME" ]] && envs="$envs BAT_THEME=$(printf %q "$BAT_THEME")"
echo "$envs;" > "$argsf" echo "$envs;" > "$argsf"
# Build arguments to fzf # Build arguments to fzf
@@ -224,9 +224,9 @@ close="; trap - EXIT SIGINT SIGTERM $close"
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX") export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
mkfifo -m o+w $fifo2 mkfifo -m o+w $fifo2
if [[ $opt =~ "-E" ]]; then if [[ "$opt" =~ "-E" ]]; then
cat $fifo2 & cat $fifo2 &
if [[ -n $term ]] || [[ -t 0 ]]; then if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf cat <<< "\"$fzf\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
else else
mkfifo $fifo1 mkfifo $fifo1
@@ -239,7 +239,7 @@ if [[ $opt =~ "-E" ]]; then
fi fi
mkfifo -m o+w $fifo3 mkfifo -m o+w $fifo3
if [[ -n $term ]] || [[ -t 0 ]]; then if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf cat <<< "\"$fzf\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
else else
mkfifo $fifo1 mkfifo $fifo1
@@ -249,9 +249,6 @@ fi
tmux \ tmux \
split-window -c "$PWD" $opt "bash -c 'exec -a fzf bash $argsf'" $swap \ split-window -c "$PWD" $opt "bash -c 'exec -a fzf bash $argsf'" $swap \
$tmux_off_opts \ $tmux_off_opts \
> /dev/null 2>&1 || { > /dev/null 2>&1 || { "$fzf" "${args[@]}"; exit $?; }
"$fzf" "${args[@]}"
exit $?
}
cat $fifo2 cat $fifo2
exit "$(cat $fifo3)" exit "$(cat $fifo3)"

110
install
View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.66.1 version=0.65.2
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2
@@ -46,16 +46,16 @@ for opt in "$@"; do
prefix_expand=${XDG_CONFIG_HOME:-$HOME/.config}/fzf/fzf prefix_expand=${XDG_CONFIG_HOME:-$HOME/.config}/fzf/fzf
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/fzf" mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/fzf"
;; ;;
--key-bindings) key_bindings=1 ;; --key-bindings) key_bindings=1 ;;
--no-key-bindings) key_bindings=0 ;; --no-key-bindings) key_bindings=0 ;;
--completion) auto_completion=1 ;; --completion) auto_completion=1 ;;
--no-completion) auto_completion=0 ;; --no-completion) auto_completion=0 ;;
--update-rc) update_config=1 ;; --update-rc) update_config=1 ;;
--no-update-rc) update_config=0 ;; --no-update-rc) update_config=0 ;;
--bin) ;; --bin) ;;
--no-bash) shells=${shells/bash/} ;; --no-bash) shells=${shells/bash/} ;;
--no-zsh) shells=${shells/zsh/} ;; --no-zsh) shells=${shells/zsh/} ;;
--no-fish) shells=${shells/fish/} ;; --no-fish) shells=${shells/fish/} ;;
*) *)
echo "unknown option: $opt" echo "unknown option: $opt"
help help
@@ -104,7 +104,7 @@ check_binary() {
link_fzf_in_path() { link_fzf_in_path() {
if which_fzf="$(command -v fzf)"; then if which_fzf="$(command -v fzf)"; then
echo ' - Found in $PATH' echo " - Found in \$PATH"
echo " - Creating symlink: bin/fzf -> $which_fzf" echo " - Creating symlink: bin/fzf -> $which_fzf"
(cd "$fzf_base"/bin && rm -f fzf && ln -sf "$which_fzf" fzf) (cd "$fzf_base"/bin && rm -f fzf && ln -sf "$which_fzf" fzf)
check_binary && return check_binary && return
@@ -114,22 +114,22 @@ link_fzf_in_path() {
try_curl() { try_curl() {
command -v curl > /dev/null && command -v curl > /dev/null &&
if [[ $1 =~ tar.gz$ ]]; then if [[ $1 =~ tar.gz$ ]]; then
curl -fL $1 | tar --no-same-owner -xzf - curl -fL $1 | tar --no-same-owner -xzf -
else else
local temp=${TMPDIR:-/tmp}/fzf.zip local temp=${TMPDIR:-/tmp}/fzf.zip
curl -fLo "$temp" $1 && unzip -o "$temp" && rm -f "$temp" curl -fLo "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
fi fi
} }
try_wget() { try_wget() {
command -v wget > /dev/null && command -v wget > /dev/null &&
if [[ $1 =~ tar.gz$ ]]; then if [[ $1 =~ tar.gz$ ]]; then
wget -O - $1 | tar --no-same-owner -xzf - wget -O - $1 | tar --no-same-owner -xzf -
else else
local temp=${TMPDIR:-/tmp}/fzf.zip local temp=${TMPDIR:-/tmp}/fzf.zip
wget -O "$temp" $1 && unzip -o "$temp" && rm -f "$temp" wget -O "$temp" $1 && unzip -o "$temp" && rm -f "$temp"
fi fi
} }
download() { download() {
@@ -164,29 +164,29 @@ download() {
} }
# Try to download binary executable # Try to download binary executable
archi=$(uname -smo 2> /dev/null || uname -sm) archi=$(uname -smo 2>/dev/null || uname -sm)
binary_available=1 binary_available=1
binary_error="" binary_error=""
case "$archi" in case "$archi" in
Darwin\ arm64*) download fzf-$version-darwin_arm64.tar.gz ;; Darwin\ arm64*) download fzf-$version-darwin_arm64.tar.gz ;;
Darwin\ x86_64*) download fzf-$version-darwin_amd64.tar.gz ;; Darwin\ x86_64*) download fzf-$version-darwin_amd64.tar.gz ;;
Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;; Linux\ armv5*) download fzf-$version-linux_armv5.tar.gz ;;
Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;; Linux\ armv6*) download fzf-$version-linux_armv6.tar.gz ;;
Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;; Linux\ armv7*) download fzf-$version-linux_armv7.tar.gz ;;
Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;; Linux\ armv8*) download fzf-$version-linux_arm64.tar.gz ;;
Linux\ aarch64\ Android) download fzf-$version-android_arm64.tar.gz ;; Linux\ aarch64\ Android) download fzf-$version-android_arm64.tar.gz ;;
Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;; Linux\ aarch64*) download fzf-$version-linux_arm64.tar.gz ;;
Linux\ loongarch64*) download fzf-$version-linux_loong64.tar.gz ;; Linux\ loongarch64*) download fzf-$version-linux_loong64.tar.gz ;;
Linux\ ppc64le*) download fzf-$version-linux_ppc64le.tar.gz ;; Linux\ ppc64le*) download fzf-$version-linux_ppc64le.tar.gz ;;
Linux\ *64*) download fzf-$version-linux_amd64.tar.gz ;; Linux\ *64*) download fzf-$version-linux_amd64.tar.gz ;;
Linux\ s390x*) download fzf-$version-linux_s390x.tar.gz ;; Linux\ s390x*) download fzf-$version-linux_s390x.tar.gz ;;
FreeBSD\ *64*) download fzf-$version-freebsd_amd64.tar.gz ;; FreeBSD\ *64*) download fzf-$version-freebsd_amd64.tar.gz ;;
OpenBSD\ *64*) download fzf-$version-openbsd_amd64.tar.gz ;; OpenBSD\ *64*) download fzf-$version-openbsd_amd64.tar.gz ;;
CYGWIN*\ *64*) download fzf-$version-windows_amd64.zip ;; CYGWIN*\ *64*) download fzf-$version-windows_amd64.zip ;;
MINGW*\ *64*) download fzf-$version-windows_amd64.zip ;; MINGW*\ *64*) download fzf-$version-windows_amd64.zip ;;
MSYS*\ *64*) download fzf-$version-windows_amd64.zip ;; MSYS*\ *64*) download fzf-$version-windows_amd64.zip ;;
Windows*\ *64*) download fzf-$version-windows_amd64.zip ;; Windows*\ *64*) download fzf-$version-windows_amd64.zip ;;
*) binary_available=0 binary_error=1 ;; *) binary_available=0 binary_error=1 ;;
esac esac
cd "$fzf_base" cd "$fzf_base"
@@ -215,7 +215,7 @@ if [ -n "$binary_error" ]; then
fi fi
fi fi
[[ $* =~ "--bin" ]] && exit 0 [[ "$*" =~ "--bin" ]] && exit 0
for s in $shells; do for s in $shells; do
if ! command -v "$s" > /dev/null; then if ! command -v "$s" > /dev/null; then
@@ -242,7 +242,7 @@ fi
echo echo
for shell in $shells; do for shell in $shells; do
[[ $shell == fish ]] && continue [[ "$shell" = fish ]] && continue
src=${prefix_expand}.${shell} src=${prefix_expand}.${shell}
echo -n "Generate $src ... " echo -n "Generate $src ... "
@@ -266,7 +266,7 @@ fi
EOF EOF
if [[ $auto_completion -eq 1 ]] && [[ $key_bindings -eq 1 ]]; then if [[ $auto_completion -eq 1 ]] && [[ $key_bindings -eq 1 ]]; then
if [[ $shell == zsh ]]; then if [[ "$shell" = zsh ]]; then
echo "source <(fzf --$shell)" >> "$src" echo "source <(fzf --$shell)" >> "$src"
else else
echo "eval \"\$(fzf --$shell)\"" >> "$src" echo "eval \"\$(fzf --$shell)\"" >> "$src"
@@ -286,7 +286,7 @@ EOF
done done
# fish # fish
if [[ $shells =~ fish ]]; then if [[ "$shells" =~ fish ]]; then
echo -n "Update fish_user_paths ... " echo -n "Update fish_user_paths ... "
fish << EOF fish << EOF
echo \$fish_user_paths | \grep "$fzf_base"/bin > /dev/null echo \$fish_user_paths | \grep "$fzf_base"/bin > /dev/null
@@ -318,9 +318,9 @@ append_line() {
sed 's/^/ Line /' <<< "$lines" sed 's/^/ Line /' <<< "$lines"
update=0 update=0
if ! \grep -qv "^[0-9]*:[[:space:]]*#" <<< "$lines"; then if ! \grep -qv "^[0-9]*:[[:space:]]*#" <<< "$lines" ; then
echo " - But they all seem to be commented" echo " - But they all seem to be commented"
ask " - Continue modifying $file?" ask " - Continue modifying $file?"
update=$? update=$?
fi fi
fi fi
@@ -356,12 +356,12 @@ if [ $update_config -eq 2 ]; then
fi fi
echo echo
for shell in $shells; do for shell in $shells; do
[[ $shell == fish ]] && continue [[ "$shell" = fish ]] && continue
[ $shell = zsh ] && dest=${ZDOTDIR:-~}/.zshrc || dest=~/.bashrc [ $shell = zsh ] && dest=${ZDOTDIR:-~}/.zshrc || dest=~/.bashrc
append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}" append_line $update_config "[ -f ${prefix}.${shell} ] && source ${prefix}.${shell}" "$dest" "${prefix}.${shell}"
done done
if [ $key_bindings -eq 1 ] && [[ $shells =~ fish ]]; then if [ $key_bindings -eq 1 ] && [[ "$shells" =~ fish ]]; then
bind_file="${fish_dir}/functions/fish_user_key_bindings.fish" bind_file="${fish_dir}/functions/fish_user_key_bindings.fish"
if [ ! -e "$bind_file" ]; then if [ ! -e "$bind_file" ]; then
mkdir -p "${fish_dir}/functions" mkdir -p "${fish_dir}/functions"
@@ -386,13 +386,13 @@ fi
if [ $update_config -eq 1 ]; then if [ $update_config -eq 1 ]; then
echo 'Finished. Restart your shell or reload config file.' echo 'Finished. Restart your shell or reload config file.'
if [[ $shells =~ bash ]]; then if [[ "$shells" =~ bash ]]; then
echo -n ' source ~/.bashrc # bash' echo -n ' source ~/.bashrc # bash'
[[ $archi =~ Darwin ]] && echo -n ' (.bashrc should be loaded from .bash_profile)' [[ "$archi" =~ Darwin ]] && echo -n ' (.bashrc should be loaded from .bash_profile)'
echo echo
fi fi
[[ $shells =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh" [[ "$shells" =~ zsh ]] && echo " source ${ZDOTDIR:-~}/.zshrc # zsh"
[[ $shells =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish' [[ "$shells" =~ fish ]] && [ $key_bindings -eq 1 ] && echo ' fzf_key_bindings # fish'
echo echo
echo 'Use uninstall script to remove fzf.' echo 'Use uninstall script to remove fzf.'
echo echo

View File

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

View File

@@ -11,7 +11,7 @@ import (
"github.com/junegunn/fzf/src/protector" "github.com/junegunn/fzf/src/protector"
) )
var version = "0.66" var version = "0.65"
var revision = "devel" var revision = "devel"
//go:embed shell/key-bindings.bash //go:embed shell/key-bindings.bash

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 "Oct 2025" "fzf 0.66.1" "fzf\-tmux - open fzf in tmux split pane" .TH fzf\-tmux 1 "Aug 2025" "fzf 0.65.2" "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 "Oct 2025" "fzf 0.66.1" "fzf - a command-line fuzzy finder" .TH fzf 1 "Aug 2025" "fzf 0.65.2" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -299,7 +299,6 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
\fBheader (header\-fg) \fRHeader \fBheader (header\-fg) \fRHeader
\fBfooter (footer\-fg) \fRFooter \fBfooter (footer\-fg) \fRFooter
\fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes) \fBnth \fRParts of the line specified by \fB\-\-nth\fR (only supports attributes)
\fBnomatch \fRNon-matching items in raw mode (default: \fBdim\fR)
.B ANSI COLORS: .B ANSI COLORS:
\fB\-1 \fRDefault terminal foreground/background color \fB\-1 \fRDefault terminal foreground/background color
@@ -325,8 +324,7 @@ color mappings. Each entry is separated by a comma and/or whitespaces.
\fB#rrggbb \fR24-bit colors \fB#rrggbb \fR24-bit colors
.B ANSI ATTRIBUTES: (Only applies to foreground colors) .B ANSI ATTRIBUTES: (Only applies to foreground colors)
\fBregular \fRClear previously set attributes; should precede the other ones \fBregular \fRClears previously set attributes; should precede the other ones
\fBstrip \fRRemove colors
\fBbold\fR \fBbold\fR
\fBunderline\fR \fBunderline\fR
\fBreverse\fR \fBreverse\fR
@@ -598,9 +596,6 @@ Indicator for wrapped lines. The default is '↳ ' or '> ' depending on
.B "\-\-no\-multi\-line" .B "\-\-no\-multi\-line"
Disable multi-line display of items when using \fB\-\-read0\fR Disable multi-line display of items when using \fB\-\-read0\fR
.TP .TP
.B "\-\-raw"
Enable raw mode where non-matching items are also displayed in a dimmed color.
.TP
.B "\-\-track" .B "\-\-track"
Make fzf track the current selection when the result list is updated. Make fzf track the current selection when the result list is updated.
This can be useful when browsing logs using fzf with sorting disabled. It is This can be useful when browsing logs using fzf with sorting disabled. It is
@@ -629,16 +624,9 @@ Render empty lines between each item
The given string will be repeated to draw a horizontal line on each gap The given string will be repeated to draw a horizontal line on each gap
(default: '┈' or '\-' depending on \fB\-\-no\-unicode\fR). (default: '┈' or '\-' depending on \fB\-\-no\-unicode\fR).
.TP .TP
.BI "\-\-freeze\-left=" "N"
Number of fields to freeze on the left.
.TP
.BI "\-\-freeze\-right=" "N"
Number of fields to freeze on the right.
.TP
.B "\-\-keep\-right" .B "\-\-keep\-right"
Keep the right end of the line visible when it's too long. Effective only when Keep the right end of the line visible when it's too long. Effective only when
the query string is empty. Use \fB\-\-freeze\-right=1\fR instead if you want the query string is empty.
the last field to be always visible even with a non-empty query.
.TP .TP
.BI "\-\-scroll\-off=" "LINES" .BI "\-\-scroll\-off=" "LINES"
Number of screen lines to keep above or below when scrolling to the top or to Number of screen lines to keep above or below when scrolling to the top or to
@@ -655,12 +643,6 @@ on the center of the screen.
.BI "\-\-jump\-labels=" "CHARS" .BI "\-\-jump\-labels=" "CHARS"
Label characters for \fBjump\fR mode. Label characters for \fBjump\fR mode.
.TP .TP
.BI "\-\-gutter=" "CHAR"
Character used for the gutter column (default: '▌' unless \fB\-\-no\-unicode\fR is given)
.TP
.BI "\-\-gutter\-raw=" "CHAR"
Character used for the gutter column in raw mode (default: '▖' unless \fB\-\-no\-unicode\fR is given)
.TP
.BI "\-\-pointer=" "STR" .BI "\-\-pointer=" "STR"
Pointer to the current line (default: '▌' or '>' depending on \fB\-\-no\-unicode\fR) Pointer to the current line (default: '▌' or '>' depending on \fB\-\-no\-unicode\fR)
.TP .TP
@@ -1143,25 +1125,19 @@ On Windows, the default value is \fBcmd /s/c\fR when \fB$SHELL\fR is not
set. set.
.TP .TP
.B "\-\-listen[=SOCKET_PATH|[ADDR:]PORT]" "\-\-listen\-unsafe[=[ADDR:]PORT]" .B "\-\-listen[=[ADDR:]PORT]" "\-\-listen\-unsafe[=[ADDR:]PORT]"
Start HTTP server and listen on the given address or Unix socket. It allows Start HTTP server and listen on the given address. It allows external processes
external processes to send actions to perform via POST method and query the to send actions to perform via POST method.
program state via GET method. For the argument to be recognized as a socket
path, it must have \fB.sock\fR extension.
- If the port number is omitted or given as 0, fzf will automatically choose - If the port number is omitted or given as 0, fzf will automatically choose
a port and export it as \fBFZF_PORT\fR environment variable to the child processes. a port and export it as \fBFZF_PORT\fR environment variable to the child processes
- If a Unix socket path is given, fzf will create a Unix domain socket at the
given path. The existing file will be removed. The path to the socket file
is exported as \fBFZF_SOCK\fR environment variable.
- If \fBFZF_API_KEY\fR environment variable is set, the server would require - If \fBFZF_API_KEY\fR environment variable is set, the server would require
sending an API key with the same value in the \fBx\-api\-key\fR HTTP header. sending an API key with the same value in the \fBx\-api\-key\fR HTTP header
- \fBFZF_API_KEY\fR is required for a non-localhost listen address. - \fBFZF_API_KEY\fR is required for a non-localhost listen address
- To allow remote process execution, use \fB\-\-listen\-unsafe\fR. - To allow remote process execution, use \fB\-\-listen\-unsafe\fR
e.g. e.g.
\fB# Start HTTP server on port 6266 \fB# Start HTTP server on port 6266
@@ -1200,18 +1176,6 @@ e.g.
' '
\fR \fR
Here is an example script that uses a Unix socket instead of a TCP port.
\fB
fzf --listen=/tmp/fzf.sock
# GET
curl --unix-socket /tmp/fzf.sock http
# POST
curl --unix-socket /tmp/fzf.sock http -d up
\fR
.SS DIRECTORY TRAVERSAL .SS DIRECTORY TRAVERSAL
.TP .TP
.B "\-\-walker=[file][,dir][,follow][,hidden]" .B "\-\-walker=[file][,dir][,follow][,hidden]"
@@ -1363,8 +1327,6 @@ fzf exports the following environment variables to its child processes.
.br .br
.BR FZF_COLUMNS " Number of columns fzf takes up excluding padding and margin" .BR FZF_COLUMNS " Number of columns fzf takes up excluding padding and margin"
.br .br
.BR FZF_DIRECTION " Direction of the list (\fBup\fR or \fBdown\fR)"
.br
.BR FZF_TOTAL_COUNT " Total number of items" .BR FZF_TOTAL_COUNT " Total number of items"
.br .br
.BR FZF_MATCH_COUNT " Number of matched items" .BR FZF_MATCH_COUNT " Number of matched items"
@@ -1401,8 +1363,6 @@ fzf exports the following environment variables to its child processes.
.br .br
.BR FZF_PORT " Port number when \-\-listen option is used" .BR FZF_PORT " Port number when \-\-listen option is used"
.br .br
.BR FZF_SOCK " Unix socket path when \-\-listen option is used"
.br
.BR FZF_PREVIEW_TOP " Top position of the preview window" .BR FZF_PREVIEW_TOP " Top position of the preview window"
.br .br
.BR FZF_PREVIEW_LEFT " Left position of the preview window" .BR FZF_PREVIEW_LEFT " Left position of the preview window"
@@ -1410,8 +1370,6 @@ fzf exports the following environment variables to its child processes.
.BR FZF_PREVIEW_LINES " Number of lines in the preview window" .BR FZF_PREVIEW_LINES " Number of lines in the preview window"
.br .br
.BR FZF_PREVIEW_COLUMNS " Number of columns in the preview window" .BR FZF_PREVIEW_COLUMNS " Number of columns in the preview window"
.br
.BR FZF_RAW " Only in raw mode. 1 if the current item matches, 0 otherwise"
.SH EXTENDED SEARCH MODE .SH EXTENDED SEARCH MODE
@@ -1856,7 +1814,6 @@ A key or an event can be bound to one or more of the following actions.
\fBbecome(...)\fR (replace fzf process with the specified command; see below for the details) \fBbecome(...)\fR (replace fzf process with the specified command; see below for the details)
\fBbeginning\-of\-line\fR \fIctrl\-a home\fR \fBbeginning\-of\-line\fR \fIctrl\-a home\fR
\fBbell\fR (ring the terminal bell) \fBbell\fR (ring the terminal bell)
\fBbest\fR (move to the best match; same as \fBfirst\fR if raw mode is disabled)
\fBbg\-cancel\fR (cancel background transform processes) \fBbg\-cancel\fR (cancel background transform processes)
\fBcancel\fR (clear query string if not empty, abort fzf otherwise) \fBcancel\fR (clear query string if not empty, abort fzf otherwise)
\fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string) \fBchange\-border\-label(...)\fR (change \fB\-\-border\-label\fR to the given string)
@@ -1881,13 +1838,9 @@ A key or an event can be bound to one or more of the following actions.
\fBdelete\-char\fR \fIdel\fR \fBdelete\-char\fR \fIdel\fR
\fBdelete\-char/eof\fR \fIctrl\-d\fR (same as \fBdelete\-char\fR except aborts fzf if query is empty) \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; to also clear non-matching selections, use \fBclear\-multi\fR) \fBdeselect\-all\fR (deselect all matches; to also clear non-matched selections, use \fBclear\-multi\fR)
\fBdisable\-raw\fR (disable raw mode)
\fBdisable\-search\fR (disable search functionality) \fBdisable\-search\fR (disable search functionality)
\fBdown\fR \fIctrl\-j down\fR \fBdown\fR \fIctrl\-j ctrl\-n down\fR
\fBdown\-match\fR \fIctrl\-n\fR \fIalt\-down\fR (move to the match below the cursor)
\fBdown\-selected\fR (move to the selected item below the cursor)
\fBenable\-raw\fR (enable raw mode)
\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
\fBexclude\fR (exclude the current item from the result) \fBexclude\fR (exclude the current item from the result)
@@ -1905,7 +1858,7 @@ A key or an event can be bound to one or more of the following actions.
\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 (synonym to \fBdown\-selected\fR) \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
@@ -1918,7 +1871,7 @@ A key or an event can be bound to one or more of the following actions.
\fBoffset\-middle\fR (place the current item is in the middle of the screen) \fBoffset\-middle\fR (place the current item is in the middle of the screen)
\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 (synonym to \fBup\-selected\fR) \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
@@ -1953,7 +1906,6 @@ A key or an event can be bound to one or more of the following actions.
\fBtoggle\-multi\-line\fR \fBtoggle\-multi\-line\fR
\fBtoggle\-preview\fR \fBtoggle\-preview\fR
\fBtoggle\-preview\-wrap\fR \fBtoggle\-preview\-wrap\fR
\fBtoggle\-raw\fR (toggle raw mode for displaying non-matching items)
\fBtoggle\-search\fR (toggle search functionality) \fBtoggle\-search\fR (toggle search functionality)
\fBtoggle\-sort\fR \fBtoggle\-sort\fR
\fBtoggle\-track\fR (toggle global tracking option (\fB\-\-track\fR)) \fBtoggle\-track\fR (toggle global tracking option (\fB\-\-track\fR))
@@ -1980,9 +1932,7 @@ A key or an event can be bound to one or more of the following actions.
\fBunix\-line\-discard\fR \fIctrl\-u\fR \fBunix\-line\-discard\fR \fIctrl\-u\fR
\fBunix\-word\-rubout\fR \fIctrl\-w\fR \fBunix\-word\-rubout\fR \fIctrl\-w\fR
\fBuntrack\-current\fR (stop tracking the current item; no-op if global tracking is enabled) \fBuntrack\-current\fR (stop tracking the current item; no-op if global tracking is enabled)
\fBup\fR \fIctrl\-k up\fR \fBup\fR \fIctrl\-k ctrl\-p up\fR
\fBup\-match\fR \fIctrl\-p\fR \fIalt\-up\fR (move to the match above the cursor)
\fBup\-selected\fR (move to the selected item above the cursor)
\fByank\fR \fIctrl\-y\fR \fByank\fR \fIctrl\-y\fR
Each \fBtransform*\fR action has a corresponding \fBbg\-transform*\fR Each \fBtransform*\fR action has a corresponding \fBbg\-transform*\fR

View File

@@ -1,3 +1,4 @@
__fzf_defaults() { __fzf_defaults() {
# $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS # $1: Prepend to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS # $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
@@ -21,15 +22,12 @@ __fzf_exec_awk() {
# modern point of view. To use a standard-conforming version in Solaris, # modern point of view. To use a standard-conforming version in Solaris,
# one needs to explicitly use /usr/xpg4/bin/awk. # one needs to explicitly use /usr/xpg4/bin/awk.
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk >/dev/null 2>&1; then
# choose the faster mawk if: it's installed && build date >= 20230322 && # choose the faster mawk if: it's installed && build date >= 20230322 &&
# version >= 1.3.4 # version >= 1.3.4
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
# Note: macOS awk has a quirk that it stops processing at all when it sees # Note: macOS awk has a quirk that it stops processing at all when it sees

View File

@@ -31,10 +31,9 @@ if [[ $- =~ i ]]; then
########################################################### ###########################################################
#----BEGIN shfmt
#----BEGIN INCLUDE common.sh #----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh". # NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply # To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details. # the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() { __fzf_defaults() {
@@ -48,13 +47,10 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"
@@ -62,9 +58,9 @@ __fzf_exec_awk() {
#----END INCLUDE #----END INCLUDE
__fzf_comprun() { __fzf_comprun() {
if [[ "$(type -t _fzf_comprun 2>&1)" == function ]]; then if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
_fzf_comprun "$@" _fzf_comprun "$@"
elif [[ -n ${TMUX_PANE-} ]] && { [[ ${FZF_TMUX:-0} != 0 ]] || [[ -n ${FZF_TMUX_OPTS-} ]]; }; then elif [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; }; then
shift shift
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@" fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
else else
@@ -76,13 +72,13 @@ __fzf_comprun() {
__fzf_orig_completion() { __fzf_orig_completion() {
local l comp f cmd local l comp f cmd
while read -r l; do while read -r l; do
if [[ $l =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then if [[ "$l" =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then
comp="${BASH_REMATCH[1]}" comp="${BASH_REMATCH[1]}"
f="${BASH_REMATCH[2]}" f="${BASH_REMATCH[2]}"
cmd="${BASH_REMATCH[3]}" cmd="${BASH_REMATCH[3]}"
[[ $f == _fzf_* ]] && continue [[ "$f" = _fzf_* ]] && continue
printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}" printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
if [[ $l == *" -o nospace "* ]] && [[ ${__fzf_nospace_commands-} != *" $cmd "* ]]; then if [[ "$l" = *" -o nospace "* ]] && [[ ! "${__fzf_nospace_commands-}" = *" $cmd "* ]]; then
__fzf_nospace_commands="${__fzf_nospace_commands-} $cmd " __fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
fi fi
fi fi
@@ -118,7 +114,7 @@ _fzf_opts_completion() {
local cur prev opts local cur prev opts
COMPREPLY=() COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD - 1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
opts=" opts="
+c --no-color +c --no-color
+i --no-ignore-case +i --no-ignore-case
@@ -201,28 +197,28 @@ _fzf_opts_completion() {
--" --"
case "${prev}" in case "${prev}" in
--scheme) --scheme)
COMPREPLY=($(compgen -W "default path history" -- "$cur")) COMPREPLY=( $(compgen -W "default path history" -- "$cur") )
return 0 return 0
;; ;;
--tiebreak) --tiebreak)
COMPREPLY=($(compgen -W "length chunk begin end index" -- "$cur")) COMPREPLY=( $(compgen -W "length chunk begin end index" -- "$cur") )
return 0 return 0
;; ;;
--color) --color)
COMPREPLY=($(compgen -W "dark light 16 bw no" -- "$cur")) COMPREPLY=( $(compgen -W "dark light 16 bw no" -- "$cur") )
return 0 return 0
;; ;;
--layout) --layout)
COMPREPLY=($(compgen -W "default reverse reverse-list" -- "$cur")) COMPREPLY=( $(compgen -W "default reverse reverse-list" -- "$cur") )
return 0 return 0
;; ;;
--info) --info)
COMPREPLY=($(compgen -W "default right hidden inline inline-right" -- "$cur")) COMPREPLY=( $(compgen -W "default right hidden inline inline-right" -- "$cur") )
return 0 return 0
;; ;;
--preview-window) --preview-window)
COMPREPLY=($(compgen -W " COMPREPLY=( $(compgen -W "
default default
hidden hidden
nohidden nohidden
@@ -248,21 +244,21 @@ _fzf_opts_completion() {
border-left border-left
border-right border-right
follow follow
nofollow" -- "$cur")) nofollow" -- "$cur") )
return 0 return 0
;; ;;
--border) --border)
COMPREPLY=($(compgen -W "rounded sharp bold block thinblock double horizontal vertical top bottom left right none" -- "$cur")) COMPREPLY=( $(compgen -W "rounded sharp bold block thinblock double horizontal vertical top bottom left right none" -- "$cur") )
return 0 return 0
;; ;;
--border-label-pos | --preview-label-pos) --border-label-pos|--preview-label-pos)
COMPREPLY=($(compgen -W "center bottom top" -- "$cur")) COMPREPLY=( $(compgen -W "center bottom top" -- "$cur") )
return 0 return 0
;; ;;
esac esac
if [[ $cur =~ ^-|\+ ]]; then if [[ "$cur" =~ ^-|\+ ]]; then
COMPREPLY=($(compgen -W "${opts}" -- "$cur")) COMPREPLY=( $(compgen -W "${opts}" -- "$cur") )
return 0 return 0
fi fi
@@ -276,7 +272,7 @@ _fzf_handle_dynamic_completion() {
orig_cmd="$1" orig_cmd="$1"
if __fzf_orig_completion_get_orig_func "$cmd"; then if __fzf_orig_completion_get_orig_func "$cmd"; then
"$REPLY" "$@" "$REPLY" "$@"
elif [[ -n ${_fzf_completion_loader-} ]]; then elif [[ -n "${_fzf_completion_loader-}" ]]; then
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null) orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
$_fzf_completion_loader "$@" $_fzf_completion_loader "$@"
ret=$? ret=$?
@@ -290,7 +286,7 @@ _fzf_handle_dynamic_completion() {
__fzf_orig_completion_instantiate "$cmd" "${BASH_REMATCH[1]}" && __fzf_orig_completion_instantiate "$cmd" "${BASH_REMATCH[1]}" &&
orig_complete=$REPLY orig_complete=$REPLY
if [[ ${__fzf_nospace_commands-} == *" $orig_cmd "* ]]; then if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }" eval "${orig_complete/ -F / -o nospace -F }"
else else
eval "$orig_complete" eval "$orig_complete"
@@ -310,18 +306,18 @@ __fzf_generic_path_completion() {
COMPREPLY=() COMPREPLY=()
trigger=${FZF_COMPLETION_TRIGGER-'**'} trigger=${FZF_COMPLETION_TRIGGER-'**'}
[[ $COMP_CWORD -ge 0 ]] && cur="${COMP_WORDS[COMP_CWORD]}" [[ $COMP_CWORD -ge 0 ]] && cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $cur == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then if [[ "$cur" == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
base=${cur:0:${#cur}-${#trigger}} base=${cur:0:${#cur}-${#trigger}}
eval "base=$base" 2> /dev/null || return eval "base=$base" 2> /dev/null || return
dir= dir=
[[ $base == *"/"* ]] && dir="$base" [[ $base = *"/"* ]] && dir="$base"
while true; do while true; do
if [[ -z $dir ]] || [[ -d $dir ]]; then if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
leftover=${base/#"$dir"/} leftover=${base/#"$dir"}
leftover=${leftover/#\//} leftover=${leftover/#\/}
[[ -z $dir ]] && dir='.' [[ -z "$dir" ]] && dir='.'
[[ $dir != "/" ]] && dir="${dir/%\//}" [[ "$dir" != "/" ]] && dir="${dir/%\//}"
matches=$( matches=$(
export FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-} $2") export FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-} $2")
unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
@@ -341,11 +337,11 @@ __fzf_generic_path_completion() {
done done
) )
matches=${matches% } matches=${matches% }
[[ -z $3 ]] && [[ ${__fzf_nospace_commands-} == *" ${COMP_WORDS[0]} "* ]] && matches="$matches " [[ -z "$3" ]] && [[ "${__fzf_nospace_commands-}" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
if [[ -n $matches ]]; then if [[ -n "$matches" ]]; then
COMPREPLY=("$matches") COMPREPLY=( "$matches" )
else else
COMPREPLY=("$cur") COMPREPLY=( "$cur" )
fi fi
# To redraw line after fzf closes (printf '\e[5n') # To redraw line after fzf closes (printf '\e[5n')
bind '"\e[0n": redraw-current-line' 2> /dev/null bind '"\e[0n": redraw-current-line' 2> /dev/null
@@ -353,7 +349,7 @@ __fzf_generic_path_completion() {
return 0 return 0
fi fi
dir=$(command dirname "$dir") dir=$(command dirname "$dir")
[[ $dir =~ /$ ]] || dir="$dir"/ [[ "$dir" =~ /$ ]] || dir="$dir"/
done done
else else
shift shift
@@ -369,15 +365,15 @@ _fzf_complete() {
args=("$@") args=("$@")
sep= sep=
for i in "${!args[@]}"; do for i in "${!args[@]}"; do
if [[ ${args[$i]} == -- ]]; then if [[ "${args[$i]}" = -- ]]; then
sep=$i sep=$i
break break
fi fi
done done
if [[ -n $sep ]]; then if [[ -n "$sep" ]]; then
str_arg= str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}") rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:sep}") args=("${args[@]:0:$sep}")
else else
str_arg=$1 str_arg=$1
args=() args=()
@@ -392,16 +388,15 @@ _fzf_complete() {
trigger=${FZF_COMPLETION_TRIGGER-'**'} trigger=${FZF_COMPLETION_TRIGGER-'**'}
cmd="${COMP_WORDS[0]}" cmd="${COMP_WORDS[0]}"
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $cur == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then if [[ "$cur" == *"$trigger" ]] && [[ $cur != *'$('* ]] && [[ $cur != *':='* ]] && [[ $cur != *'`'* ]]; then
cur=${cur:0:${#cur}-${#trigger}} cur=${cur:0:${#cur}-${#trigger}}
selected=$( selected=$(
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
FZF_DEFAULT_OPTS_FILE='' \ FZF_DEFAULT_OPTS_FILE='' \
__fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | eval "$post" | command tr '\n' ' ' __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | eval "$post" | command tr '\n' ' ')
)
selected=${selected% } # Strip trailing space not to repeat "-o nospace" selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [[ -n $selected ]]; then if [[ -n "$selected" ]]; then
COMPREPLY=("$selected") COMPREPLY=("$selected")
else else
COMPREPLY=("$cur") COMPREPLY=("$cur")
@@ -459,10 +454,10 @@ _fzf_proc_completion() {
' '
_fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \ _fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \
--bind "click-header:transform:$transformer" -- "$@" < <( --bind "click-header:transform:$transformer" -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2> /dev/null || command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox
command ps --everyone --full --windows # For cygwin command ps --everyone --full --windows # For cygwin
) )
} }
_fzf_proc_completion_post() { _fzf_proc_completion_post() {
@@ -545,12 +540,12 @@ _fzf_host_completion() {
# > and the third argument ($3) is the word preceding the word being completed on the current command line. # > and the third argument ($3) is the word preceding the word being completed on the current command line.
_fzf_complete_ssh() { _fzf_complete_ssh() {
case $3 in case $3 in
-i | -F | -E) -i|-F|-E)
_fzf_path_completion "$@" _fzf_path_completion "$@"
;; ;;
*) *)
local user= local user=
[[ $2 =~ '@' ]] && user="${2%%@*}@" [[ "$2" =~ '@' ]] && user="${2%%@*}@"
_fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}') _fzf_complete +m -- "$@" < <(__fzf_list_hosts | __fzf_exec_awk -v user="$user" '{print user $0}')
;; ;;
esac esac
@@ -681,13 +676,12 @@ _fzf_setup_completion() {
__fzf_orig_completion < <(complete -p "$@" 2> /dev/null) __fzf_orig_completion < <(complete -p "$@" 2> /dev/null)
for cmd in "$@"; do for cmd in "$@"; do
case "$kind" in case "$kind" in
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;; dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;; var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
alias) __fzf_defc "$cmd" "$fn" "-a" ;; alias) __fzf_defc "$cmd" "$fn" "-a" ;;
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;; *) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
esac esac
done done
} }
#----END shfmt
fi fi

View File

@@ -98,7 +98,7 @@ if [[ -o interactive ]]; then
#----BEGIN INCLUDE common.sh #----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh". # NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply # To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details. # the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() { __fzf_defaults() {
@@ -112,13 +112,10 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"

View File

@@ -7,7 +7,6 @@
# - $FZF_TMUX_OPTS # - $FZF_TMUX_OPTS
# - $FZF_CTRL_T_COMMAND # - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS # - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS # - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND # - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS # - $FZF_ALT_C_OPTS
@@ -18,10 +17,9 @@ if [[ $- =~ i ]]; then
# Key bindings # Key bindings
# ------------ # ------------
#----BEGIN shfmt
#----BEGIN INCLUDE common.sh #----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh". # NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply # To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details. # the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() { __fzf_defaults() {
@@ -35,13 +33,10 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"
@@ -50,30 +45,30 @@ __fzf_exec_awk() {
__fzf_select__() { __fzf_select__() {
FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \ FZF_DEFAULT_COMMAND=${FZF_CTRL_T_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path" "${FZF_CTRL_T_OPTS-} -m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) "$@" | FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) "$@" |
while read -r item; do while read -r item; do
printf '%q ' "$item" # escape special chars printf '%q ' "$item" # escape special chars
done done
} }
__fzfcmd() { __fzfcmd() {
[[ -n ${TMUX_PANE-} ]] && { [[ ${FZF_TMUX:-0} != 0 ]] || [[ -n ${FZF_TMUX_OPTS-} ]]; } && [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } &&
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf" echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
} }
fzf-file-widget() { fzf-file-widget() {
local selected="$(__fzf_select__ "$@")" local selected="$(__fzf_select__ "$@")"
READLINE_LINE="${READLINE_LINE:0:READLINE_POINT}$selected${READLINE_LINE:READLINE_POINT}" READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#selected})) READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
} }
__fzf_cd__() { __fzf_cd__() {
local dir local dir
dir=$( dir=$(
FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \ FZF_DEFAULT_COMMAND=${FZF_ALT_C_COMMAND:-} \
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path" "${FZF_ALT_C_OPTS-} +m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd)
) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)" ) && printf 'builtin cd -- %q' "$(builtin unset CDPATH && builtin cd -- "$dir" && builtin pwd)"
} }
@@ -85,11 +80,11 @@ if command -v perl > /dev/null; then
set +o pipefail set +o pipefail
builtin fc -lnr -2147483648 | builtin fc -lnr -2147483648 |
last_hist=$(HISTTIMEFORMAT='' builtin history 1) command perl -n -l0 -e "$script" | last_hist=$(HISTTIMEFORMAT='' builtin history 1) command perl -n -l0 -e "$script" |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE" FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
) || return ) || return
READLINE_LINE=$(command perl -pe 's/^\d*\t//' <<< "$output") READLINE_LINE=$(command perl -pe 's/^\d*\t//' <<< "$output")
if [[ -z $READLINE_POINT ]]; then if [[ -z "$READLINE_POINT" ]]; then
echo "$READLINE_LINE" echo "$READLINE_LINE"
else else
READLINE_POINT=0x7fffffff READLINE_POINT=0x7fffffff
@@ -98,7 +93,7 @@ if command -v perl > /dev/null; then
else # awk - fallback for POSIX systems else # awk - fallback for POSIX systems
__fzf_history__() { __fzf_history__() {
local output script local output script
[[ $(HISTTIMEFORMAT='' builtin history 1) =~ [[:digit:]]+ ]] # how many history entries [[ $(HISTTIMEFORMAT='' builtin history 1) =~ [[:digit:]]+ ]] # how many history entries
script='function P(b) { ++n; sub(/^[ *]/, "", b); if (!seen[b]++) { printf "%d\t%s%c", '$((BASH_REMATCH + 1))' - n, b, 0 } } script='function P(b) { ++n; sub(/^[ *]/, "", b); if (!seen[b]++) { printf "%d\t%s%c", '$((BASH_REMATCH + 1))' - n, b, 0 } }
NR==1 { b = substr($0, 2); next } NR==1 { b = substr($0, 2); next }
/^\t/ { P(b); b = substr($0, 2); next } /^\t/ { P(b); b = substr($0, 2); next }
@@ -106,13 +101,13 @@ else # awk - fallback for POSIX systems
END { if (NR) P(b) }' END { if (NR) P(b) }'
output=$( output=$(
set +o pipefail set +o pipefail
builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '<lines>$'\n' )* ; <lines> ::= [^\n]* ( $'\n'<lines> )* builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '<lines>$'\n' )* ; <lines> ::= [^\n]* ( $'\n'<lines> )*
__fzf_exec_awk "$script" | # ( <counter>$'\t'<lines>$'\000' )* __fzf_exec_awk "$script" | # ( <counter>$'\t'<lines>$'\000' )*
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"$'\t'"↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} +m --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE" FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE"
) || return ) || return
READLINE_LINE=${output#*$'\t'} READLINE_LINE=${output#*$'\t'}
if [[ -z $READLINE_POINT ]]; then if [[ -z "$READLINE_POINT" ]]; then
echo "$READLINE_LINE" echo "$READLINE_LINE"
else else
READLINE_POINT=0x7fffffff READLINE_POINT=0x7fffffff
@@ -127,48 +122,37 @@ bind -m vi-command '"\C-z": emacs-editing-mode'
bind -m vi-insert '"\C-z": emacs-editing-mode' bind -m vi-insert '"\C-z": emacs-editing-mode'
bind -m emacs-standard '"\C-z": vi-editing-mode' bind -m emacs-standard '"\C-z": vi-editing-mode'
if ((BASH_VERSINFO[0] < 4)); then if (( BASH_VERSINFO[0] < 4 )); then
# CTRL-T - Paste the selected file path into the command line # CTRL-T - Paste the selected file path into the command line
if [[ ${FZF_CTRL_T_COMMAND-x} != "" ]]; then if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f\C-y\ey\C-_"' bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f\C-y\ey\C-_"'
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"' bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"' bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
fi fi
# CTRL-R - Paste the selected command from history into the command line # CTRL-R - Paste the selected command from history into the command line
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2 bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
fi
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u`__fzf_history__`\e\C-e\er"'
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
fi
else else
# CTRL-T - Paste the selected file path into the command line # CTRL-T - Paste the selected file path into the command line
if [[ ${FZF_CTRL_T_COMMAND-x} != "" ]]; then if [[ "${FZF_CTRL_T_COMMAND-x}" != "" ]]; then
bind -m emacs-standard -x '"\C-t": fzf-file-widget' bind -m emacs-standard -x '"\C-t": fzf-file-widget'
bind -m vi-command -x '"\C-t": fzf-file-widget' bind -m vi-command -x '"\C-t": fzf-file-widget'
bind -m vi-insert -x '"\C-t": fzf-file-widget' bind -m vi-insert -x '"\C-t": fzf-file-widget'
fi fi
# CTRL-R - Paste the selected command from history into the command line # CTRL-R - Paste the selected command from history into the command line
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then bind -m emacs-standard -x '"\C-r": __fzf_history__'
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then bind -m vi-command -x '"\C-r": __fzf_history__'
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2 bind -m vi-insert -x '"\C-r": __fzf_history__'
fi
bind -m emacs-standard -x '"\C-r": __fzf_history__'
bind -m vi-command -x '"\C-r": __fzf_history__'
bind -m vi-insert -x '"\C-r": __fzf_history__'
fi
fi fi
# ALT-C - cd into the selected directory # ALT-C - cd into the selected directory
if [[ ${FZF_ALT_C_COMMAND-x} != "" ]]; then if [[ "${FZF_ALT_C_COMMAND-x}" != "" ]]; then
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d\C-y\ey\C-_"' bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d\C-y\ey\C-_"'
bind -m vi-command '"\ec": "\C-z\ec\C-z"' bind -m vi-command '"\ec": "\C-z\ec\C-z"'
bind -m vi-insert '"\ec": "\C-z\ec\C-z"' bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
fi fi
#----END shfmt
fi fi

View File

@@ -7,7 +7,6 @@
# - $FZF_TMUX_OPTS # - $FZF_TMUX_OPTS
# - $FZF_CTRL_T_COMMAND # - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS # - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS # - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND # - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS # - $FZF_ALT_C_OPTS
@@ -160,7 +159,7 @@ function fzf_key_bindings
set -lx FZF_DEFAULT_OPTS (__fzf_defaults '' \ set -lx FZF_DEFAULT_OPTS (__fzf_defaults '' \
'--nth=2..,.. --scheme=history --multi --wrap-sign="\t↳ "' \ '--nth=2..,.. --scheme=history --multi --wrap-sign="\t↳ "' \
'--bind=\'shift-delete:execute-silent(eval history delete --exact --case-sensitive -- (string escape -n -- {+} | string replace -r -a "^\d*\\\\\\t|(?<=\\\\\\n)\\\\\\t" ""))+reload(eval $FZF_DEFAULT_COMMAND)\'' \ '--bind=\'shift-delete:execute-silent(eval history delete --exact --case-sensitive -- (string escape -n -- {+} | string replace -r -a "^\d*\\\\\\t|(?<=\\\\\\n)\\\\\\t" ""))+reload(eval $FZF_DEFAULT_COMMAND)\'' \
"--bind=ctrl-r:toggle-sort,alt-r:toggle-raw --highlight-line $FZF_CTRL_R_OPTS" \ "--bind=ctrl-r:toggle-sort --highlight-line $FZF_CTRL_R_OPTS" \
'--accept-nth=2.. --read0 --print0 --with-shell='(status fish-path)\\ -c) '--accept-nth=2.. --read0 --print0 --with-shell='(status fish-path)\\ -c)
set -lx FZF_DEFAULT_OPTS_FILE set -lx FZF_DEFAULT_OPTS_FILE
@@ -215,13 +214,8 @@ function fzf_key_bindings
commandline -f repaint commandline -f repaint
end end
if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND" bind \cr fzf-history-widget
if test -n "$FZF_CTRL_R_COMMAND" bind -M insert \cr fzf-history-widget
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2
end
bind \cr fzf-history-widget
bind -M insert \cr fzf-history-widget
end
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND" if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
bind \ct fzf-file-widget bind \ct fzf-file-widget

View File

@@ -7,7 +7,6 @@
# - $FZF_TMUX_OPTS # - $FZF_TMUX_OPTS
# - $FZF_CTRL_T_COMMAND # - $FZF_CTRL_T_COMMAND
# - $FZF_CTRL_T_OPTS # - $FZF_CTRL_T_OPTS
# - $FZF_CTRL_R_COMMAND
# - $FZF_CTRL_R_OPTS # - $FZF_CTRL_R_OPTS
# - $FZF_ALT_C_COMMAND # - $FZF_ALT_C_COMMAND
# - $FZF_ALT_C_OPTS # - $FZF_ALT_C_OPTS
@@ -41,7 +40,7 @@ if [[ -o interactive ]]; then
#----BEGIN INCLUDE common.sh #----BEGIN INCLUDE common.sh
# NOTE: Do not directly edit this section, which is copied from "common.sh". # NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply # To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details. # the changes. See code comments in "common.sh" for the implementation details.
__fzf_defaults() { __fzf_defaults() {
@@ -55,13 +54,10 @@ __fzf_exec_awk() {
__fzf_awk=awk __fzf_awk=awk
if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then if [[ $OSTYPE == solaris* && -x /usr/xpg4/bin/awk ]]; then
__fzf_awk=/usr/xpg4/bin/awk __fzf_awk=/usr/xpg4/bin/awk
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk >/dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && [[ $n == mawk ]] && (( d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004 )) && __fzf_awk=mawk
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"
@@ -136,11 +132,11 @@ fzf-history-widget() {
if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${+commands[perl]} )); then if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${+commands[perl]} )); then
selected="$(printf '%s\t%s\000' "${(kv)history[@]}" | selected="$(printf '%s\t%s\000' "${(kv)history[@]}" |
perl -0 -ne 'if (!$seen{(/^\s*[0-9]+\**\t(.*)/s, $1)}++) { s/\n/\n\t/g; print; }' | perl -0 -ne 'if (!$seen{(/^\s*[0-9]+\**\t(.*)/s, $1)}++) { s/\n/\n\t/g; print; }' |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m --read0") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))" FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
else else
selected="$(fc -rl 1 | __fzf_exec_awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' | selected="$(fc -rl 1 | __fzf_exec_awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \ FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '\t↳ ' --highlight-line ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m") \
FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))" FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd))"
fi fi
local ret=$? local ret=$?
@@ -154,15 +150,10 @@ fzf-history-widget() {
zle reset-prompt zle reset-prompt
return $ret return $ret
} }
if [[ ${FZF_CTRL_R_COMMAND-x} != "" ]]; then zle -N fzf-history-widget
if [[ -n ${FZF_CTRL_R_COMMAND-} ]]; then bindkey -M emacs '^R' fzf-history-widget
echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2 bindkey -M vicmd '^R' fzf-history-widget
fi bindkey -M viins '^R' fzf-history-widget
zle -N fzf-history-widget
bindkey -M emacs '^R' fzf-history-widget
bindkey -M vicmd '^R' fzf-history-widget
bindkey -M viins '^R' fzf-history-widget
fi
fi fi
} always { } always {

31
shell/update-common.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/sh
# This script applies the contents of "common.sh" to the other files.
set -e
# Go to the directory that contains this script
dir=${0%"${0##*/}"}
if [ -n "$dir" ]; then
cd "$dir"
fi
update() {
{
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
cat <<EOF
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update-common.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
EOF
grep -v '^[[:blank:]]*#' common.sh # remove code comments in common.sh
sed -n '/^#----END INCLUDE/,$p' "$1"
} > "$1.part"
mv -f "$1.part" "$1"
}
update completion.bash
update completion.zsh
update key-bindings.bash
update key-bindings.zsh

View File

@@ -1,68 +0,0 @@
#!/usr/bin/env bash
# This script applies the contents of "common.sh" to the other files.
set -e
dir=${0%"${0##*/}"}
update() {
{
sed -n '1,/^#----BEGIN INCLUDE common\.sh/p' "$1"
cat << EOF
# NOTE: Do not directly edit this section, which is copied from "common.sh".
# To modify it, one can edit "common.sh" and run "./update.sh" to apply
# the changes. See code comments in "common.sh" for the implementation details.
EOF
echo
grep -v '^[[:blank:]]*#' "$dir/common.sh" # remove code comments in common.sh
sed -n '/^#----END INCLUDE/,$p' "$1"
} > "$1.part"
mv -f "$1.part" "$1"
}
update "$dir/completion.bash"
update "$dir/completion.zsh"
update "$dir/key-bindings.bash"
update "$dir/key-bindings.zsh"
# Check if --check is in ARGV
check=0
rest=()
for arg in "$@"; do
case $arg in
--check) check=1 ;;
*) rest+=("$arg") ;;
esac
done
fmt() {
if ! grep -q "^#----BEGIN shfmt" "$1"; then
if [[ $check == 1 ]]; then
shfmt -d "$1"
return $?
else
shfmt -w "$1"
fi
else
{
sed -n '1,/^#----BEGIN shfmt/p' "$1" | sed '$d'
sed -n '/^#----BEGIN shfmt/,/^#----END shfmt/p' "$1" | shfmt --filename "$1"
sed -n '/^#----END shfmt/,$p' "$1" | sed '1d'
} > "$1.part"
if [[ $check == 1 ]]; then
diff -q "$1" "$1.part"
ret=$?
rm -f "$1.part"
return $ret
fi
mv -f "$1.part" "$1"
fi
}
for file in "${rest[@]}"; do
fmt "$file" || exit $?
done

View File

@@ -77,112 +77,106 @@ func _() {
_ = x[actToggleWrap-66] _ = x[actToggleWrap-66]
_ = x[actToggleMultiLine-67] _ = x[actToggleMultiLine-67]
_ = x[actToggleHscroll-68] _ = x[actToggleHscroll-68]
_ = x[actToggleRaw-69] _ = x[actTrackCurrent-69]
_ = x[actEnableRaw-70] _ = x[actToggleInput-70]
_ = x[actDisableRaw-71] _ = x[actHideInput-71]
_ = x[actTrackCurrent-72] _ = x[actShowInput-72]
_ = x[actToggleInput-73] _ = x[actUntrackCurrent-73]
_ = x[actHideInput-74] _ = x[actDown-74]
_ = x[actShowInput-75] _ = x[actUp-75]
_ = x[actUntrackCurrent-76] _ = x[actPageUp-76]
_ = x[actDown-77] _ = x[actPageDown-77]
_ = x[actDownMatch-78] _ = x[actPosition-78]
_ = x[actUp-79] _ = x[actHalfPageUp-79]
_ = x[actUpMatch-80] _ = x[actHalfPageDown-80]
_ = x[actPageUp-81] _ = x[actOffsetUp-81]
_ = x[actPageDown-82] _ = x[actOffsetDown-82]
_ = x[actPosition-83] _ = x[actOffsetMiddle-83]
_ = x[actHalfPageUp-84] _ = x[actJump-84]
_ = x[actHalfPageDown-85] _ = x[actJumpAccept-85]
_ = x[actOffsetUp-86] _ = x[actPrintQuery-86]
_ = x[actOffsetDown-87] _ = x[actRefreshPreview-87]
_ = x[actOffsetMiddle-88] _ = x[actReplaceQuery-88]
_ = x[actJump-89] _ = x[actToggleSort-89]
_ = x[actJumpAccept-90] _ = x[actShowPreview-90]
_ = x[actPrintQuery-91] _ = x[actHidePreview-91]
_ = x[actRefreshPreview-92] _ = x[actTogglePreview-92]
_ = x[actReplaceQuery-93] _ = x[actTogglePreviewWrap-93]
_ = x[actToggleSort-94] _ = x[actTransform-94]
_ = x[actShowPreview-95] _ = x[actTransformBorderLabel-95]
_ = x[actHidePreview-96] _ = x[actTransformGhost-96]
_ = x[actTogglePreview-97] _ = x[actTransformHeader-97]
_ = x[actTogglePreviewWrap-98] _ = x[actTransformFooter-98]
_ = x[actTransform-99] _ = x[actTransformHeaderLabel-99]
_ = x[actTransformBorderLabel-100] _ = x[actTransformFooterLabel-100]
_ = x[actTransformGhost-101] _ = x[actTransformInputLabel-101]
_ = x[actTransformHeader-102] _ = x[actTransformListLabel-102]
_ = x[actTransformFooter-103] _ = x[actTransformNth-103]
_ = x[actTransformHeaderLabel-104] _ = x[actTransformPointer-104]
_ = x[actTransformFooterLabel-105] _ = x[actTransformPreviewLabel-105]
_ = x[actTransformInputLabel-106] _ = x[actTransformPrompt-106]
_ = x[actTransformListLabel-107] _ = x[actTransformQuery-107]
_ = x[actTransformNth-108] _ = x[actTransformSearch-108]
_ = x[actTransformPointer-109] _ = x[actTrigger-109]
_ = x[actTransformPreviewLabel-110] _ = x[actBgTransform-110]
_ = x[actTransformPrompt-111] _ = x[actBgTransformBorderLabel-111]
_ = x[actTransformQuery-112] _ = x[actBgTransformGhost-112]
_ = x[actTransformSearch-113] _ = x[actBgTransformHeader-113]
_ = x[actTrigger-114] _ = x[actBgTransformFooter-114]
_ = x[actBgTransform-115] _ = x[actBgTransformHeaderLabel-115]
_ = x[actBgTransformBorderLabel-116] _ = x[actBgTransformFooterLabel-116]
_ = x[actBgTransformGhost-117] _ = x[actBgTransformInputLabel-117]
_ = x[actBgTransformHeader-118] _ = x[actBgTransformListLabel-118]
_ = x[actBgTransformFooter-119] _ = x[actBgTransformNth-119]
_ = x[actBgTransformHeaderLabel-120] _ = x[actBgTransformPointer-120]
_ = x[actBgTransformFooterLabel-121] _ = x[actBgTransformPreviewLabel-121]
_ = x[actBgTransformInputLabel-122] _ = x[actBgTransformPrompt-122]
_ = x[actBgTransformListLabel-123] _ = x[actBgTransformQuery-123]
_ = x[actBgTransformNth-124] _ = x[actBgTransformSearch-124]
_ = x[actBgTransformPointer-125] _ = x[actBgCancel-125]
_ = x[actBgTransformPreviewLabel-126] _ = x[actSearch-126]
_ = x[actBgTransformPrompt-127] _ = x[actPreview-127]
_ = x[actBgTransformQuery-128] _ = x[actPreviewTop-128]
_ = x[actBgTransformSearch-129] _ = x[actPreviewBottom-129]
_ = x[actBgCancel-130] _ = x[actPreviewUp-130]
_ = x[actSearch-131] _ = x[actPreviewDown-131]
_ = x[actPreview-132] _ = x[actPreviewPageUp-132]
_ = x[actPreviewTop-133] _ = x[actPreviewPageDown-133]
_ = x[actPreviewBottom-134] _ = x[actPreviewHalfPageUp-134]
_ = x[actPreviewUp-135] _ = x[actPreviewHalfPageDown-135]
_ = x[actPreviewDown-136] _ = x[actPrevHistory-136]
_ = x[actPreviewPageUp-137] _ = x[actPrevSelected-137]
_ = x[actPreviewPageDown-138] _ = x[actPrint-138]
_ = x[actPreviewHalfPageUp-139] _ = x[actPut-139]
_ = x[actPreviewHalfPageDown-140] _ = x[actNextHistory-140]
_ = x[actPrevHistory-141] _ = x[actNextSelected-141]
_ = x[actPrevSelected-142] _ = x[actExecute-142]
_ = x[actPrint-143] _ = x[actExecuteSilent-143]
_ = x[actPut-144] _ = x[actExecuteMulti-144]
_ = x[actNextHistory-145] _ = x[actSigStop-145]
_ = x[actNextSelected-146] _ = x[actFirst-146]
_ = x[actExecute-147] _ = x[actLast-147]
_ = x[actExecuteSilent-148] _ = x[actReload-148]
_ = x[actExecuteMulti-149] _ = x[actReloadSync-149]
_ = x[actSigStop-150] _ = x[actDisableSearch-150]
_ = x[actBest-151] _ = x[actEnableSearch-151]
_ = x[actFirst-152] _ = x[actSelect-152]
_ = x[actLast-153] _ = x[actDeselect-153]
_ = x[actReload-154] _ = x[actUnbind-154]
_ = x[actReloadSync-155] _ = x[actRebind-155]
_ = x[actDisableSearch-156] _ = x[actToggleBind-156]
_ = x[actEnableSearch-157] _ = x[actBecome-157]
_ = x[actSelect-158] _ = x[actShowHeader-158]
_ = x[actDeselect-159] _ = x[actHideHeader-159]
_ = x[actUnbind-160] _ = x[actBell-160]
_ = x[actRebind-161] _ = x[actExclude-161]
_ = x[actToggleBind-162] _ = x[actExcludeMulti-162]
_ = x[actBecome-163] _ = x[actAsync-163]
_ = x[actShowHeader-164]
_ = x[actHideHeader-165]
_ = x[actBell-166]
_ = x[actExclude-167]
_ = x[actExcludeMulti-168]
_ = x[actAsync-169]
} }
const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactToggleRawactEnableRawactDisableRawactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactDownMatchactUpactUpMatchactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactBestactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync" const _actionType_name = "actIgnoreactStartactClickactInvalidactBracketedPasteBeginactBracketedPasteEndactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactBackwardSubWordactCancelactChangeBorderLabelactChangeGhostactChangeHeaderactChangeFooteractChangeHeaderLabelactChangeFooterLabelactChangeInputLabelactChangeListLabelactChangeMultiactChangeNthactChangePointeractChangePreviewactChangePreviewLabelactChangePreviewWindowactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactForwardSubWordactKillLineactKillWordactKillSubWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactBackwardKillSubWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactToggleInputactHideInputactShowInputactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformGhostactTransformHeaderactTransformFooteractTransformHeaderLabelactTransformFooterLabelactTransformInputLabelactTransformListLabelactTransformNthactTransformPointeractTransformPreviewLabelactTransformPromptactTransformQueryactTransformSearchactTriggeractBgTransformactBgTransformBorderLabelactBgTransformGhostactBgTransformHeaderactBgTransformFooteractBgTransformHeaderLabelactBgTransformFooterLabelactBgTransformInputLabelactBgTransformListLabelactBgTransformNthactBgTransformPointeractBgTransformPreviewLabelactBgTransformPromptactBgTransformQueryactBgTransformSearchactBgCancelactSearchactPreviewactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactToggleBindactBecomeactShowHeaderactHideHeaderactBellactExcludeactExcludeMultiactAsync"
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 331, 351, 371, 390, 408, 422, 434, 450, 466, 487, 509, 524, 538, 552, 565, 582, 590, 603, 619, 631, 639, 653, 667, 684, 695, 706, 720, 738, 755, 762, 781, 803, 815, 829, 838, 853, 865, 878, 889, 900, 912, 926, 947, 962, 975, 993, 1009, 1021, 1033, 1046, 1061, 1075, 1087, 1099, 1116, 1123, 1135, 1140, 1150, 1159, 1170, 1181, 1194, 1209, 1220, 1233, 1248, 1255, 1268, 1281, 1298, 1313, 1326, 1340, 1354, 1370, 1390, 1402, 1425, 1442, 1460, 1478, 1501, 1524, 1546, 1567, 1582, 1601, 1625, 1643, 1660, 1678, 1688, 1702, 1727, 1746, 1766, 1786, 1811, 1836, 1860, 1883, 1900, 1921, 1947, 1967, 1986, 2006, 2017, 2026, 2036, 2049, 2065, 2077, 2091, 2107, 2125, 2145, 2167, 2181, 2196, 2204, 2210, 2224, 2239, 2249, 2265, 2280, 2290, 2297, 2305, 2312, 2321, 2334, 2350, 2365, 2374, 2385, 2394, 2403, 2416, 2425, 2438, 2451, 2458, 2468, 2483, 2491} var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 57, 77, 84, 92, 110, 118, 127, 144, 165, 180, 201, 225, 240, 258, 267, 287, 301, 316, 331, 351, 371, 390, 408, 422, 434, 450, 466, 487, 509, 524, 538, 552, 565, 582, 590, 603, 619, 631, 639, 653, 667, 684, 695, 706, 720, 738, 755, 762, 781, 803, 815, 829, 838, 853, 865, 878, 889, 900, 912, 926, 947, 962, 975, 993, 1009, 1024, 1038, 1050, 1062, 1079, 1086, 1091, 1100, 1111, 1122, 1135, 1150, 1161, 1174, 1189, 1196, 1209, 1222, 1239, 1254, 1267, 1281, 1295, 1311, 1331, 1343, 1366, 1383, 1401, 1419, 1442, 1465, 1487, 1508, 1523, 1542, 1566, 1584, 1601, 1619, 1629, 1643, 1668, 1687, 1707, 1727, 1752, 1777, 1801, 1824, 1841, 1862, 1888, 1908, 1927, 1947, 1958, 1967, 1977, 1990, 2006, 2018, 2032, 2048, 2066, 2086, 2108, 2122, 2137, 2145, 2151, 2165, 2180, 2190, 2206, 2221, 2231, 2239, 2246, 2255, 2268, 2284, 2299, 2308, 2319, 2328, 2337, 2350, 2359, 2372, 2385, 2392, 2402, 2417, 2425}
func (i actionType) String() string { func (i actionType) String() string {
if i < 0 || i >= actionType(len(_actionType_index)-1) { if i < 0 || i >= actionType(len(_actionType_index)-1) {

View File

@@ -2,7 +2,6 @@
package fzf package fzf
import ( import (
"maps"
"os" "os"
"sync" "sync"
"time" "time"
@@ -226,7 +225,10 @@ func Run(opts *Options) (int, error) {
} }
patternBuilder := func(runes []rune) *Pattern { patternBuilder := func(runes []rune) *Pattern {
denyMutex.Lock() denyMutex.Lock()
denylistCopy := maps.Clone(denylist) denylistCopy := make(map[int32]struct{})
for k, v := range denylist {
denylistCopy[k] = v
}
denyMutex.Unlock() denyMutex.Unlock()
return BuildPattern(cache, patternCache, return BuildPattern(cache, patternCache,
opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos, opts.Fuzzy, opts.FuzzyAlgo, opts.Extended, opts.Case, opts.Normalize, forward, withPos,
@@ -267,11 +269,11 @@ func Run(opts *Options) (int, error) {
// NOTE: Streaming filter is inherently not compatible with --tail // NOTE: Streaming filter is inherently not compatible with --tail
snapshot, _, _ := chunkList.Snapshot(opts.Tail) snapshot, _, _ := chunkList.Snapshot(opts.Tail)
result := matcher.scan(MatchRequest{ merger, _ := matcher.scan(MatchRequest{
chunks: snapshot, chunks: snapshot,
pattern: pattern}) pattern: pattern})
for i := 0; i < result.merger.Length(); i++ { for i := 0; i < merger.Length(); i++ {
opts.Printer(result.merger.Get(i).item.AsString(opts.Ansi)) opts.Printer(merger.Get(i).item.AsString(opts.Ansi))
found = true found = true
} }
} }
@@ -479,13 +481,12 @@ func Run(opts *Options) (int, error) {
case EvtSearchFin: case EvtSearchFin:
switch val := value.(type) { switch val := value.(type) {
case MatchResult: case *Merger:
merger := val.merger
if deferred { if deferred {
count := merger.Length() count := val.Length()
if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 { if opts.Select1 && count > 1 || opts.Exit0 && !opts.Select1 && count > 0 {
determine(merger.final) determine(val.final)
} else if merger.final { } else if val.final {
if opts.Exit0 && count == 0 || opts.Select1 && count == 1 { if opts.Exit0 && count == 0 || opts.Select1 && count == 1 {
if opts.PrintQuery { if opts.PrintQuery {
opts.Printer(opts.Query) opts.Printer(opts.Query)
@@ -503,7 +504,7 @@ func Run(opts *Options) (int, error) {
} }
} }
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
opts.Printer(transformer(merger.Get(i).item)) opts.Printer(transformer(val.Get(i).item))
} }
if count == 0 { if count == 0 {
exitCode = ExitNoMatch exitCode = ExitNoMatch
@@ -511,7 +512,7 @@ func Run(opts *Options) (int, error) {
stop = true stop = true
return return
} }
determine(merger.final) determine(val.final)
} }
} }
terminal.UpdateList(val) terminal.UpdateList(val)

View File

@@ -19,20 +19,6 @@ type MatchRequest struct {
revision revision revision revision
} }
type MatchResult struct {
merger *Merger
passMerger *Merger
cancelled bool
}
func (mr MatchResult) cacheable() bool {
return mr.merger != nil && mr.merger.cacheable()
}
func (mr MatchResult) final() bool {
return mr.merger != nil && mr.merger.final
}
// Matcher is responsible for performing search // Matcher is responsible for performing search
type Matcher struct { type Matcher struct {
cache *ChunkCache cache *ChunkCache
@@ -43,7 +29,7 @@ type Matcher struct {
reqBox *util.EventBox reqBox *util.EventBox
partitions int partitions int
slab []*util.Slab slab []*util.Slab
mergerCache map[string]MatchResult mergerCache map[string]*Merger
revision revision revision revision
} }
@@ -65,7 +51,7 @@ func NewMatcher(cache *ChunkCache, patternBuilder func([]rune) *Pattern,
reqBox: util.NewEventBox(), reqBox: util.NewEventBox(),
partitions: partitions, partitions: partitions,
slab: make([]*util.Slab, partitions), slab: make([]*util.Slab, partitions),
mergerCache: make(map[string]MatchResult), mergerCache: make(map[string]*Merger),
revision: revision} revision: revision}
} }
@@ -99,42 +85,43 @@ func (m *Matcher) Loop() {
cacheCleared := false cacheCleared := false
if request.sort != m.sort || request.revision != m.revision { if request.sort != m.sort || request.revision != m.revision {
m.sort = request.sort m.sort = request.sort
m.mergerCache = make(map[string]MatchResult) m.revision = request.revision
m.mergerCache = make(map[string]*Merger)
if !request.revision.compatible(m.revision) { if !request.revision.compatible(m.revision) {
m.cache.Clear() m.cache.Clear()
} }
m.revision = request.revision
cacheCleared = true cacheCleared = true
} }
// Restart search // Restart search
patternString := request.pattern.AsString() patternString := request.pattern.AsString()
var result MatchResult var merger *Merger
cancelled := false
count := CountItems(request.chunks) count := CountItems(request.chunks)
if !cacheCleared { if !cacheCleared {
if count == prevCount { if count == prevCount {
// Look up mergerCache // Look up mergerCache
if cached, found := m.mergerCache[patternString]; found && cached.final() == request.final { if cached, found := m.mergerCache[patternString]; found && cached.final == request.final {
result = cached merger = cached
} }
} else { } else {
// Invalidate mergerCache // Invalidate mergerCache
prevCount = count prevCount = count
m.mergerCache = make(map[string]MatchResult) m.mergerCache = make(map[string]*Merger)
} }
} }
if result.merger == nil { if merger == nil {
result = m.scan(request) merger, cancelled = m.scan(request)
} }
if !result.cancelled { if !cancelled {
if result.cacheable() { if merger.cacheable() {
m.mergerCache[patternString] = result m.mergerCache[patternString] = merger
} }
result.merger.final = request.final merger.final = request.final
m.eventBox.Set(EvtSearchFin, result) m.eventBox.Set(EvtSearchFin, merger)
} }
} }
} }
@@ -165,18 +152,16 @@ type partialResult struct {
matches []Result matches []Result
} }
func (m *Matcher) scan(request MatchRequest) MatchResult { func (m *Matcher) scan(request MatchRequest) (*Merger, bool) {
startedAt := time.Now() startedAt := time.Now()
numChunks := len(request.chunks) numChunks := len(request.chunks)
if numChunks == 0 { if numChunks == 0 {
m := EmptyMerger(request.revision) return EmptyMerger(request.revision), false
return MatchResult{m, m, false}
} }
pattern := request.pattern pattern := request.pattern
passMerger := PassMerger(&request.chunks, m.tac, request.revision)
if pattern.IsEmpty() { if pattern.IsEmpty() {
return MatchResult{passMerger, passMerger, false} return PassMerger(&request.chunks, m.tac, request.revision), false
} }
minIndex := request.chunks[0].items[0].Index() minIndex := request.chunks[0].items[0].Index()
@@ -239,7 +224,7 @@ func (m *Matcher) scan(request MatchRequest) MatchResult {
} }
if m.reqBox.Peek(reqReset) { if m.reqBox.Peek(reqReset) {
return MatchResult{nil, nil, wait()} return nil, wait()
} }
if time.Since(startedAt) > progressMinDuration { if time.Since(startedAt) > progressMinDuration {
@@ -252,8 +237,7 @@ func (m *Matcher) scan(request MatchRequest) MatchResult {
partialResult := <-resultChan partialResult := <-resultChan
partialResults[partialResult.index] = partialResult.matches partialResults[partialResult.index] = partialResult.matches
} }
merger := NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex) return NewMerger(pattern, partialResults, m.sort && request.pattern.sortable, m.tac, request.revision, minIndex, maxIndex), false
return MatchResult{merger, passMerger, false}
} }
// Reset is called to interrupt/signal the ongoing search // Reset is called to interrupt/signal the ongoing search

View File

@@ -141,15 +141,6 @@ func (mg *Merger) Get(idx int) Result {
panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count)) panic(fmt.Sprintf("Index out of bounds (unsorted, %d/%d)", idx, mg.count))
} }
func (mg *Merger) ToMap() map[int32]Result {
ret := make(map[int32]Result, mg.count)
for i := 0; i < mg.count; i++ {
result := mg.Get(i)
ret[result.Index()] = result
}
return ret
}
func (mg *Merger) cacheable() bool { func (mg *Merger) cacheable() bool {
return mg.count < mergerCacheMax return mg.count < mergerCacheMax
} }

View File

@@ -3,7 +3,6 @@ package fzf
import ( import (
"errors" "errors"
"fmt" "fmt"
"maps"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
@@ -98,14 +97,11 @@ Usage: fzf [options]
--wrap Enable line wrap --wrap Enable line wrap
--wrap-sign=STR Indicator for wrapped lines --wrap-sign=STR Indicator for wrapped lines
--no-multi-line Disable multi-line display of items when using --read0 --no-multi-line Disable multi-line display of items when using --read0
--raw Enable raw mode (show non-matching items)
--track Track the current selection when the result is updated --track Track the current selection when the result is updated
--tac Reverse the order of the input --tac Reverse the order of the input
--gap[=N] Render empty lines between each item --gap[=N] Render empty lines between each item
--gap-line[=STR] Draw horizontal line on each gap using the string --gap-line[=STR] Draw horizontal line on each gap using the string
(default: '┈' or '-') (default: '┈' or '-')
--freeze-left=N Number of fields to freeze on the left
--freeze-right=N Number of fields to freeze on the right
--keep-right Keep the right end of the line visible on overflow --keep-right Keep the right end of the line visible on overflow
--scroll-off=LINES Number of screen lines to keep above or below when --scroll-off=LINES Number of screen lines to keep above or below when
scrolling to the top or to the bottom (default: 0) scrolling to the top or to the bottom (default: 0)
@@ -113,8 +109,6 @@ Usage: fzf [options]
--hscroll-off=COLS Number of screen columns to keep to the right of the --hscroll-off=COLS Number of screen columns to keep to the right of the
highlighted substring (default: 10) highlighted substring (default: 10)
--jump-labels=CHARS Label characters for jump mode --jump-labels=CHARS Label characters for jump mode
--gutter=CHAR Character used for the gutter column (default: '▌')
--gutter-raw=CHAR Character used for the gutter column in raw mode (default: '▖')
--pointer=STR Pointer to the current line (default: '▌' or '>') --pointer=STR Pointer to the current line (default: '▌' or '>')
--marker=STR Multi-select marker (default: '┃' or '>') --marker=STR Multi-select marker (default: '┃' or '>')
--marker-multi-line=STR Multi-select marker for multi-line entries; --marker-multi-line=STR Multi-select marker for multi-line entries;
@@ -208,10 +202,8 @@ Usage: fzf [options]
ADVANCED ADVANCED
--with-shell=STR Shell command and flags to start child processes with --with-shell=STR Shell command and flags to start child processes with
--listen[=[ADDR:]PORT] Start HTTP server to receive actions via TCP --listen[=[ADDR:]PORT] Start HTTP server to receive actions (POST /)
(To allow remote process execution, use --listen-unsafe) (To allow remote process execution, use --listen-unsafe)
--listen=SOCKET_PATH Start HTTP server to receive actions via Unix domain socket
(Path should end with .sock)
DIRECTORY TRAVERSAL (Only used when $FZF_DEFAULT_COMMAND is not set) DIRECTORY TRAVERSAL (Only used when $FZF_DEFAULT_COMMAND is not set)
--walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden) --walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden)
@@ -220,8 +212,8 @@ Usage: fzf [options]
(default: .git,node_modules) (default: .git,node_modules)
HISTORY HISTORY
--history=FILE File to store fzf search history (*not* shell command history) --history=FILE History file
--history-size=N Maximum number of entries to keep in the file (default: 1000) --history-size=N Maximum number of history entries (default: 1000)
SHELL INTEGRATION SHELL INTEGRATION
--bash Print script to set up Bash shell integration --bash Print script to set up Bash shell integration
@@ -564,13 +556,10 @@ type Options struct {
Case Case Case Case
Normalize bool Normalize bool
Nth []Range Nth []Range
FreezeLeft int
FreezeRight int
WithNth func(Delimiter) func([]Token, int32) string WithNth func(Delimiter) func([]Token, int32) string
AcceptNth func(Delimiter) func([]Token, int32) string AcceptNth func(Delimiter) func([]Token, int32) string
Delimiter Delimiter Delimiter Delimiter
Sort int Sort int
Raw bool
Track trackOption Track trackOption
Tac bool Tac bool
Tail int Tail int
@@ -578,7 +567,6 @@ type Options struct {
Multi int Multi int
Ansi bool Ansi bool
Mouse bool Mouse bool
BaseTheme *tui.ColorTheme
Theme *tui.ColorTheme Theme *tui.ColorTheme
Black bool Black bool
Bold bool Bold bool
@@ -603,7 +591,6 @@ type Options struct {
JumpLabels string JumpLabels string
Prompt string Prompt string
Gutter *string Gutter *string
GutterRaw *string
Pointer *string Pointer *string
Marker *string Marker *string
MarkerMulti *[3]string MarkerMulti *[3]string
@@ -677,12 +664,11 @@ func defaultPreviewOpts(command string) previewOpts {
} }
func defaultOptions() *Options { func defaultOptions() *Options {
var theme, baseTheme *tui.ColorTheme var theme *tui.ColorTheme
if os.Getenv("NO_COLOR") != "" { if os.Getenv("NO_COLOR") != "" {
theme = tui.NoColorTheme theme = tui.NoColorTheme()
baseTheme = tui.NoColorTheme
} else { } else {
theme = tui.EmptyTheme theme = tui.EmptyTheme()
} }
return &Options{ return &Options{
@@ -708,7 +694,6 @@ func defaultOptions() *Options {
Ansi: false, Ansi: false,
Mouse: true, Mouse: true,
Theme: theme, Theme: theme,
BaseTheme: baseTheme,
Black: false, Black: false,
Bold: true, Bold: true,
MinHeight: -10, MinHeight: -10,
@@ -727,7 +712,6 @@ func defaultOptions() *Options {
JumpLabels: defaultJumpLabels, JumpLabels: defaultJumpLabels,
Prompt: "> ", Prompt: "> ",
Gutter: nil, Gutter: nil,
GutterRaw: nil,
Pointer: nil, Pointer: nil,
Marker: nil, Marker: nil,
MarkerMulti: nil, MarkerMulti: nil,
@@ -1326,9 +1310,8 @@ func dupeTheme(theme *tui.ColorTheme) *tui.ColorTheme {
return &dupe return &dupe
} }
func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui.ColorTheme, error) { func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, error) {
var err error var err error
var baseTheme *tui.ColorTheme
theme := dupeTheme(defaultTheme) theme := dupeTheme(defaultTheme)
rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$") rrggbb := regexp.MustCompile("^#[0-9a-fA-F]{6}$")
comma := regexp.MustCompile(`[\s,]+`) comma := regexp.MustCompile(`[\s,]+`)
@@ -1339,17 +1322,13 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui
} }
switch str { switch str {
case "dark": case "dark":
baseTheme = tui.Dark256
theme = dupeTheme(tui.Dark256) theme = dupeTheme(tui.Dark256)
case "light": case "light":
baseTheme = tui.Light256
theme = dupeTheme(tui.Light256) theme = dupeTheme(tui.Light256)
case "base16", "16": case "base16", "16":
baseTheme = tui.Default16
theme = dupeTheme(tui.Default16) theme = dupeTheme(tui.Default16)
case "bw", "no": case "bw", "no":
baseTheme = tui.NoColorTheme theme = tui.NoColorTheme()
theme = dupeTheme(tui.NoColorTheme)
default: default:
fail := func() { fail := func() {
// Let the code proceed to simplify the error handling // Let the code proceed to simplify the error handling
@@ -1374,8 +1353,6 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui
cattr.Attr |= tui.Bold cattr.Attr |= tui.Bold
case "dim": case "dim":
cattr.Attr |= tui.Dim cattr.Attr |= tui.Dim
case "strip":
cattr.Attr |= tui.Strip
case "italic": case "italic":
cattr.Attr |= tui.Italic cattr.Attr |= tui.Italic
case "underline": case "underline":
@@ -1463,8 +1440,6 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui
mergeAttr(&theme.SelectedBg) mergeAttr(&theme.SelectedBg)
case "nth": case "nth":
mergeAttr(&theme.Nth) mergeAttr(&theme.Nth)
case "nomatch":
mergeAttr(&theme.Nomatch)
case "gutter": case "gutter":
mergeAttr(&theme.Gutter) mergeAttr(&theme.Gutter)
case "hl": case "hl":
@@ -1530,7 +1505,7 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) (*tui.ColorTheme, *tui
} }
} }
} }
return baseTheme, theme, err return theme, err
} }
func parseWalkerOpts(str string) (walkerOpts, error) { func parseWalkerOpts(str string) (walkerOpts, error) {
@@ -1764,12 +1739,6 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actToggleMultiLine) appendAction(actToggleMultiLine)
case "toggle-hscroll": case "toggle-hscroll":
appendAction(actToggleHscroll) appendAction(actToggleHscroll)
case "toggle-raw":
appendAction(actToggleRaw)
case "enable-raw":
appendAction(actEnableRaw)
case "disable-raw":
appendAction(actDisableRaw)
case "show-header": case "show-header":
appendAction(actShowHeader) appendAction(actShowHeader)
case "hide-header": case "hide-header":
@@ -1790,18 +1759,12 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actToggle) appendAction(actToggle)
case "down": case "down":
appendAction(actDown) appendAction(actDown)
case "down-match":
appendAction(actDownMatch)
case "up": case "up":
appendAction(actUp) appendAction(actUp)
case "up-match":
appendAction(actUpMatch)
case "first", "top": case "first", "top":
appendAction(actFirst) appendAction(actFirst)
case "last": case "last":
appendAction(actLast) appendAction(actLast)
case "best":
appendAction(actBest)
case "page-up": case "page-up":
appendAction(actPageUp) appendAction(actPageUp)
case "page-down": case "page-down":
@@ -1814,9 +1777,9 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actPrevHistory) appendAction(actPrevHistory)
case "next-history": case "next-history":
appendAction(actNextHistory) appendAction(actNextHistory)
case "up-selected", "prev-selected": case "prev-selected":
appendAction(actPrevSelected) appendAction(actPrevSelected)
case "down-selected", "next-selected": case "next-selected":
appendAction(actNextSelected) appendAction(actNextSelected)
case "show-preview": case "show-preview":
appendAction(actShowPreview) appendAction(actShowPreview)
@@ -2639,7 +2602,9 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if err != nil { if err != nil {
return err return err
} }
maps.Copy(opts.Expect, chords) for k, v := range chords {
opts.Expect[k] = v
}
case "--no-expect": case "--no-expect":
opts.Expect = make(map[tui.Event]string) opts.Expect = make(map[tui.Event]string)
case "--enabled", "--no-phony": case "--enabled", "--no-phony":
@@ -2667,15 +2632,11 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
case "--color": case "--color":
_, spec := optionalNextString() _, spec := optionalNextString()
if len(spec) == 0 { if len(spec) == 0 {
opts.Theme = tui.EmptyTheme opts.Theme = tui.EmptyTheme()
} else { } else {
var baseTheme *tui.ColorTheme if opts.Theme, err = parseTheme(opts.Theme, spec); err != nil {
if baseTheme, opts.Theme, err = parseTheme(opts.Theme, spec); err != nil {
return err return err
} }
if baseTheme != nil {
opts.BaseTheme = baseTheme
}
} }
case "--toggle-sort": case "--toggle-sort":
str, err := nextString("key name required") str, err := nextString("key name required")
@@ -2699,14 +2660,6 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.Nth, err = splitNth(str); err != nil { if opts.Nth, err = splitNth(str); err != nil {
return err return err
} }
case "--freeze-left":
if opts.FreezeLeft, err = nextInt("number of fields required"); err != nil {
return err
}
case "--freeze-right":
if opts.FreezeRight, err = nextInt("number of fields required"); err != nil {
return err
}
case "--with-nth": case "--with-nth":
str, err := nextString("nth expression required") str, err := nextString("nth expression required")
if err != nil { if err != nil {
@@ -2729,10 +2682,6 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
} }
case "+s", "--no-sort": case "+s", "--no-sort":
opts.Sort = 0 opts.Sort = 0
case "--raw":
opts.Raw = true
case "--no-raw":
opts.Raw = false
case "--track": case "--track":
opts.Track = trackEnabled opts.Track = trackEnabled
case "--no-track": case "--no-track":
@@ -2769,8 +2718,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
case "--no-mouse": case "--no-mouse":
opts.Mouse = false opts.Mouse = false
case "+c", "--no-color": case "+c", "--no-color":
opts.BaseTheme = tui.NoColorTheme opts.Theme = tui.NoColorTheme()
opts.Theme = tui.NoColorTheme
case "+2", "--no-256": case "+2", "--no-256":
opts.Theme = tui.Default16 opts.Theme = tui.Default16
case "--black": case "--black":
@@ -2918,13 +2866,6 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
} }
str = firstLine(str) str = firstLine(str)
opts.Gutter = &str opts.Gutter = &str
case "--gutter-raw":
str, err := nextString("gutter character for raw mode required")
if err != nil {
return err
}
str = firstLine(str)
opts.GutterRaw = &str
case "--pointer": case "--pointer":
str, err := nextString("pointer sign required") str, err := nextString("pointer sign required")
if err != nil { if err != nil {
@@ -3350,10 +3291,6 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
return errors.New("empty jump labels") return errors.New("empty jump labels")
} }
if opts.FreezeLeft < 0 || opts.FreezeRight < 0 {
return errors.New("number of fields to freeze must be a non-negative integer")
}
if validateJumpLabels { if validateJumpLabels {
for _, r := range opts.JumpLabels { for _, r := range opts.JumpLabels {
if r < 32 || r > 126 { if r < 32 || r > 126 {
@@ -3447,9 +3384,10 @@ func validateOptions(opts *Options) error {
} }
} }
if opts.Gutter != nil && uniseg.StringWidth(*opts.Gutter) != 1 || if opts.Gutter != nil {
opts.GutterRaw != nil && uniseg.StringWidth(*opts.GutterRaw) != 1 { if err := validateSign(*opts.Gutter, "gutter", 1); err != nil {
return errors.New("gutter display width should be 1") return err
}
} }
if opts.Scrollbar != nil { if opts.Scrollbar != nil {
@@ -3651,6 +3589,23 @@ func postProcessOptions(opts *Options) error {
} }
} }
if opts.Bold {
theme := opts.Theme
boldify := func(c tui.ColorAttr) tui.ColorAttr {
dup := c
if (c.Attr & tui.AttrRegular) == 0 {
dup.Attr |= tui.BoldForce
}
return dup
}
theme.Current = boldify(theme.Current)
theme.CurrentMatch = boldify(theme.CurrentMatch)
theme.Prompt = boldify(theme.Prompt)
theme.Input = boldify(theme.Input)
theme.Cursor = boldify(theme.Cursor)
theme.Spinner = boldify(theme.Spinner)
}
// If --height option is not supported on the platform, just ignore it // If --height option is not supported on the platform, just ignore it
if !tui.IsLightRendererSupported() && opts.Height.size > 0 { if !tui.IsLightRendererSupported() && opts.Height.size > 0 {
opts.Height = heightSpec{} opts.Height = heightSpec{}

View File

@@ -300,12 +300,8 @@ func TestBind(t *testing.T) {
} }
func TestColorSpec(t *testing.T) { func TestColorSpec(t *testing.T) {
var base *tui.ColorTheme
theme := tui.Dark256 theme := tui.Dark256
base, dark, _ := parseTheme(theme, "dark") dark, _ := parseTheme(theme, "dark")
if *dark != *base {
t.Errorf("incorrect base theme returned")
}
if *dark != *theme { if *dark != *theme {
t.Errorf("colors should be equivalent") t.Errorf("colors should be equivalent")
} }
@@ -313,10 +309,7 @@ func TestColorSpec(t *testing.T) {
t.Errorf("point should not be equivalent") t.Errorf("point should not be equivalent")
} }
base, light, _ := parseTheme(theme, "dark,light") light, _ := parseTheme(theme, "dark,light")
if *light != *base {
t.Errorf("incorrect base theme returned")
}
if *light == *theme { if *light == *theme {
t.Errorf("should not be equivalent") t.Errorf("should not be equivalent")
} }
@@ -327,7 +320,7 @@ func TestColorSpec(t *testing.T) {
t.Errorf("point should not be equivalent") t.Errorf("point should not be equivalent")
} }
_, customized, _ := parseTheme(theme, "fg:231,bg:232") customized, _ := parseTheme(theme, "fg:231,bg:232")
if customized.Fg.Color != 231 || customized.Bg.Color != 232 { if customized.Fg.Color != 231 || customized.Bg.Color != 232 {
t.Errorf("color not customized") t.Errorf("color not customized")
} }
@@ -340,7 +333,7 @@ func TestColorSpec(t *testing.T) {
t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized) t.Errorf("colors should now be equivalent: %v, %v", tui.Dark256, customized)
} }
_, customized, _ = parseTheme(theme, "fg:231,dark bg:232") customized, _ = parseTheme(theme, "fg:231,dark bg:232")
if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg { if customized.Fg != tui.Dark256.Fg || customized.Bg == tui.Dark256.Bg {
t.Errorf("color not customized") t.Errorf("color not customized")
} }
@@ -357,8 +350,8 @@ func TestDefaultCtrlNP(t *testing.T) {
t.Error() t.Error()
} }
} }
check([]string{}, tui.CtrlN, actDownMatch) check([]string{}, tui.CtrlN, actDown)
check([]string{}, tui.CtrlP, actUpMatch) check([]string{}, tui.CtrlP, actUp)
check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept) check([]string{"--bind=ctrl-n:accept"}, tui.CtrlN, actAccept)
check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept) check([]string{"--bind=ctrl-p:accept"}, tui.CtrlP, actAccept)

View File

@@ -123,7 +123,7 @@ func minRank() Result {
return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}} return Result{item: &minItem, points: [4]uint16{math.MaxUint16, 0, 0, 0}}
} }
func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, attrNth tui.Attr, hidden bool) []colorOffset { func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, theme *tui.ColorTheme, colBase tui.ColorPair, colMatch tui.ColorPair, attrNth tui.Attr) []colorOffset {
itemColors := result.item.Colors() itemColors := result.item.Colors()
// No ANSI codes // No ANSI codes
@@ -194,10 +194,6 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
if !theme.Colored { if !theme.Colored {
return tui.NewColorPair(-1, -1, ansi.color.attr).MergeAttr(base) return tui.NewColorPair(-1, -1, ansi.color.attr).MergeAttr(base)
} }
// fd --color always | fzf --ansi --delimiter / --nth -1 --color fg:dim:strip,nth:regular
if base.ShouldStripColors() {
return base
}
fg := ansi.color.fg fg := ansi.color.fg
bg := ansi.color.bg bg := ansi.color.bg
if fg == -1 { if fg == -1 {
@@ -255,9 +251,6 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
if curr.nth { if curr.nth {
base = base.WithAttr(attrNth) base = base.WithAttr(attrNth)
} }
if hidden {
base = base.WithFg(theme.Nomatch)
}
color := ansiToColorPair(ansi, base) color := ansiToColorPair(ansi, base)
colors = append(colors, colorOffset{ colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, offset: [2]int32{int32(start), int32(idx)},
@@ -265,13 +258,9 @@ func (result *Result) colorOffsets(matchOffsets []Offset, nthOffsets []Offset, t
match: false, match: false,
url: ansi.color.url}) url: ansi.color.url})
} else { } else {
color := colBase.WithAttr(attrNth)
if hidden {
color = color.WithFg(theme.Nomatch)
}
colors = append(colors, colorOffset{ colors = append(colors, colorOffset{
offset: [2]int32{int32(start), int32(idx)}, offset: [2]int32{int32(start), int32(idx)},
color: color, color: colBase.WithAttr(attrNth),
match: false, match: false,
url: nil}) url: nil})
} }

View File

@@ -131,7 +131,7 @@ func TestColorOffset(t *testing.T) {
colBase := tui.NewColorPair(89, 189, tui.AttrUndefined) colBase := tui.NewColorPair(89, 189, tui.AttrUndefined)
colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined) colMatch := tui.NewColorPair(99, 199, tui.AttrUndefined)
colors := item.colorOffsets(offsets, nil, tui.Dark256, colBase, colMatch, tui.AttrUndefined, false) colors := item.colorOffsets(offsets, nil, tui.Dark256, colBase, colMatch, tui.AttrUndefined)
assert := func(idx int, b int32, e int32, c tui.ColorPair) { assert := func(idx int, b int32, e int32, c tui.ColorPair) {
o := colors[idx] o := colors[idx]
if o.offset[0] != b || o.offset[1] != e || o.color != c { if o.offset[0] != b || o.offset[1] != e || o.color != c {
@@ -158,7 +158,7 @@ func TestColorOffset(t *testing.T) {
nthOffsets := []Offset{{37, 39}, {42, 45}} nthOffsets := []Offset{{37, 39}, {42, 45}}
for _, attr := range []tui.Attr{tui.AttrRegular, tui.StrikeThrough} { for _, attr := range []tui.Attr{tui.AttrRegular, tui.StrikeThrough} {
colors = item.colorOffsets(offsets, nthOffsets, tui.Dark256, colRegular, colUnderline, attr, false) colors = item.colorOffsets(offsets, nthOffsets, tui.Dark256, colRegular, colUnderline, attr)
// [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}} // [{[0 5] {1 5 0}} {[5 15] {1 5 8}} {[15 20] {1 5 0}}
// {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}} // {[22 25] {2 6 1}} {[25 27] {2 6 9}} {[27 30] {-1 -1 8}}

View File

@@ -46,20 +46,15 @@ type httpServer struct {
type listenAddress struct { type listenAddress struct {
host string host string
port int port int
sock string
} }
func (addr listenAddress) IsLocal() bool { func (addr listenAddress) IsLocal() bool {
return addr.host == "localhost" || addr.host == "127.0.0.1" || len(addr.sock) > 0 return addr.host == "localhost" || addr.host == "127.0.0.1"
} }
var defaultListenAddr = listenAddress{"localhost", 0, ""} var defaultListenAddr = listenAddress{"localhost", 0}
func parseListenAddress(address string) (listenAddress, error) { func parseListenAddress(address string) (listenAddress, error) {
if strings.HasSuffix(address, ".sock") {
return listenAddress{"", 0, address}, nil
}
parts := strings.SplitN(address, ":", 3) parts := strings.SplitN(address, ":", 3)
if len(parts) == 1 { if len(parts) == 1 {
parts = []string{"localhost", parts[0]} parts = []string{"localhost", parts[0]}
@@ -75,7 +70,7 @@ func parseListenAddress(address string) (listenAddress, error) {
if len(parts[0]) == 0 { if len(parts[0]) == 0 {
parts[0] = "localhost" parts[0] = "localhost"
} }
return listenAddress{parts[0], port, ""}, nil return listenAddress{parts[0], port}, nil
} }
func startHttpServer(address listenAddress, actionChannel chan []*action, getHandler func(getParams) string) (net.Listener, int, error) { func startHttpServer(address listenAddress, actionChannel chan []*action, getHandler func(getParams) string) (net.Listener, int, error) {
@@ -85,40 +80,21 @@ func startHttpServer(address listenAddress, actionChannel chan []*action, getHan
if !address.IsLocal() && len(apiKey) == 0 { if !address.IsLocal() && len(apiKey) == 0 {
return nil, port, errors.New("FZF_API_KEY is required to allow remote access") return nil, port, errors.New("FZF_API_KEY is required to allow remote access")
} }
addrStr := fmt.Sprintf("%s:%d", host, port)
var listener net.Listener listener, err := net.Listen("tcp", addrStr)
var err error if err != nil {
if len(address.sock) > 0 { return nil, port, fmt.Errorf("failed to listen on %s", addrStr)
if _, err := os.Stat(address.sock); err == nil { }
// Check if the socket is already in use if port == 0 {
if conn, err := net.Dial("unix", address.sock); err == nil { addr := listener.Addr().String()
conn.Close() parts := strings.Split(addr, ":")
return nil, 0, fmt.Errorf("socket already in use: %s", address.sock) if len(parts) < 2 {
} return nil, port, fmt.Errorf("cannot extract port: %s", addr)
os.Remove(address.sock)
} }
listener, err = net.Listen("unix", address.sock) var err error
port, err = strconv.Atoi(parts[len(parts)-1])
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("failed to listen on %s", address.sock) return nil, port, err
}
os.Chmod(address.sock, 0600)
} else {
addrStr := fmt.Sprintf("%s:%d", host, port)
listener, err = net.Listen("tcp", addrStr)
if err != nil {
return nil, port, fmt.Errorf("failed to listen on %s", addrStr)
}
if port == 0 {
addr := listener.Addr().String()
parts := strings.Split(addr, ":")
if len(parts) < 2 {
return nil, port, fmt.Errorf("cannot extract port: %s", addr)
}
var err error
port, err = strconv.Atoi(parts[len(parts)-1])
if err != nil {
return nil, port, err
}
} }
} }

View File

@@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"maps"
"math" "math"
"net" "net"
"os" "os"
@@ -180,7 +179,6 @@ type itemLine struct {
result Result result Result
empty bool empty bool
other bool other bool
hidden bool
} }
func (t *Terminal) inListWindow() bool { func (t *Terminal) inListWindow() bool {
@@ -275,11 +273,9 @@ type Terminal struct {
footerLabelLen int footerLabelLen int
footerLabelOpts labelOpts footerLabelOpts labelOpts
gutterReverse bool gutterReverse bool
gutterRawReverse bool
pointer string pointer string
pointerLen int pointerLen int
pointerEmpty string pointerEmpty string
pointerEmptyRaw string
marker string marker string
markerLen int markerLen int
markerEmpty string markerEmpty string
@@ -331,8 +327,6 @@ type Terminal struct {
scrollbar string scrollbar string
previewScrollbar string previewScrollbar string
ansi bool ansi bool
freezeLeft int
freezeRight int
nthAttr tui.Attr nthAttr tui.Attr
nth []Range nth []Range
nthCurrent []Range nthCurrent []Range
@@ -388,9 +382,6 @@ type Terminal struct {
printer func(string) printer func(string)
printsep string printsep string
merger *Merger merger *Merger
passMerger *Merger
resultMerger *Merger
matchMap map[int32]Result
selected map[int32]selectedItem selected map[int32]selectedItem
version int64 version int64
revision revision revision revision
@@ -409,6 +400,7 @@ type Terminal struct {
initFunc func() error initFunc func() error
prevLines []itemLine prevLines []itemLine
suppress bool suppress bool
sigstop bool
startChan chan fitpad startChan chan fitpad
killChan chan bool killChan chan bool
serverInputChan chan []*action serverInputChan chan []*action
@@ -437,7 +429,6 @@ type Terminal struct {
clickFooterColumn int clickFooterColumn int
proxyScript string proxyScript string
numLinesCache map[int32]numLinesCacheValue numLinesCache map[int32]numLinesCacheValue
raw bool
} }
type numLinesCacheValue struct { type numLinesCacheValue struct {
@@ -578,18 +569,13 @@ const (
actToggleWrap actToggleWrap
actToggleMultiLine actToggleMultiLine
actToggleHscroll actToggleHscroll
actToggleRaw
actEnableRaw
actDisableRaw
actTrackCurrent actTrackCurrent
actToggleInput actToggleInput
actHideInput actHideInput
actShowInput actShowInput
actUntrackCurrent actUntrackCurrent
actDown actDown
actDownMatch
actUp actUp
actUpMatch
actPageUp actPageUp
actPageDown actPageDown
actPosition actPosition
@@ -665,7 +651,6 @@ const (
actExecuteSilent actExecuteSilent
actExecuteMulti // Deprecated actExecuteMulti // Deprecated
actSigStop actSigStop
actBest
actFirst actFirst
actLast actLast
actReload actReload
@@ -802,6 +787,7 @@ func defaultKeymap() map[tui.Event][]*action {
add(tui.CtrlD, actDeleteCharEof) add(tui.CtrlD, actDeleteCharEof)
add(tui.CtrlE, actEndOfLine) add(tui.CtrlE, actEndOfLine)
add(tui.CtrlF, actForwardChar) add(tui.CtrlF, actForwardChar)
add(tui.CtrlH, actBackwardDeleteChar)
add(tui.Backspace, actBackwardDeleteChar) add(tui.Backspace, actBackwardDeleteChar)
add(tui.CtrlBackspace, actBackwardDeleteChar) add(tui.CtrlBackspace, actBackwardDeleteChar)
add(tui.Tab, actToggleDown) add(tui.Tab, actToggleDown)
@@ -810,10 +796,8 @@ func defaultKeymap() map[tui.Event][]*action {
add(tui.CtrlK, actUp) add(tui.CtrlK, actUp)
add(tui.CtrlL, actClearScreen) add(tui.CtrlL, actClearScreen)
add(tui.Enter, actAccept) add(tui.Enter, actAccept)
add(tui.CtrlN, actDownMatch) add(tui.CtrlN, actDown)
add(tui.CtrlP, actUpMatch) add(tui.CtrlP, actUp)
add(tui.AltDown, actDownMatch)
add(tui.AltUp, actUpMatch)
add(tui.CtrlU, actUnixLineDiscard) add(tui.CtrlU, actUnixLineDiscard)
add(tui.CtrlW, actUnixWordRubout) add(tui.CtrlW, actUnixWordRubout)
add(tui.CtrlY, actYank) add(tui.CtrlY, actYank)
@@ -967,9 +951,11 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
wordRubout = fmt.Sprintf("%s[^%s]", sep, sep) wordRubout = fmt.Sprintf("%s[^%s]", sep, sep)
wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep) wordNext = fmt.Sprintf("[^%s]%s|(.$)", sep, sep)
} }
keymapCopy := maps.Clone(opts.Keymap) keymapCopy := make(map[tui.Event][]*action)
for key, action := range opts.Keymap {
keymapCopy[key] = action
}
em := EmptyMerger(revision{})
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
infoCommand: opts.InfoCommand, infoCommand: opts.InfoCommand,
@@ -1052,13 +1038,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
footer: opts.Footer, footer: opts.Footer,
header0: opts.Header, header0: opts.Header,
ansi: opts.Ansi, ansi: opts.Ansi,
freezeLeft: opts.FreezeLeft,
freezeRight: opts.FreezeRight,
nthAttr: opts.Theme.Nth.Attr, nthAttr: opts.Theme.Nth.Attr,
nth: opts.Nth, nth: opts.Nth,
nthCurrent: opts.Nth, nthCurrent: opts.Nth,
tabstop: opts.Tabstop, tabstop: opts.Tabstop,
raw: opts.Raw,
hasStartActions: false, hasStartActions: false,
hasResultActions: false, hasResultActions: false,
hasFocusActions: false, hasFocusActions: false,
@@ -1072,10 +1055,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
printer: opts.Printer, printer: opts.Printer,
printsep: opts.PrintSep, printsep: opts.PrintSep,
proxyScript: opts.ProxyScript, proxyScript: opts.ProxyScript,
merger: em, merger: EmptyMerger(revision{}),
passMerger: em,
resultMerger: em,
matchMap: make(map[int32]Result),
selected: make(map[int32]selectedItem), selected: make(map[int32]selectedItem),
runningCmds: util.NewConcurrentSet[*runningCmd](), runningCmds: util.NewConcurrentSet[*runningCmd](),
reqBox: util.NewEventBox(), reqBox: util.NewEventBox(),
@@ -1112,41 +1092,26 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
t.acceptNth = opts.AcceptNth(t.delimiter) t.acceptNth = opts.AcceptNth(t.delimiter)
} }
baseTheme := opts.BaseTheme
if baseTheme == nil {
baseTheme = renderer.DefaultTheme()
}
// This should be called before accessing tui.Color* // This should be called before accessing tui.Color*
tui.InitTheme(opts.Theme, baseTheme, opts.Bold, opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible()) tui.InitTheme(opts.Theme, renderer.DefaultTheme(), opts.Black, opts.InputBorderShape.Visible(), opts.HeaderBorderShape.Visible())
// Gutter character // Gutter character
var gutterChar, gutterRawChar string var gutterChar string
if opts.Gutter != nil { if opts.Gutter != nil {
gutterChar = *opts.Gutter gutterChar = *opts.Gutter
} else if t.unicode { } else if t.unicode && !t.theme.Gutter.Color.IsDefault() {
gutterChar = "▌" gutterChar = "▌"
} else { } else {
gutterChar = " " gutterChar = " "
t.gutterReverse = true t.gutterReverse = true
} }
if opts.GutterRaw != nil {
gutterRawChar = *opts.GutterRaw
} else if t.unicode {
gutterRawChar = "▖"
} else {
gutterRawChar = ":"
t.gutterRawReverse = false
}
t.prompt, t.promptLen = t.parsePrompt(opts.Prompt) t.prompt, t.promptLen = t.parsePrompt(opts.Prompt)
// Pre-calculated empty pointer and marker signs // Pre-calculated empty pointer and marker signs
if t.pointerLen == 0 { if t.pointerLen == 0 {
t.pointerEmpty = "" t.pointerEmpty = ""
t.pointerEmptyRaw = ""
} else { } else {
t.pointerEmpty = gutterChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1)) t.pointerEmpty = gutterChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
t.pointerEmptyRaw = gutterRawChar + strings.Repeat(" ", util.Max(0, t.pointerLen-1))
} }
t.markerEmpty = strings.Repeat(" ", t.markerLen) t.markerEmpty = strings.Repeat(" ", t.markerLen)
@@ -1271,9 +1236,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
return nil, err return nil, err
} }
t.listener = listener t.listener = listener
if port > 0 { t.listenPort = &port
t.listenPort = &port
}
} }
if t.hasStartActions { if t.hasStartActions {
@@ -1297,9 +1260,6 @@ func (t *Terminal) environForPreview() []string {
func (t *Terminal) environImpl(forPreview bool) []string { func (t *Terminal) environImpl(forPreview bool) []string {
env := os.Environ() env := os.Environ()
if t.listenAddr != nil && len(t.listenAddr.sock) > 0 {
env = append(env, "FZF_SOCK="+t.listenAddr.sock)
}
if t.listenPort != nil { if t.listenPort != nil {
env = append(env, fmt.Sprintf("FZF_PORT=%d", *t.listenPort)) env = append(env, fmt.Sprintf("FZF_PORT=%d", *t.listenPort))
} }
@@ -1314,21 +1274,9 @@ func (t *Terminal) environImpl(forPreview bool) []string {
env = append(env, "FZF_LIST_LABEL="+t.listLabelOpts.label) env = append(env, "FZF_LIST_LABEL="+t.listLabelOpts.label)
env = append(env, "FZF_INPUT_LABEL="+t.inputLabelOpts.label) env = append(env, "FZF_INPUT_LABEL="+t.inputLabelOpts.label)
env = append(env, "FZF_HEADER_LABEL="+t.headerLabelOpts.label) env = append(env, "FZF_HEADER_LABEL="+t.headerLabelOpts.label)
direction := "down"
if t.layout == layoutDefault {
direction = "up"
}
env = append(env, "FZF_DIRECTION="+direction)
if len(t.nthCurrent) > 0 { if len(t.nthCurrent) > 0 {
env = append(env, "FZF_NTH="+RangesToString(t.nthCurrent)) env = append(env, "FZF_NTH="+RangesToString(t.nthCurrent))
} }
if t.raw {
val := "0"
if t.isCurrentItemMatch() {
val = "1"
}
env = append(env, "FZF_RAW="+val)
}
inputState := "enabled" inputState := "enabled"
if t.inputless { if t.inputless {
inputState = "hidden" inputState = "hidden"
@@ -1337,7 +1285,7 @@ func (t *Terminal) environImpl(forPreview bool) []string {
} }
env = append(env, "FZF_INPUT_STATE="+inputState) env = append(env, "FZF_INPUT_STATE="+inputState)
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count)) env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.resultMerger.Length())) env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.merger.Length()))
env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected))) env = append(env, fmt.Sprintf("FZF_SELECT_COUNT=%d", len(t.selected)))
env = append(env, fmt.Sprintf("FZF_LINES=%d", t.areaLines)) env = append(env, fmt.Sprintf("FZF_LINES=%d", t.areaLines))
env = append(env, fmt.Sprintf("FZF_COLUMNS=%d", t.areaColumns)) env = append(env, fmt.Sprintf("FZF_COLUMNS=%d", t.areaColumns))
@@ -1498,7 +1446,7 @@ func (t *Terminal) ansiLabelPrinter(str string, color *tui.ColorPair, fill bool)
printFn := func(window tui.Window, limit int) { printFn := func(window tui.Window, limit int) {
if offsets == nil { if offsets == nil {
// tui.Col* are not initialized until renderer.Init() // tui.Col* are not initialized until renderer.Init()
offsets = result.colorOffsets(nil, nil, t.theme, *color, *color, t.nthAttr, false) offsets = result.colorOffsets(nil, nil, t.theme, *color, *color, t.nthAttr)
} }
for limit > 0 { for limit > 0 {
if length > limit { if length > limit {
@@ -1561,7 +1509,7 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) {
return 1 return 1
} }
t.printHighlighted( t.printHighlighted(
Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, false, line, line, true, preTask, nil) Result{item: item}, tui.ColPrompt, tui.ColPrompt, false, false, line, line, true, preTask, nil)
}) })
t.wrap = wrap t.wrap = wrap
} }
@@ -1748,8 +1696,7 @@ func (t *Terminal) UpdateProgress(progress float32) {
} }
// UpdateList updates Merger to display the list // UpdateList updates Merger to display the list
func (t *Terminal) UpdateList(result MatchResult) { func (t *Terminal) UpdateList(merger *Merger) {
merger := result.merger
t.mutex.Lock() t.mutex.Lock()
prevIndex := minItem.Index() prevIndex := minItem.Index()
newRevision := merger.Revision() newRevision := merger.Revision()
@@ -1762,15 +1709,6 @@ func (t *Terminal) UpdateList(result MatchResult) {
} }
t.progress = 100 t.progress = 100
t.merger = merger t.merger = merger
t.resultMerger = merger
t.passMerger = result.passMerger
if t.raw {
t.merger = result.passMerger
t.matchMap = t.resultMerger.ToMap()
} else {
t.merger = result.merger
t.matchMap = make(map[int32]Result)
}
if t.revision != newRevision { if t.revision != newRevision {
if !t.revision.compatible(newRevision) { if !t.revision.compatible(newRevision) {
// Reloaded: clear selection // Reloaded: clear selection
@@ -1819,7 +1757,7 @@ func (t *Terminal) UpdateList(result MatchResult) {
} }
needActivation := false needActivation := false
if !t.reading { if !t.reading {
switch t.resultMerger.Length() { switch t.merger.Length() {
case 0: case 0:
zero := tui.Zero.AsEvent() zero := tui.Zero.AsEvent()
if _, prs := t.keymap[zero]; prs { if _, prs := t.keymap[zero]; prs {
@@ -2462,13 +2400,6 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
innerHeight-shrink, tui.WindowList, noBorder, true) innerHeight-shrink, tui.WindowList, noBorder, true)
} }
if len(t.scrollbar) == 0 {
for y := 0; y < t.window.Height(); y++ {
t.window.Move(y, t.window.Width()-1)
t.window.Print(" ")
}
}
createInnerWindow := func(b tui.Window, shape tui.BorderShape, windowType tui.WindowType, shift int) tui.Window { createInnerWindow := func(b tui.Window, shape tui.BorderShape, windowType tui.WindowType, shift int) tui.Window {
top := b.Top() top := b.Top()
left := b.Left() + shift left := b.Left() + shift
@@ -2866,7 +2797,7 @@ func (t *Terminal) printInfoImpl() {
return return
} }
found := t.resultMerger.Length() found := t.merger.Length()
total := util.Max(found, t.count) total := util.Max(found, t.count)
output := fmt.Sprintf("%d/%d", found, total) output := fmt.Sprintf("%d/%d", found, total)
if t.toggleSort { if t.toggleSort {
@@ -2985,11 +2916,6 @@ func (t *Terminal) printInfoImpl() {
} else { } else {
outputPrinter(t.window, maxWidth) outputPrinter(t.window, maxWidth)
} }
if t.infoStyle == infoInline && outputLen < maxWidth-1 && t.reading {
t.window.Print(" ")
printSpinner()
outputLen += 2
}
if t.infoStyle == infoInlineRight { if t.infoStyle == infoInlineRight {
if t.separatorLen > 0 { if t.separatorLen > 0 {
@@ -3088,7 +3014,7 @@ func (t *Terminal) printFooter() {
colors: colors} colors: colors}
t.printHighlighted(Result{item: item}, t.printHighlighted(Result{item: item},
tui.ColFooter, tui.ColFooter, false, false, false, line, line, true, tui.ColFooter, tui.ColFooter, false, false, line, line, true,
func(markerClass) int { func(markerClass) int {
t.footerWindow.Print(indent) t.footerWindow.Print(indent)
return indentSize return indentSize
@@ -3160,7 +3086,7 @@ func (t *Terminal) printHeaderImpl(window tui.Window, borderShape tui.BorderShap
colors: colors} colors: colors}
t.printHighlighted(Result{item: item}, t.printHighlighted(Result{item: item},
tui.ColHeader, tui.ColHeader, false, false, false, line, line, true, tui.ColHeader, tui.ColHeader, false, false, line, line, true,
func(markerClass) int { func(markerClass) int {
t.window.Print(indent) t.window.Print(indent)
return indentSize return indentSize
@@ -3192,16 +3118,12 @@ func (t *Terminal) gutter(current bool) {
var color tui.ColorPair var color tui.ColorPair
if current { if current {
color = tui.ColCurrentCursorEmpty color = tui.ColCurrentCursorEmpty
} else if !t.raw && t.gutterReverse || t.raw && t.gutterRawReverse { } else if t.gutterReverse {
color = tui.ColCursorEmpty color = tui.ColCursorEmpty
} else { } else {
color = tui.ColCursorEmptyChar color = tui.ColCursorEmptyChar
} }
gutter := t.pointerEmpty t.window.CPrint(color, t.pointerEmpty)
if t.raw {
gutter = t.pointerEmptyRaw
}
t.window.CPrint(color, gutter)
} }
func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) { func (t *Terminal) renderGapLine(line int, barRange [2]int, drawLine bool) {
@@ -3236,8 +3158,7 @@ func (t *Terminal) printList() {
for line, itemCount := startLine, 0; line <= maxy; line, itemCount = line+1, itemCount+1 { for line, itemCount := startLine, 0; line <= maxy; line, itemCount = line+1, itemCount+1 {
if itemCount < count { if itemCount < count {
item := t.merger.Get(itemCount + t.offset) item := t.merger.Get(itemCount + t.offset)
current := itemCount == t.cy-t.offset line = t.printItem(item, line, maxy, itemCount, itemCount == t.cy-t.offset, barRange)
line = t.printItem(item, line, maxy, itemCount, current, barRange)
} else if !t.prevLines[line].empty { } else if !t.prevLines[line].empty {
t.renderEmptyLine(line, barRange) t.renderEmptyLine(line, barRange)
} }
@@ -3259,14 +3180,6 @@ func (t *Terminal) printBar(lineNum int, forceRedraw bool, barRange [2]int) bool
func (t *Terminal) printItem(result Result, line int, maxLine int, index int, current bool, barRange [2]int) int { func (t *Terminal) printItem(result Result, line int, maxLine int, index int, current bool, barRange [2]int) int {
item := result.item item := result.item
matched := true
var matchResult Result
if t.raw {
if matchResult, matched = t.matchMap[item.Index()]; matched {
result = matchResult
}
}
_, selected := t.selected[item.Index()] _, selected := t.selected[item.Index()]
label := "" label := ""
extraWidth := 0 extraWidth := 0
@@ -3297,7 +3210,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
// Avoid unnecessary redraw // Avoid unnecessary redraw
numLines, _ := t.numItemLines(item, maxLine-line+1) numLines, _ := t.numItemLines(item, maxLine-line+1)
newLine := itemLine{valid: true, firstLine: line, numLines: numLines, cy: index + t.offset, current: current, selected: selected, label: label, newLine := itemLine{valid: true, firstLine: line, numLines: numLines, cy: index + t.offset, current: current, selected: selected, label: label,
result: result, queryLen: len(t.input), width: 0, hasBar: line >= barRange[0] && line < barRange[1], hidden: !matched} result: result, queryLen: len(t.input), width: 0, hasBar: line >= barRange[0] && line < barRange[1]}
prevLine := t.prevLines[line] prevLine := t.prevLines[line]
forceRedraw := !prevLine.valid || prevLine.other || prevLine.firstLine != newLine.firstLine forceRedraw := !prevLine.valid || prevLine.other || prevLine.firstLine != newLine.firstLine
printBar := func(lineNum int, forceRedraw bool) bool { printBar := func(lineNum int, forceRedraw bool) bool {
@@ -3305,7 +3218,6 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
} }
if !forceRedraw && if !forceRedraw &&
prevLine.hidden == newLine.hidden &&
prevLine.numLines == newLine.numLines && prevLine.numLines == newLine.numLines &&
prevLine.current == newLine.current && prevLine.current == newLine.current &&
prevLine.selected == newLine.selected && prevLine.selected == newLine.selected &&
@@ -3394,7 +3306,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
} }
return indentSize return indentSize
} }
finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, !matched, line, maxLine, forceRedraw, preTask, postTask) finalLineNum = t.printHighlighted(result, tui.ColCurrent, tui.ColCurrentMatch, true, true, line, maxLine, forceRedraw, preTask, postTask)
} else { } else {
preTask := func(marker markerClass) int { preTask := func(marker markerClass) int {
w := t.window.Width() - t.pointerLen w := t.window.Width() - t.pointerLen
@@ -3428,7 +3340,7 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
base = base.WithBg(altBg) base = base.WithBg(altBg)
match = match.WithBg(altBg) match = match.WithBg(altBg)
} }
finalLineNum = t.printHighlighted(result, base, match, false, true, !matched, line, maxLine, forceRedraw, preTask, postTask) finalLineNum = t.printHighlighted(result, base, match, false, true, line, maxLine, forceRedraw, preTask, postTask)
} }
for i := 0; i < t.gap && finalLineNum < maxLine; i++ { for i := 0; i < t.gap && finalLineNum < maxLine; i++ {
finalLineNum++ finalLineNum++
@@ -3475,13 +3387,13 @@ func (t *Terminal) overflow(runes []rune, max int) bool {
return t.displayWidthWithLimit(runes, 0, max) > max return t.displayWidthWithLimit(runes, 0, max) > max
} }
func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool, hidden bool, lineNum int, maxLineNum int, forceRedraw bool, preTask func(markerClass) int, postTask func(int, int, bool, bool, tui.ColorPair)) int { func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMatch tui.ColorPair, current bool, match bool, lineNum int, maxLineNum int, forceRedraw bool, preTask func(markerClass) int, postTask func(int, int, bool, bool, tui.ColorPair)) int {
var displayWidth int var displayWidth int
item := result.item item := result.item
matchOffsets := []Offset{} matchOffsets := []Offset{}
var pos *[]int var pos *[]int
if match && t.resultMerger.pattern != nil { if match && t.merger.pattern != nil {
_, matchOffsets, pos = t.resultMerger.pattern.MatchItem(item, true, t.slab) _, matchOffsets, pos = t.merger.pattern.MatchItem(item, true, t.slab)
} }
charOffsets := matchOffsets charOffsets := matchOffsets
if pos != nil { if pos != nil {
@@ -3513,7 +3425,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
} }
if !wholeCovered && t.nthAttr > 0 { if !wholeCovered && t.nthAttr > 0 {
var tokens []Token var tokens []Token
if item.transformed != nil && item.transformed.revision == t.resultMerger.revision { if item.transformed != nil && item.transformed.revision == t.merger.revision {
tokens = item.transformed.tokens tokens = item.transformed.tokens
} else { } else {
tokens = Transform(Tokenize(item.text.ToString(), t.delimiter), t.nthCurrent) tokens = Transform(Tokenize(item.text.ToString(), t.delimiter), t.nthCurrent)
@@ -3527,35 +3439,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
sort.Sort(ByOrder(nthOffsets)) sort.Sort(ByOrder(nthOffsets))
} }
} }
allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr, hidden) allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr)
// Determine split offset for horizontal scrolling with freeze
splitOffset1 := -1
splitOffset2 := -1
if t.hscroll && !t.wrap {
var tokens []Token
if t.freezeLeft > 0 || t.freezeRight > 0 {
tokens = Tokenize(item.text.ToString(), t.delimiter)
}
// 0 1 2| 3| 4 5
// ----- ---
if t.freezeLeft > 0 {
if len(tokens) > 0 {
token := tokens[util.Min(t.freezeLeft, len(tokens))-1]
splitOffset1 = int(token.prefixLength) + token.text.Length() - token.text.TrailingWhitespaces()
}
}
if t.freezeRight > 0 {
index := util.Max(t.freezeLeft-1, len(tokens)-t.freezeRight-1)
if index < 0 {
splitOffset2 = 0
} else if index >= t.freezeLeft {
token := tokens[index]
splitOffset2 = int(token.prefixLength) + token.text.Length()
}
splitOffset2 = util.Max(splitOffset2, splitOffset1)
}
}
maxLines := 1 maxLines := 1
if t.canSpanMultiLines() { if t.canSpanMultiLines() {
@@ -3626,24 +3510,16 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
break break
} }
} }
splitOffsetLeft := 0
if splitOffset1 >= 0 && splitOffset1 > from && splitOffset1 < from+len(line) {
splitOffsetLeft = splitOffset1 - from
}
splitOffsetRight := -1
if splitOffset2 >= 0 && splitOffset2 >= from && splitOffset2 < from+len(line) {
splitOffsetRight = splitOffset2 - from
}
from += len(line) from += len(line)
if lineOffset < skipLines { if lineOffset < skipLines {
continue continue
} }
actualLineOffset := lineOffset - skipLines actualLineOffset := lineOffset - skipLines
var maxEnd int var maxe int
for _, offset := range offsets { for _, offset := range offsets {
if offset.match { if offset.match {
maxEnd = util.Max(maxEnd, int(offset.offset[1])) maxe = util.Max(maxe, int(offset.offset[1]))
} }
} }
@@ -3707,112 +3583,65 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
wrapped = true wrapped = true
} }
frozenLeft := line[:splitOffsetLeft] displayWidth = t.displayWidthWithLimit(line, 0, maxWidth)
middle := line[splitOffsetLeft:] if !t.wrap && displayWidth > maxWidth {
frozenRight := []rune{} ellipsis, ellipsisWidth := util.Truncate(t.ellipsis, maxWidth/2)
if splitOffsetRight >= splitOffsetLeft { maxe = util.Constrain(maxe+util.Min(maxWidth/2-ellipsisWidth, t.hscrollOff), 0, len(line))
middle = line[splitOffsetLeft:splitOffsetRight] transformOffsets := func(diff int32, rightTrim bool) {
frozenRight = line[splitOffsetRight:] for idx, offset := range offsets {
} b, e := offset.offset[0], offset.offset[1]
displayWidthSum := 0 el := int32(len(ellipsis))
todo := [3]func(){} b += el - diff
for fidx, runes := range [][]rune{frozenLeft, frozenRight, middle} { e += el - diff
if len(runes) == 0 { b = util.Max32(b, el)
continue if rightTrim {
} e = util.Min32(e, int32(maxWidth-ellipsisWidth))
shift := 0
maxe := maxEnd
offs := make([]colorOffset, len(offsets))
for idx := range offsets {
offs[idx] = offsets[idx]
if fidx == 1 && splitOffsetRight > 0 {
shift = splitOffsetRight
} else if fidx == 2 && splitOffsetLeft > 0 {
shift = splitOffsetLeft
}
offs[idx].offset[0] -= int32(shift)
offs[idx].offset[1] -= int32(shift)
}
maxe -= shift
displayWidth = t.displayWidthWithLimit(runes, 0, maxWidth)
if !t.wrap && displayWidth > maxWidth {
ellipsis, ellipsisWidth := util.Truncate(t.ellipsis, maxWidth/2)
maxe = util.Constrain(maxe+util.Min(maxWidth/2-ellipsisWidth, t.hscrollOff), 0, len(runes))
transformOffsets := func(diff int32, rightTrim bool) {
for idx, offset := range offs {
b, e := offset.offset[0], offset.offset[1]
el := int32(len(ellipsis))
b += el - diff
e += el - diff
b = util.Max32(b, el)
if rightTrim {
e = util.Min32(e, int32(maxWidth-ellipsisWidth))
}
offs[idx].offset[0] = b
offs[idx].offset[1] = util.Max32(b, e)
} }
offsets[idx].offset[0] = b
offsets[idx].offset[1] = util.Max32(b, e)
} }
if t.hscroll { }
if fidx == 1 || fidx == 2 && t.keepRight && pos == nil { if t.hscroll {
trimmed, diff := t.trimLeft(runes, maxWidth, ellipsisWidth) if t.keepRight && pos == nil {
transformOffsets(diff, false) trimmed, diff := t.trimLeft(line, maxWidth, ellipsisWidth)
runes = append(ellipsis, trimmed...) transformOffsets(diff, false)
} else if fidx == 0 || !t.overflow(runes[:maxe], maxWidth-ellipsisWidth) { line = append(ellipsis, trimmed...)
// Stri.. } else if !t.overflow(line[:maxe], maxWidth-ellipsisWidth) {
runes, _ = t.trimRight(runes, maxWidth-ellipsisWidth) // Stri..
runes = append(runes, ellipsis...) line, _ = t.trimRight(line, maxWidth-ellipsisWidth)
} else { line = append(line, ellipsis...)
// Stri..
rightTrim := false
if t.overflow(runes[maxe:], ellipsisWidth) {
runes = append(runes[:maxe], ellipsis...)
rightTrim = true
}
// ..ri..
var diff int32
runes, diff = t.trimLeft(runes, maxWidth, ellipsisWidth)
// Transform offsets
transformOffsets(diff, rightTrim)
runes = append(ellipsis, runes...)
}
} else { } else {
runes, _ = t.trimRight(runes, maxWidth-ellipsisWidth) // Stri..
runes = append(runes, ellipsis...) rightTrim := false
if t.overflow(line[maxe:], ellipsisWidth) {
for idx, offset := range offs { line = append(line[:maxe], ellipsis...)
offs[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-len(ellipsis))) rightTrim = true
offs[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
} }
} // ..ri..
displayWidth = t.displayWidthWithLimit(runes, 0, displayWidth) var diff int32
} line, diff = t.trimLeft(line, maxWidth, ellipsisWidth)
displayWidthSum += displayWidth
if maxWidth > 0 { // Transform offsets
color := colBase transformOffsets(diff, rightTrim)
if hidden { line = append(ellipsis, line...)
color = color.WithFg(t.theme.Nomatch)
}
todo[fidx] = func() {
t.printColoredString(t.window, runes, offs, color)
} }
} else { } else {
break line, _ = t.trimRight(line, maxWidth-ellipsisWidth)
line = append(line, ellipsis...)
for idx, offset := range offsets {
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-len(ellipsis)))
offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
}
} }
maxWidth -= displayWidth displayWidth = t.displayWidthWithLimit(line, 0, displayWidth)
} }
if todo[0] != nil {
todo[0]() if maxWidth > 0 {
} t.printColoredString(t.window, line, offsets, colBase)
if todo[2] != nil {
todo[2]()
}
if todo[1] != nil {
todo[1]()
} }
if postTask != nil { if postTask != nil {
postTask(actualLineNum, displayWidthSum, wasWrapped, forceRedraw, lbg) postTask(actualLineNum, displayWidth, wasWrapped, forceRedraw, lbg)
} else { } else {
t.markOtherLine(actualLineNum) t.markOtherLine(actualLineNum)
} }
@@ -4844,33 +4673,6 @@ func (t *Terminal) currentItem() *Item {
return nil return nil
} }
func (t *Terminal) isCurrentItemMatch() bool {
cnt := t.merger.Length()
if t.cy >= 0 && cnt > 0 && cnt > t.cy {
if !t.raw {
return true
}
item := t.merger.Get(t.cy).item
return t.isItemMatch(item)
}
return false
}
func (t *Terminal) isItemMatch(item *Item) bool {
_, matched := t.matchMap[item.Index()]
return matched
}
func (t *Terminal) filterSelected() {
filtered := make(map[int32]selectedItem)
for k, v := range t.selected {
if t.isItemMatch(v.item) {
filtered[k] = v
}
}
t.selected = filtered
}
func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) { func (t *Terminal) buildPlusList(template string, forcePlus bool) (bool, [3][]*Item) {
current := t.currentItem() current := t.currentItem()
slot, plus, asterisk, forceUpdate := hasPreviewFlags(template) slot, plus, asterisk, forceUpdate := hasPreviewFlags(template)
@@ -6111,9 +5913,8 @@ func (t *Terminal) Loop() error {
} }
case actSelectAll: case actSelectAll:
if t.multi > 0 { if t.multi > 0 {
// Limit the scope only to the matching items for i := 0; i < t.merger.Length(); i++ {
for i := 0; i < t.resultMerger.Length(); i++ { if !t.selectItem(t.merger.Get(i).item) {
if !t.selectItem(t.resultMerger.Get(i).item) {
break break
} }
} }
@@ -6121,10 +5922,8 @@ func (t *Terminal) Loop() error {
} }
case actDeselectAll: case actDeselectAll:
if t.multi > 0 { if t.multi > 0 {
// Also limit the scope only to the matching items, while this may for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
// not be straightforward in raw mode. t.deselectItem(t.merger.Get(i).item)
for i := 0; i < t.resultMerger.Length() && len(t.selected) > 0; i++ {
t.deselectItem(t.resultMerger.Get(i).item)
} }
req(reqList, reqInfo) req(reqList, reqInfo)
} }
@@ -6152,17 +5951,17 @@ func (t *Terminal) Loop() error {
case actToggleAll: case actToggleAll:
if t.multi > 0 { if t.multi > 0 {
prevIndexes := make(map[int]struct{}) prevIndexes := make(map[int]struct{})
for i := 0; i < t.resultMerger.Length() && len(t.selected) > 0; i++ { for i := 0; i < t.merger.Length() && len(t.selected) > 0; i++ {
item := t.resultMerger.Get(i).item item := t.merger.Get(i).item
if _, found := t.selected[item.Index()]; found { if _, found := t.selected[item.Index()]; found {
prevIndexes[i] = struct{}{} prevIndexes[i] = struct{}{}
t.deselectItem(item) t.deselectItem(item)
} }
} }
for i := 0; i < t.resultMerger.Length(); i++ { for i := 0; i < t.merger.Length(); i++ {
if _, found := prevIndexes[i]; !found { if _, found := prevIndexes[i]; !found {
item := t.resultMerger.Get(i).item item := t.merger.Get(i).item
if !t.selectItem(item) { if !t.selectItem(item) {
break break
} }
@@ -6190,88 +5989,11 @@ func (t *Terminal) Loop() error {
t.vmove(1, true) t.vmove(1, true)
req(reqList) req(reqList)
} }
case actDown, actDownMatch, actUp, actUpMatch: case actDown:
dir := -1 t.vmove(-1, true)
if a.t == actUp || a.t == actUpMatch {
dir = 1
}
if t.raw && (a.t == actDownMatch || a.t == actUpMatch) {
if t.resultMerger.Length() > 0 {
prevCy := t.cy
for t.vmove(dir, true) && !t.isCurrentItemMatch() {
}
if !t.isCurrentItemMatch() {
t.vset(prevCy)
}
}
} else {
t.vmove(dir, true)
}
req(reqList) req(reqList)
case actToggleRaw, actEnableRaw, actDisableRaw: case actUp:
prevRaw := t.raw t.vmove(1, true)
newRaw := t.raw
switch a.t {
case actEnableRaw:
newRaw = true
case actDisableRaw:
newRaw = false
case actToggleRaw:
newRaw = !t.raw
}
if prevRaw == newRaw {
break
}
prevPos := t.cy - t.offset
prevIndex := t.currentIndex()
if newRaw {
// Build matchMap if not available
if len(t.matchMap) == 0 {
t.matchMap = t.resultMerger.ToMap()
}
t.merger = t.passMerger
} else {
// Find the closest matching item
if !t.isCurrentItemMatch() && t.resultMerger.Length() > 1 {
distance := 0
Loop:
for {
distance++
checks := 0
for _, cy := range []int{t.cy + distance, t.cy - distance} {
if cy >= 0 && cy < t.merger.Length() {
checks++
item := t.merger.Get(cy).item
if t.isItemMatch(item) {
prevIndex = item.Index()
break Loop
}
}
}
if checks == 0 {
break
}
}
}
t.merger = t.resultMerger
// Need to remove non-matching items from the selection
if t.multi > 0 && len(t.selected) > 0 {
t.filterSelected()
req(reqInfo)
}
}
t.raw = newRaw
// Try to retain position
if prevIndex != minItem.Index() {
t.cy = util.Max(0, t.merger.FindIndex(prevIndex))
t.offset = t.cy - prevPos
}
// List needs to be rerendered
t.forceRerenderList()
req(reqList) req(reqList)
case actAccept: case actAccept:
req(reqClose) req(reqClose)
@@ -6296,14 +6018,8 @@ func (t *Terminal) Loop() error {
t.version++ t.version++
req(reqList, reqInfo) req(reqList, reqInfo)
} }
case actFirst, actBest: case actFirst:
if t.raw && a.t == actBest { t.vset(0)
if t.resultMerger.Length() > 0 {
t.vset(t.merger.FindIndex(t.resultMerger.Get(0).item.Index()))
}
} else {
t.vset(0)
}
t.constrain() t.constrain()
req(reqList) req(reqList)
case actLast: case actLast:
@@ -6431,7 +6147,7 @@ func (t *Terminal) Loop() error {
if a.t == actOffsetDown { if a.t == actOffsetDown {
diff = -1 diff = -1
} }
if t.layout != layoutDefault { if t.layout == layoutReverse {
diff *= -1 diff *= -1
} }
t.offset += diff t.offset += diff
@@ -6439,7 +6155,7 @@ func (t *Terminal) Loop() error {
t.constrain() t.constrain()
if before != t.offset { if before != t.offset {
t.offset = before t.offset = before
if t.layout != layoutDefault { if t.layout == layoutReverse {
diff *= -1 diff *= -1
} }
t.vmove(diff, false) t.vmove(diff, false)
@@ -7121,7 +6837,7 @@ func (t *Terminal) Loop() error {
reload := changed || newCommand != nil reload := changed || newCommand != nil
var reloadRequest *searchRequest var reloadRequest *searchRequest
if reload { if reload {
reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.resultMerger.Revision()} reloadRequest = &searchRequest{sort: t.sort, sync: reloadSync, nth: newNth, command: newCommand, environ: t.environ(), changed: changed, denylist: denylist, revision: t.merger.Revision()}
} }
// Dispatch queued background requests // Dispatch queued background requests
@@ -7241,8 +6957,7 @@ func (t *Terminal) constrain() {
} }
} }
// Returns true if the cursor position is successfully updated func (t *Terminal) vmove(o int, allowCycle bool) {
func (t *Terminal) vmove(o int, allowCycle bool) bool {
if t.layout != layoutDefault { if t.layout != layoutDefault {
o *= -1 o *= -1
} }
@@ -7259,7 +6974,7 @@ func (t *Terminal) vmove(o int, allowCycle bool) bool {
} }
} }
} }
return t.vset(dest) t.vset(dest)
} }
func (t *Terminal) vset(o int) bool { func (t *Terminal) vset(o int) bool {
@@ -7326,9 +7041,9 @@ func (t *Terminal) dumpStatus(params getParams) string {
selected[i] = t.dumpItem(selectedItems[i+params.offset].item) selected[i] = t.dumpItem(selectedItems[i+params.offset].item)
} }
matches := make([]StatusItem, util.Max(0, util.Min(params.limit, t.resultMerger.Length()-params.offset))) matches := make([]StatusItem, util.Max(0, util.Min(params.limit, t.merger.Length()-params.offset)))
for i := range matches { for i := range matches {
matches[i] = t.dumpItem(t.resultMerger.Get(i + params.offset).item) matches[i] = t.dumpItem(t.merger.Get(i + params.offset).item)
} }
var current *StatusItem var current *StatusItem
@@ -7345,7 +7060,7 @@ func (t *Terminal) dumpStatus(params getParams) string {
Position: t.cy, Position: t.cy,
Sort: t.sort, Sort: t.sort,
TotalCount: t.count, TotalCount: t.count,
MatchCount: t.resultMerger.Length(), MatchCount: t.merger.Length(),
Current: current, Current: current,
Matches: matches, Matches: matches,
Selected: selected, Selected: selected,

View File

@@ -2,7 +2,30 @@
package tui package tui
type Attr int32
func HasFullscreenRenderer() bool {
return false
}
var DefaultBorderShape = BorderRounded
func (a Attr) Merge(b Attr) Attr {
if b&AttrRegular > 0 {
// Only keep bold attribute set by the system
return (b &^ AttrRegular) | (a & BoldForce)
}
return (a &^ AttrRegular) | b
}
const ( const (
AttrUndefined = Attr(0)
AttrRegular = Attr(1 << 8)
AttrClear = Attr(1 << 9)
BoldForce = Attr(1 << 10)
FullBg = Attr(1 << 11)
Bold = Attr(1) Bold = Attr(1)
Dim = Attr(1 << 1) Dim = Attr(1 << 1)
Italic = Attr(1 << 2) Italic = Attr(1 << 2)
@@ -13,12 +36,6 @@ const (
StrikeThrough = Attr(1 << 7) StrikeThrough = Attr(1 << 7)
) )
func HasFullscreenRenderer() bool {
return false
}
var DefaultBorderShape = BorderRounded
func (r *FullscreenRenderer) Init() error { return nil } func (r *FullscreenRenderer) Init() error { return nil }
func (r *FullscreenRenderer) DefaultTheme() *ColorTheme { return nil } func (r *FullscreenRenderer) DefaultTheme() *ColorTheme { return nil }
func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {} func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}

View File

@@ -16,7 +16,7 @@ func _() {
_ = x[CtrlE-5] _ = x[CtrlE-5]
_ = x[CtrlF-6] _ = x[CtrlF-6]
_ = x[CtrlG-7] _ = x[CtrlG-7]
_ = x[CtrlBackspace-8] _ = x[CtrlH-8]
_ = x[Tab-9] _ = x[Tab-9]
_ = x[CtrlJ-10] _ = x[CtrlJ-10]
_ = x[CtrlK-11] _ = x[CtrlK-11]
@@ -99,74 +99,75 @@ func _() {
_ = x[CtrlRight-88] _ = x[CtrlRight-88]
_ = x[CtrlHome-89] _ = x[CtrlHome-89]
_ = x[CtrlEnd-90] _ = x[CtrlEnd-90]
_ = x[CtrlDelete-91] _ = x[CtrlBackspace-91]
_ = x[CtrlPageUp-92] _ = x[CtrlDelete-92]
_ = x[CtrlPageDown-93] _ = x[CtrlPageUp-93]
_ = x[Alt-94] _ = x[CtrlPageDown-94]
_ = x[CtrlAlt-95] _ = x[Alt-95]
_ = x[CtrlAltUp-96] _ = x[CtrlAlt-96]
_ = x[CtrlAltDown-97] _ = x[CtrlAltUp-97]
_ = x[CtrlAltLeft-98] _ = x[CtrlAltDown-98]
_ = x[CtrlAltRight-99] _ = x[CtrlAltLeft-99]
_ = x[CtrlAltHome-100] _ = x[CtrlAltRight-100]
_ = x[CtrlAltEnd-101] _ = x[CtrlAltHome-101]
_ = x[CtrlAltBackspace-102] _ = x[CtrlAltEnd-102]
_ = x[CtrlAltDelete-103] _ = x[CtrlAltBackspace-103]
_ = x[CtrlAltPageUp-104] _ = x[CtrlAltDelete-104]
_ = x[CtrlAltPageDown-105] _ = x[CtrlAltPageUp-105]
_ = x[CtrlShiftUp-106] _ = x[CtrlAltPageDown-106]
_ = x[CtrlShiftDown-107] _ = x[CtrlShiftUp-107]
_ = x[CtrlShiftLeft-108] _ = x[CtrlShiftDown-108]
_ = x[CtrlShiftRight-109] _ = x[CtrlShiftLeft-109]
_ = x[CtrlShiftHome-110] _ = x[CtrlShiftRight-110]
_ = x[CtrlShiftEnd-111] _ = x[CtrlShiftHome-111]
_ = x[CtrlShiftDelete-112] _ = x[CtrlShiftEnd-112]
_ = x[CtrlShiftPageUp-113] _ = x[CtrlShiftDelete-113]
_ = x[CtrlShiftPageDown-114] _ = x[CtrlShiftPageUp-114]
_ = x[CtrlAltShiftUp-115] _ = x[CtrlShiftPageDown-115]
_ = x[CtrlAltShiftDown-116] _ = x[CtrlAltShiftUp-116]
_ = x[CtrlAltShiftLeft-117] _ = x[CtrlAltShiftDown-117]
_ = x[CtrlAltShiftRight-118] _ = x[CtrlAltShiftLeft-118]
_ = x[CtrlAltShiftHome-119] _ = x[CtrlAltShiftRight-119]
_ = x[CtrlAltShiftEnd-120] _ = x[CtrlAltShiftHome-120]
_ = x[CtrlAltShiftDelete-121] _ = x[CtrlAltShiftEnd-121]
_ = x[CtrlAltShiftPageUp-122] _ = x[CtrlAltShiftDelete-122]
_ = x[CtrlAltShiftPageDown-123] _ = x[CtrlAltShiftPageUp-123]
_ = x[Invalid-124] _ = x[CtrlAltShiftPageDown-124]
_ = x[Fatal-125] _ = x[Invalid-125]
_ = x[BracketedPasteBegin-126] _ = x[Fatal-126]
_ = x[BracketedPasteEnd-127] _ = x[BracketedPasteBegin-127]
_ = x[Mouse-128] _ = x[BracketedPasteEnd-128]
_ = x[DoubleClick-129] _ = x[Mouse-129]
_ = x[LeftClick-130] _ = x[DoubleClick-130]
_ = x[RightClick-131] _ = x[LeftClick-131]
_ = x[SLeftClick-132] _ = x[RightClick-132]
_ = x[SRightClick-133] _ = x[SLeftClick-133]
_ = x[ScrollUp-134] _ = x[SRightClick-134]
_ = x[ScrollDown-135] _ = x[ScrollUp-135]
_ = x[SScrollUp-136] _ = x[ScrollDown-136]
_ = x[SScrollDown-137] _ = x[SScrollUp-137]
_ = x[PreviewScrollUp-138] _ = x[SScrollDown-138]
_ = x[PreviewScrollDown-139] _ = x[PreviewScrollUp-139]
_ = x[Resize-140] _ = x[PreviewScrollDown-140]
_ = x[Change-141] _ = x[Resize-141]
_ = x[BackwardEOF-142] _ = x[Change-142]
_ = x[Start-143] _ = x[BackwardEOF-143]
_ = x[Load-144] _ = x[Start-144]
_ = x[Focus-145] _ = x[Load-145]
_ = x[One-146] _ = x[Focus-146]
_ = x[Zero-147] _ = x[One-147]
_ = x[Result-148] _ = x[Zero-148]
_ = x[Jump-149] _ = x[Result-149]
_ = x[JumpCancel-150] _ = x[Jump-150]
_ = x[ClickHeader-151] _ = x[JumpCancel-151]
_ = x[ClickFooter-152] _ = x[ClickHeader-152]
_ = x[Multi-153] _ = x[ClickFooter-153]
_ = x[Multi-154]
} }
const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlBackspaceTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteShiftHomeShiftEndShiftPageUpShiftPageDownF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltDeleteAltHomeAltEndAltPageUpAltPageDownAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltShiftDeleteAltShiftHomeAltShiftEndAltShiftPageUpAltShiftPageDownCtrlUpCtrlDownCtrlLeftCtrlRightCtrlHomeCtrlEndCtrlDeleteCtrlPageUpCtrlPageDownAltCtrlAltCtrlAltUpCtrlAltDownCtrlAltLeftCtrlAltRightCtrlAltHomeCtrlAltEndCtrlAltBackspaceCtrlAltDeleteCtrlAltPageUpCtrlAltPageDownCtrlShiftUpCtrlShiftDownCtrlShiftLeftCtrlShiftRightCtrlShiftHomeCtrlShiftEndCtrlShiftDeleteCtrlShiftPageUpCtrlShiftPageDownCtrlAltShiftUpCtrlAltShiftDownCtrlAltShiftLeftCtrlAltShiftRightCtrlAltShiftHomeCtrlAltShiftEndCtrlAltShiftDeleteCtrlAltShiftPageUpCtrlAltShiftPageDownInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderClickFooterMulti" const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteShiftHomeShiftEndShiftPageUpShiftPageDownF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltDeleteAltHomeAltEndAltPageUpAltPageDownAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltShiftDeleteAltShiftHomeAltShiftEndAltShiftPageUpAltShiftPageDownCtrlUpCtrlDownCtrlLeftCtrlRightCtrlHomeCtrlEndCtrlBackspaceCtrlDeleteCtrlPageUpCtrlPageDownAltCtrlAltCtrlAltUpCtrlAltDownCtrlAltLeftCtrlAltRightCtrlAltHomeCtrlAltEndCtrlAltBackspaceCtrlAltDeleteCtrlAltPageUpCtrlAltPageDownCtrlShiftUpCtrlShiftDownCtrlShiftLeftCtrlShiftRightCtrlShiftHomeCtrlShiftEndCtrlShiftDeleteCtrlShiftPageUpCtrlShiftPageDownCtrlAltShiftUpCtrlAltShiftDownCtrlAltShiftLeftCtrlAltShiftRightCtrlAltShiftHomeCtrlAltShiftEndCtrlAltShiftDeleteCtrlAltShiftPageUpCtrlAltShiftPageDownInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderClickFooterMulti"
var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 52, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 143, 152, 165, 181, 190, 199, 207, 216, 222, 228, 236, 238, 242, 246, 251, 255, 258, 264, 271, 280, 289, 299, 310, 319, 327, 338, 351, 353, 355, 357, 359, 361, 363, 365, 367, 369, 372, 375, 378, 390, 395, 402, 409, 417, 426, 433, 439, 448, 459, 469, 481, 493, 506, 520, 532, 543, 557, 573, 579, 587, 595, 604, 612, 619, 629, 639, 651, 654, 661, 670, 681, 692, 704, 715, 725, 741, 754, 767, 782, 793, 806, 819, 833, 846, 858, 873, 888, 905, 919, 935, 951, 968, 984, 999, 1017, 1035, 1055, 1062, 1067, 1086, 1103, 1108, 1119, 1128, 1138, 1148, 1159, 1167, 1177, 1186, 1197, 1212, 1229, 1235, 1241, 1252, 1257, 1261, 1266, 1269, 1273, 1279, 1283, 1293, 1304, 1315, 1320} var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 157, 173, 182, 191, 199, 208, 214, 220, 228, 230, 234, 238, 243, 247, 250, 256, 263, 272, 281, 291, 302, 311, 319, 330, 343, 345, 347, 349, 351, 353, 355, 357, 359, 361, 364, 367, 370, 382, 387, 394, 401, 409, 418, 425, 431, 440, 451, 461, 473, 485, 498, 512, 524, 535, 549, 565, 571, 579, 587, 596, 604, 611, 624, 634, 644, 656, 659, 666, 675, 686, 697, 709, 720, 730, 746, 759, 772, 787, 798, 811, 824, 838, 851, 863, 878, 893, 910, 924, 940, 956, 973, 989, 1004, 1022, 1040, 1060, 1067, 1072, 1091, 1108, 1113, 1124, 1133, 1143, 1153, 1164, 1172, 1182, 1191, 1202, 1217, 1234, 1240, 1246, 1257, 1262, 1266, 1271, 1274, 1278, 1284, 1288, 1298, 1309, 1320, 1325}
func (i EventType) String() string { func (i EventType) String() string {
if i < 0 || i >= EventType(len(_EventType_index)-1) { if i < 0 || i >= EventType(len(_EventType_index)-1) {

View File

@@ -479,7 +479,6 @@ func (r *LightRenderer) escSequence(sz *int) Event {
return Event{Delete, 0, nil} return Event{Delete, 0, nil}
} }
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' { if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
*sz = 7
switch r.buffer[5] { switch r.buffer[5] {
case '0': case '0':
return Event{AltShiftDelete, 0, nil} return Event{AltShiftDelete, 0, nil}
@@ -526,7 +525,6 @@ func (r *LightRenderer) escSequence(sz *int) Event {
return Event{PageUp, 0, nil} return Event{PageUp, 0, nil}
} }
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' { if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
*sz = 7
switch r.buffer[5] { switch r.buffer[5] {
case '0': case '0':
return Event{AltShiftPageUp, 0, nil} return Event{AltShiftPageUp, 0, nil}
@@ -571,7 +569,6 @@ func (r *LightRenderer) escSequence(sz *int) Event {
return Event{PageDown, 0, nil} return Event{PageDown, 0, nil}
} }
if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' { if len(r.buffer) == 7 && r.buffer[6] == '~' && r.buffer[4] == '1' {
*sz = 7
switch r.buffer[5] { switch r.buffer[5] {
case '0': case '0':
return Event{AltShiftPageDown, 0, nil} return Event{AltShiftPageDown, 0, nil}

View File

@@ -36,6 +36,8 @@ func (p ColorPair) style() tcell.Style {
return style.Foreground(asTcellColor(p.Fg())).Background(asTcellColor(p.Bg())) return style.Foreground(asTcellColor(p.Fg())).Background(asTcellColor(p.Bg()))
} }
type Attr int32
type TcellWindow struct { type TcellWindow struct {
color bool color bool
windowType WindowType windowType WindowType
@@ -96,6 +98,14 @@ const (
Italic = Attr(tcell.AttrItalic) Italic = Attr(tcell.AttrItalic)
) )
const (
AttrUndefined = Attr(0)
AttrRegular = Attr(1 << 7)
AttrClear = Attr(1 << 8)
BoldForce = Attr(1 << 10)
FullBg = Attr(1 << 11)
)
func (r *FullscreenRenderer) Bell() { func (r *FullscreenRenderer) Bell() {
_screen.Beep() _screen.Beep()
} }
@@ -149,6 +159,15 @@ func (c Color) Style() tcell.Color {
} }
} }
func (a Attr) Merge(b Attr) Attr {
if b&AttrRegular > 0 {
// Only keep bold attribute set by the system
return (b &^ AttrRegular) | (a & BoldForce)
}
return (a &^ AttrRegular) | b
}
// handle the following as private members of FullscreenRenderer instance // handle the following as private members of FullscreenRenderer instance
// they are declared here to prevent introducing tcell library in non-windows builds // they are declared here to prevent introducing tcell library in non-windows builds
var ( var (
@@ -371,10 +390,12 @@ func (r *FullscreenRenderer) GetChar() Event {
} }
case rune(tcell.KeyCtrlH): case rune(tcell.KeyCtrlH):
switch { switch {
case ctrl:
return keyfn('h')
case alt: case alt:
return Event{AltBackspace, 0, nil} return Event{AltBackspace, 0, nil}
case ctrl, none, shift: case none, shift:
return keyfn('h') return Event{Backspace, 0, nil}
} }
} }
case tcell.KeyCtrlI: case tcell.KeyCtrlI:

View File

@@ -110,21 +110,21 @@ func TestGetCharEventKey(t *testing.T) {
{giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{AltDelete, 0, nil}}, {giveKey{tcell.KeyDelete, 0, tcell.ModAlt}, wantKey{AltDelete, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, {giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, {giveKey{tcell.KeyBackspace, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}},
{giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{CtrlBackspace, 0, nil}}, // actual "Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{Backspace, 0, nil}}, // actual "Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke
{giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke {giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // fabricated "Ctrl+Alt+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{AltBackspace, 0, nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right) // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}}, {giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},

View File

@@ -8,26 +8,6 @@ import (
"github.com/rivo/uniseg" "github.com/rivo/uniseg"
) )
type Attr int32
const (
AttrUndefined = Attr(0)
AttrRegular = Attr(1 << 8)
AttrClear = Attr(1 << 9)
BoldForce = Attr(1 << 10)
FullBg = Attr(1 << 11)
Strip = Attr(1 << 12)
)
func (a Attr) Merge(b Attr) Attr {
if b&AttrRegular > 0 {
// Only keep bold attribute set by the system
return (b &^ AttrRegular) | (a & BoldForce)
}
return (a &^ AttrRegular) | b
}
// Types of user action // Types of user action
// //
//go:generate stringer -type=EventType //go:generate stringer -type=EventType
@@ -43,7 +23,7 @@ const (
CtrlE CtrlE
CtrlF CtrlF
CtrlG CtrlG
CtrlBackspace CtrlH
Tab Tab
CtrlJ CtrlJ
CtrlK CtrlK
@@ -137,6 +117,7 @@ const (
CtrlRight CtrlRight
CtrlHome CtrlHome
CtrlEnd CtrlEnd
CtrlBackspace
CtrlDelete CtrlDelete
CtrlPageUp CtrlPageUp
CtrlPageDown CtrlPageDown
@@ -294,14 +275,6 @@ func (a ColorAttr) IsColorDefined() bool {
return a.Color != colUndefined return a.Color != colUndefined
} }
func (a ColorAttr) IsAttrDefined() bool {
return a.Attr&^BoldForce != AttrUndefined
}
func (a ColorAttr) IsUndefined() bool {
return !a.IsColorDefined() && !a.IsAttrDefined()
}
func NewColorAttr() ColorAttr { func NewColorAttr() ColorAttr {
return ColorAttr{Color: colUndefined, Attr: AttrUndefined} return ColorAttr{Color: colUndefined, Attr: AttrUndefined}
} }
@@ -385,10 +358,6 @@ func (p ColorPair) IsFullBgMarker() bool {
return p.attr&FullBg > 0 return p.attr&FullBg > 0
} }
func (p ColorPair) ShouldStripColors() bool {
return p.attr&Strip > 0
}
func (p ColorPair) HasBg() bool { func (p ColorPair) HasBg() bool {
return p.attr&Reverse == 0 && p.bg != colDefault || return p.attr&Reverse == 0 && p.bg != colDefault ||
p.attr&Reverse > 0 && p.fg != colDefault p.attr&Reverse > 0 && p.fg != colDefault
@@ -412,12 +381,6 @@ func (p ColorPair) WithAttr(attr Attr) ColorPair {
return dup return dup
} }
func (p ColorPair) WithFg(fg ColorAttr) ColorPair {
dup := p
fgPair := ColorPair{fg.Color, colUndefined, fg.Attr}
return dup.Merge(fgPair)
}
func (p ColorPair) WithBg(bg ColorAttr) ColorPair { func (p ColorPair) WithBg(bg ColorAttr) ColorPair {
dup := p dup := p
bgPair := ColorPair{colUndefined, bg.Color, bg.Attr} bgPair := ColorPair{colUndefined, bg.Color, bg.Attr}
@@ -447,7 +410,6 @@ type ColorTheme struct {
ListBg ColorAttr ListBg ColorAttr
AltBg ColorAttr AltBg ColorAttr
Nth ColorAttr Nth ColorAttr
Nomatch ColorAttr
SelectedFg ColorAttr SelectedFg ColorAttr
SelectedBg ColorAttr SelectedBg ColorAttr
SelectedMatch ColorAttr SelectedMatch ColorAttr
@@ -810,11 +772,9 @@ func NewFullscreenRenderer(theme *ColorTheme, forceBlack bool, mouse bool) Rende
} }
var ( var (
NoColorTheme *ColorTheme Default16 *ColorTheme
EmptyTheme *ColorTheme Dark256 *ColorTheme
Default16 *ColorTheme Light256 *ColorTheme
Dark256 *ColorTheme
Light256 *ColorTheme
ColPrompt ColorPair ColPrompt ColorPair
ColNormal ColorPair ColNormal ColorPair
@@ -858,120 +818,119 @@ var (
ColInputLabel ColorPair ColInputLabel ColorPair
) )
func init() { func EmptyTheme() *ColorTheme {
defaultColor := ColorAttr{colDefault, AttrUndefined} return &ColorTheme{
undefined := ColorAttr{colUndefined, AttrUndefined}
NoColorTheme = &ColorTheme{
Colored: false,
Input: defaultColor,
Fg: defaultColor,
Bg: defaultColor,
ListFg: defaultColor,
ListBg: defaultColor,
AltBg: undefined,
SelectedFg: defaultColor,
SelectedBg: defaultColor,
SelectedMatch: defaultColor,
DarkBg: defaultColor,
Prompt: defaultColor,
Match: defaultColor,
Current: undefined,
CurrentMatch: undefined,
Spinner: defaultColor,
Info: defaultColor,
Cursor: defaultColor,
Marker: defaultColor,
Header: defaultColor,
Border: undefined,
BorderLabel: defaultColor,
Ghost: undefined,
Disabled: defaultColor,
PreviewFg: defaultColor,
PreviewBg: defaultColor,
Gutter: undefined,
PreviewBorder: defaultColor,
PreviewScrollbar: defaultColor,
PreviewLabel: defaultColor,
ListLabel: defaultColor,
ListBorder: defaultColor,
Separator: defaultColor,
Scrollbar: defaultColor,
InputBg: defaultColor,
InputBorder: defaultColor,
InputLabel: defaultColor,
HeaderBg: defaultColor,
HeaderBorder: defaultColor,
HeaderLabel: defaultColor,
FooterBg: defaultColor,
FooterBorder: defaultColor,
FooterLabel: defaultColor,
GapLine: defaultColor,
Nth: undefined,
Nomatch: undefined,
}
EmptyTheme = &ColorTheme{
Colored: true, Colored: true,
Input: undefined, Input: ColorAttr{colUndefined, AttrUndefined},
Fg: undefined, Fg: ColorAttr{colUndefined, AttrUndefined},
Bg: undefined, Bg: ColorAttr{colUndefined, AttrUndefined},
ListFg: undefined, ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: undefined, ListBg: ColorAttr{colUndefined, AttrUndefined},
AltBg: undefined, AltBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: undefined, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: undefined, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: undefined, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
DarkBg: undefined, DarkBg: ColorAttr{colUndefined, AttrUndefined},
Prompt: undefined, Prompt: ColorAttr{colUndefined, AttrUndefined},
Match: undefined, Match: ColorAttr{colUndefined, AttrUndefined},
Current: undefined, Current: ColorAttr{colUndefined, AttrUndefined},
CurrentMatch: undefined, CurrentMatch: ColorAttr{colUndefined, AttrUndefined},
Spinner: undefined, Spinner: ColorAttr{colUndefined, AttrUndefined},
Info: undefined, Info: ColorAttr{colUndefined, AttrUndefined},
Cursor: undefined, Cursor: ColorAttr{colUndefined, AttrUndefined},
Marker: undefined, Marker: ColorAttr{colUndefined, AttrUndefined},
Header: undefined, Header: ColorAttr{colUndefined, AttrUndefined},
Footer: undefined, Footer: ColorAttr{colUndefined, AttrUndefined},
Border: undefined, Border: ColorAttr{colUndefined, AttrUndefined},
BorderLabel: undefined, BorderLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: undefined, ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: undefined, ListBorder: ColorAttr{colUndefined, AttrUndefined},
Ghost: undefined, Ghost: ColorAttr{colUndefined, Dim},
Disabled: undefined, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: undefined, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: undefined, PreviewBg: ColorAttr{colUndefined, AttrUndefined},
Gutter: undefined, Gutter: ColorAttr{colUndefined, AttrUndefined},
PreviewBorder: undefined, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: undefined, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: undefined, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
Separator: undefined, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: undefined, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: undefined, InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: undefined, InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: undefined, InputLabel: ColorAttr{colUndefined, AttrUndefined},
HeaderBg: undefined, HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: undefined, HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: undefined, HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
FooterBg: undefined, FooterBg: ColorAttr{colUndefined, AttrUndefined},
FooterBorder: undefined, FooterBorder: ColorAttr{colUndefined, AttrUndefined},
FooterLabel: undefined, FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: undefined, GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: undefined, Nth: ColorAttr{colUndefined, AttrUndefined},
Nomatch: undefined,
} }
}
func NoColorTheme() *ColorTheme {
return &ColorTheme{
Colored: false,
Input: ColorAttr{colDefault, AttrUndefined},
Fg: ColorAttr{colDefault, AttrUndefined},
Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: ColorAttr{colDefault, AttrUndefined},
ListBg: ColorAttr{colDefault, AttrUndefined},
AltBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: ColorAttr{colDefault, AttrUndefined},
SelectedBg: ColorAttr{colDefault, AttrUndefined},
SelectedMatch: ColorAttr{colDefault, AttrUndefined},
DarkBg: ColorAttr{colDefault, AttrUndefined},
Prompt: ColorAttr{colDefault, AttrUndefined},
Match: ColorAttr{colDefault, Underline},
Current: ColorAttr{colDefault, Reverse},
CurrentMatch: ColorAttr{colDefault, Reverse | Underline},
Spinner: ColorAttr{colDefault, AttrUndefined},
Info: ColorAttr{colDefault, AttrUndefined},
Cursor: ColorAttr{colDefault, AttrUndefined},
Marker: ColorAttr{colDefault, AttrUndefined},
Header: ColorAttr{colDefault, AttrUndefined},
Border: ColorAttr{colDefault, AttrUndefined},
BorderLabel: ColorAttr{colDefault, AttrUndefined},
Ghost: ColorAttr{colDefault, Dim},
Disabled: ColorAttr{colDefault, AttrUndefined},
PreviewFg: ColorAttr{colDefault, AttrUndefined},
PreviewBg: ColorAttr{colDefault, AttrUndefined},
Gutter: ColorAttr{colDefault, AttrUndefined},
PreviewBorder: ColorAttr{colDefault, AttrUndefined},
PreviewScrollbar: ColorAttr{colDefault, AttrUndefined},
PreviewLabel: ColorAttr{colDefault, AttrUndefined},
ListLabel: ColorAttr{colDefault, AttrUndefined},
ListBorder: ColorAttr{colDefault, AttrUndefined},
Separator: ColorAttr{colDefault, AttrUndefined},
Scrollbar: ColorAttr{colDefault, AttrUndefined},
InputBg: ColorAttr{colDefault, AttrUndefined},
InputBorder: ColorAttr{colDefault, AttrUndefined},
InputLabel: ColorAttr{colDefault, AttrUndefined},
HeaderBg: ColorAttr{colDefault, AttrUndefined},
HeaderBorder: ColorAttr{colDefault, AttrUndefined},
HeaderLabel: ColorAttr{colDefault, AttrUndefined},
FooterBg: ColorAttr{colDefault, AttrUndefined},
FooterBorder: ColorAttr{colDefault, AttrUndefined},
FooterLabel: ColorAttr{colDefault, AttrUndefined},
GapLine: ColorAttr{colDefault, AttrUndefined},
Nth: ColorAttr{colUndefined, AttrUndefined},
}
}
func init() {
Default16 = &ColorTheme{ Default16 = &ColorTheme{
Colored: true, Colored: true,
Input: defaultColor, Input: ColorAttr{colDefault, AttrUndefined},
Fg: defaultColor, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: defaultColor, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: undefined, ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: undefined, ListBg: ColorAttr{colUndefined, AttrUndefined},
AltBg: undefined, AltBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: undefined, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: undefined, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: undefined, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{colGrey, AttrUndefined}, DarkBg: ColorAttr{colGrey, AttrUndefined},
Prompt: ColorAttr{colBlue, AttrUndefined}, Prompt: ColorAttr{colBlue, AttrUndefined},
Match: ColorAttr{colGreen, AttrUndefined}, Match: ColorAttr{colGreen, AttrUndefined},
@@ -983,45 +942,43 @@ func init() {
Marker: ColorAttr{colMagenta, AttrUndefined}, Marker: ColorAttr{colMagenta, AttrUndefined},
Header: ColorAttr{colCyan, AttrUndefined}, Header: ColorAttr{colCyan, AttrUndefined},
Footer: ColorAttr{colCyan, AttrUndefined}, Footer: ColorAttr{colCyan, AttrUndefined},
Border: undefined, Border: ColorAttr{colDefault, Dim},
BorderLabel: defaultColor, BorderLabel: ColorAttr{colDefault, AttrUndefined},
Ghost: undefined, Ghost: ColorAttr{colUndefined, Dim},
Disabled: undefined, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: undefined, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: undefined, PreviewBg: ColorAttr{colUndefined, AttrUndefined},
Gutter: undefined, Gutter: ColorAttr{colUndefined, AttrUndefined},
PreviewBorder: undefined, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: undefined, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: undefined, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: undefined, ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: undefined, ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: undefined, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: undefined, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: undefined, InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: undefined, InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: undefined, InputLabel: ColorAttr{colUndefined, AttrUndefined},
HeaderBg: undefined, HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: undefined, HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: undefined, HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
FooterBg: undefined, FooterBg: ColorAttr{colUndefined, AttrUndefined},
FooterBorder: undefined, FooterBorder: ColorAttr{colUndefined, AttrUndefined},
FooterLabel: undefined, FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: undefined, GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: undefined, Nth: ColorAttr{colUndefined, AttrUndefined},
Nomatch: undefined,
} }
Dark256 = &ColorTheme{ Dark256 = &ColorTheme{
Colored: true, Colored: true,
Input: defaultColor, Input: ColorAttr{colDefault, AttrUndefined},
Fg: defaultColor, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: defaultColor, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: undefined, ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: undefined, ListBg: ColorAttr{colUndefined, AttrUndefined},
AltBg: undefined, AltBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: undefined, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: undefined, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: undefined, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{236, AttrUndefined}, DarkBg: ColorAttr{236, AttrUndefined},
Prompt: ColorAttr{110, AttrUndefined}, Prompt: ColorAttr{110, AttrUndefined},
Match: ColorAttr{108, AttrUndefined}, Match: ColorAttr{108, AttrUndefined},
@@ -1035,43 +992,41 @@ func init() {
Footer: ColorAttr{109, AttrUndefined}, Footer: ColorAttr{109, AttrUndefined},
Border: ColorAttr{59, AttrUndefined}, Border: ColorAttr{59, AttrUndefined},
BorderLabel: ColorAttr{145, AttrUndefined}, BorderLabel: ColorAttr{145, AttrUndefined},
Ghost: undefined, Ghost: ColorAttr{colUndefined, Dim},
Disabled: undefined, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: undefined, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: undefined, PreviewBg: ColorAttr{colUndefined, AttrUndefined},
Gutter: undefined, Gutter: ColorAttr{colUndefined, AttrUndefined},
PreviewBorder: undefined, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: undefined, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: undefined, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: undefined, ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: undefined, ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: undefined, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: undefined, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: undefined, InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: undefined, InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: undefined, InputLabel: ColorAttr{colUndefined, AttrUndefined},
HeaderBg: undefined, HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: undefined, HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: undefined, HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
FooterBg: undefined, FooterBg: ColorAttr{colUndefined, AttrUndefined},
FooterBorder: undefined, FooterBorder: ColorAttr{colUndefined, AttrUndefined},
FooterLabel: undefined, FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: undefined, GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: undefined, Nth: ColorAttr{colUndefined, AttrUndefined},
Nomatch: undefined,
} }
Light256 = &ColorTheme{ Light256 = &ColorTheme{
Colored: true, Colored: true,
Input: defaultColor, Input: ColorAttr{colDefault, AttrUndefined},
Fg: defaultColor, Fg: ColorAttr{colDefault, AttrUndefined},
Bg: defaultColor, Bg: ColorAttr{colDefault, AttrUndefined},
ListFg: undefined, ListFg: ColorAttr{colUndefined, AttrUndefined},
ListBg: undefined, ListBg: ColorAttr{colUndefined, AttrUndefined},
AltBg: undefined, AltBg: ColorAttr{colUndefined, AttrUndefined},
SelectedFg: undefined, SelectedFg: ColorAttr{colUndefined, AttrUndefined},
SelectedBg: undefined, SelectedBg: ColorAttr{colUndefined, AttrUndefined},
SelectedMatch: undefined, SelectedMatch: ColorAttr{colUndefined, AttrUndefined},
DarkBg: ColorAttr{251, AttrUndefined}, DarkBg: ColorAttr{251, AttrUndefined},
Prompt: ColorAttr{25, AttrUndefined}, Prompt: ColorAttr{25, AttrUndefined},
Match: ColorAttr{66, AttrUndefined}, Match: ColorAttr{66, AttrUndefined},
@@ -1085,54 +1040,37 @@ func init() {
Footer: ColorAttr{31, AttrUndefined}, Footer: ColorAttr{31, AttrUndefined},
Border: ColorAttr{145, AttrUndefined}, Border: ColorAttr{145, AttrUndefined},
BorderLabel: ColorAttr{59, AttrUndefined}, BorderLabel: ColorAttr{59, AttrUndefined},
Ghost: undefined, Ghost: ColorAttr{colUndefined, Dim},
Disabled: undefined, Disabled: ColorAttr{colUndefined, AttrUndefined},
PreviewFg: undefined, PreviewFg: ColorAttr{colUndefined, AttrUndefined},
PreviewBg: undefined, PreviewBg: ColorAttr{colUndefined, AttrUndefined},
Gutter: undefined, Gutter: ColorAttr{colUndefined, AttrUndefined},
PreviewBorder: undefined, PreviewBorder: ColorAttr{colUndefined, AttrUndefined},
PreviewScrollbar: undefined, PreviewScrollbar: ColorAttr{colUndefined, AttrUndefined},
PreviewLabel: undefined, PreviewLabel: ColorAttr{colUndefined, AttrUndefined},
ListLabel: undefined, ListLabel: ColorAttr{colUndefined, AttrUndefined},
ListBorder: undefined, ListBorder: ColorAttr{colUndefined, AttrUndefined},
Separator: undefined, Separator: ColorAttr{colUndefined, AttrUndefined},
Scrollbar: undefined, Scrollbar: ColorAttr{colUndefined, AttrUndefined},
InputBg: undefined, InputBg: ColorAttr{colUndefined, AttrUndefined},
InputBorder: undefined, InputBorder: ColorAttr{colUndefined, AttrUndefined},
InputLabel: undefined, InputLabel: ColorAttr{colUndefined, AttrUndefined},
HeaderBg: undefined, HeaderBg: ColorAttr{colUndefined, AttrUndefined},
HeaderBorder: undefined, HeaderBorder: ColorAttr{colUndefined, AttrUndefined},
HeaderLabel: undefined, HeaderLabel: ColorAttr{colUndefined, AttrUndefined},
FooterBg: undefined, FooterBg: ColorAttr{colUndefined, AttrUndefined},
FooterBorder: undefined, FooterBorder: ColorAttr{colUndefined, AttrUndefined},
FooterLabel: undefined, FooterLabel: ColorAttr{colUndefined, AttrUndefined},
GapLine: undefined, GapLine: ColorAttr{colUndefined, AttrUndefined},
Nth: undefined, Nth: ColorAttr{colUndefined, AttrUndefined},
Nomatch: undefined,
} }
} }
func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlack bool, hasInputWindow bool, hasHeaderWindow bool) { func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool, hasInputWindow bool, hasHeaderWindow bool) {
if forceBlack { if forceBlack {
theme.Bg = ColorAttr{colBlack, AttrUndefined} theme.Bg = ColorAttr{colBlack, AttrUndefined}
} }
if boldify {
boldify := func(c ColorAttr) ColorAttr {
dup := c
if (c.Attr & AttrRegular) == 0 {
dup.Attr |= BoldForce
}
return dup
}
theme.Current = boldify(theme.Current)
theme.CurrentMatch = boldify(theme.CurrentMatch)
theme.Prompt = boldify(theme.Prompt)
theme.Input = boldify(theme.Input)
theme.Cursor = boldify(theme.Cursor)
theme.Spinner = boldify(theme.Spinner)
}
o := func(a ColorAttr, b ColorAttr) ColorAttr { o := func(a ColorAttr, b ColorAttr) ColorAttr {
c := a c := a
if b.Color != colUndefined { if b.Color != colUndefined {
@@ -1148,36 +1086,18 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlac
theme.Bg = o(baseTheme.Bg, theme.Bg) theme.Bg = o(baseTheme.Bg, theme.Bg)
theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg) theme.DarkBg = o(baseTheme.DarkBg, theme.DarkBg)
theme.Prompt = o(baseTheme.Prompt, theme.Prompt) theme.Prompt = o(baseTheme.Prompt, theme.Prompt)
match := theme.Match theme.Match = o(baseTheme.Match, theme.Match)
if !baseTheme.Colored && match.IsUndefined() {
match.Attr = Underline
}
theme.Match = o(baseTheme.Match, match)
// Inherit from 'fg', so that we don't have to write 'current-fg:dim' // Inherit from 'fg', so that we don't have to write 'current-fg:dim'
// e.g. fzf --delimiter / --nth -1 --color fg:dim,nth:regular // e.g. fzf --delimiter / --nth -1 --color fg:dim,nth:regular
current := theme.Current theme.Current = theme.Fg.Merge(o(baseTheme.Current, theme.Current))
if !baseTheme.Colored && current.IsUndefined() { theme.CurrentMatch = o(baseTheme.CurrentMatch, theme.CurrentMatch)
current.Attr |= Reverse
}
theme.Current = theme.Fg.Merge(o(baseTheme.Current, current))
currentMatch := theme.CurrentMatch
if !baseTheme.Colored && currentMatch.IsUndefined() {
currentMatch.Attr |= Reverse | Underline
}
theme.CurrentMatch = o(baseTheme.CurrentMatch, currentMatch)
theme.Spinner = o(baseTheme.Spinner, theme.Spinner) theme.Spinner = o(baseTheme.Spinner, theme.Spinner)
theme.Info = o(baseTheme.Info, theme.Info) theme.Info = o(baseTheme.Info, theme.Info)
theme.Cursor = o(baseTheme.Cursor, theme.Cursor) theme.Cursor = o(baseTheme.Cursor, theme.Cursor)
theme.Marker = o(baseTheme.Marker, theme.Marker) theme.Marker = o(baseTheme.Marker, theme.Marker)
theme.Header = o(baseTheme.Header, theme.Header) theme.Header = o(baseTheme.Header, theme.Header)
theme.Footer = o(baseTheme.Footer, theme.Footer) theme.Footer = o(baseTheme.Footer, theme.Footer)
theme.Border = o(baseTheme.Border, theme.Border)
// If border color is undefined, set it to default color with dim attribute.
border := theme.Border
if baseTheme.Border.IsUndefined() && border.IsUndefined() {
border.Attr = Dim
}
theme.Border = o(baseTheme.Border, border)
theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel) theme.BorderLabel = o(baseTheme.BorderLabel, theme.BorderLabel)
undefined := NewColorAttr() undefined := NewColorAttr()
@@ -1190,23 +1110,9 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlac
theme.SelectedFg = o(theme.ListFg, theme.SelectedFg) theme.SelectedFg = o(theme.ListFg, theme.SelectedFg)
theme.SelectedBg = o(theme.ListBg, theme.SelectedBg) theme.SelectedBg = o(theme.ListBg, theme.SelectedBg)
theme.SelectedMatch = o(theme.Match, theme.SelectedMatch) theme.SelectedMatch = o(theme.Match, theme.SelectedMatch)
theme.Ghost = o(theme.Input, theme.Ghost)
ghost := theme.Ghost
if ghost.IsUndefined() {
ghost.Attr = Dim
} else if ghost.IsColorDefined() && !ghost.IsAttrDefined() {
// Don't want to inherit 'bold' from 'input'
ghost.Attr = AttrRegular
}
theme.Ghost = o(theme.Input, ghost)
theme.Disabled = o(theme.Input, theme.Disabled) theme.Disabled = o(theme.Input, theme.Disabled)
theme.Gutter = o(theme.DarkBg, theme.Gutter)
// Use dim gutter on non-colored themes if undefined
gutter := theme.Gutter
if !baseTheme.Colored && gutter.IsUndefined() {
gutter.Attr = Dim
}
theme.Gutter = o(theme.DarkBg, gutter)
theme.PreviewFg = o(theme.Fg, theme.PreviewFg) theme.PreviewFg = o(theme.Fg, theme.PreviewFg)
theme.PreviewBg = o(theme.Bg, theme.PreviewBg) theme.PreviewBg = o(theme.Bg, theme.PreviewBg)
theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel) theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel)
@@ -1248,10 +1154,6 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlac
theme.FooterBorder = o(theme.Border, theme.FooterBorder) theme.FooterBorder = o(theme.Border, theme.FooterBorder)
theme.FooterLabel = o(theme.BorderLabel, theme.FooterLabel) theme.FooterLabel = o(theme.BorderLabel, theme.FooterLabel)
if theme.Nomatch.IsUndefined() {
theme.Nomatch.Attr = Dim
}
initPalette(theme) initPalette(theme)
} }

View File

@@ -24,7 +24,7 @@ DEFAULT_TIMEOUT = 10
FILE = File.expand_path(__FILE__) FILE = File.expand_path(__FILE__)
BASE = File.expand_path('../..', __dir__) BASE = File.expand_path('../..', __dir__)
Dir.chdir(BASE) Dir.chdir(BASE)
FZF = %(FZF_DEFAULT_OPTS="--no-scrollbar --gutter ' ' --pointer '>' --marker '>'" FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf).freeze FZF = %[FZF_DEFAULT_OPTS="--no-scrollbar --gutter ' ' --pointer '>' --marker '>'" FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf].freeze
def wait(timeout = DEFAULT_TIMEOUT) def wait(timeout = DEFAULT_TIMEOUT)
since = Time.now since = Time.now

View File

@@ -1190,44 +1190,6 @@ class TestCore < TestInteractive
tmux.until { |lines| assert lines.any_include?('9999␊10000') } tmux.until { |lines| assert lines.any_include?('9999␊10000') }
end end
def test_freeze_left_keep_right
tmux.send_keys %[seq 10000 | #{FZF} --read0 --delimiter "\n" --freeze-left 3 --keep-right --ellipsis XX --no-multi-line --bind space:toggle-multi-line], :Enter
tmux.until { |lines| assert_match(/^> 1␊2␊3XX.*10000␊$/, lines[-3]) }
tmux.send_keys '5'
tmux.until { |lines| assert_match(/^> 1␊2␊3␊4␊5␊.*XX$/, lines[-3]) }
tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('> 1') }
tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('1␊2␊3␊4␊5␊') }
end
def test_freeze_left_and_right
tmux.send_keys %[seq 10000 | tr "\n" ' ' | #{FZF} --freeze-left 3 --freeze-right 3 --ellipsis XX], :Enter
tmux.until { |lines| assert_match(/XX9998 9999 10000$/, lines[-3]) }
tmux.send_keys "'1000"
tmux.until { |lines| assert_match(/^> 1 2 3XX.*XX9998 9999 10000$/,lines[-3]) }
end
def test_freeze_right_exceed_range
tmux.send_keys %[seq 10000 | tr "\n" ' ' | #{FZF} --freeze-right 100000 --ellipsis XX], :Enter
['', "'1000"].each do |query|
tmux.send_keys query
tmux.until { |lines| assert lines.any_include?("> #{query}".strip) }
tmux.until do |lines|
assert_match(/ 9998 9999 10000$/, lines[-3])
assert_equal(1, lines[-3].scan('XX').size)
end
end
end
def test_freeze_right_exceed_range_with_freeze_left
tmux.send_keys %[seq 10000 | tr "\n" ' ' | #{FZF} --freeze-left 3 --freeze-right 100000 --ellipsis XX], :Enter
tmux.until do |lines|
assert_match(/^> 1 2 3XX.*9998 9999 10000$/, lines[-3])
assert_equal(1, lines[-3].scan('XX').size)
end
end
def test_backward_eof def test_backward_eof
tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter
tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 } tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }
@@ -1720,7 +1682,6 @@ class TestCore < TestInteractive
tmux.send_keys %(seq 100 | #{FZF} --multi --reverse --preview-window 0 --preview 'env | grep ^FZF_ | sort > #{tempname}' --no-input --bind enter:show-input+refresh-preview,space:disable-search+refresh-preview), :Enter tmux.send_keys %(seq 100 | #{FZF} --multi --reverse --preview-window 0 --preview 'env | grep ^FZF_ | sort > #{tempname}' --no-input --bind enter:show-input+refresh-preview,space:disable-search+refresh-preview), :Enter
expected = { expected = {
FZF_DIRECTION: 'down',
FZF_TOTAL_COUNT: '100', FZF_TOTAL_COUNT: '100',
FZF_MATCH_COUNT: '100', FZF_MATCH_COUNT: '100',
FZF_SELECT_COUNT: '0', FZF_SELECT_COUNT: '0',
@@ -2043,7 +2004,7 @@ class TestCore < TestInteractive
end end
end end
elapsed = Time.now - time elapsed = Time.now - time
assert_operator elapsed, :<, 2 assert elapsed < 2
end end
def test_bg_cancel def test_bg_cancel
@@ -2056,7 +2017,7 @@ class TestCore < TestInteractive
tmux.until { assert_equal 2, it.match_count } tmux.until { assert_equal 2, it.match_count }
tmux.send_keys :Space tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('[0]') } tmux.until { |lines| assert lines.any_include?('[0]') }
sleep(2) sleep 2
tmux.until do |lines| tmux.until do |lines|
assert lines.any_include?('[0]') assert lines.any_include?('[0]')
refute lines.any_include?('[1]') refute lines.any_include?('[1]')

View File

@@ -1192,29 +1192,6 @@ class TestLayout < TestInteractive
tmux.until { assert_block(block, it) } tmux.until { assert_block(block, it) }
end end
# https://github.com/junegunn/fzf/issues/4537
def test_no_scrollbar_preview_toggle
x = 'x' * 300
y = 'y' * 300
tmux.send_keys %(yes #{x} | head -1000 | fzf --bind 'tab:toggle-preview' --border --no-scrollbar --preview 'echo #{y}' --preview-window 'border-left'), :Enter
# │ ▌ xxxxxxxx·· │ yyyyyyyy│
tmux.until do |lines|
lines.any? { it.match?(/x·· │ y+│$/) }
end
tmux.send_keys :Tab
# │ ▌ xxxxxxxx·· │
tmux.until do |lines|
lines.none? { it.match?(/x··y│$/) }
end
tmux.send_keys :Tab
tmux.until do |lines|
lines.any? { it.match?(/x·· │ y+│$/) }
end
end
def test_combinations def test_combinations
skip unless ENV['LONGTEST'] skip unless ENV['LONGTEST']

View File

@@ -1,113 +0,0 @@
# frozen_string_literal: true
require_relative 'lib/common'
# Testing raw mode
class TestRaw < TestInteractive
def test_raw_mode
tmux.send_keys %(seq 1000 | #{FZF} --raw --bind ctrl-x:toggle-raw,a:enable-raw,b:disable-raw --gutter '▌' --multi --bind 'space:transform-prompt:echo "[[$FZF_RAW]] "'), :Enter
tmux.until { assert_equal 1000, it.match_count }
tmux.send_keys 1
tmux.until { assert_equal 272, it.match_count }
tmux.send_keys :Up
tmux.until { assert_includes it, '> 2' }
tmux.send_keys 'C-p'
tmux.until do
assert_includes it, '> 10'
assert_includes it, '▖ 9'
end
tmux.send_keys 'C-x'
tmux.until do
assert_includes it, '> 10'
assert_includes it, '▌ 1'
end
tmux.send_keys :Up, 'C-x'
tmux.until do
assert_includes it, '> 11'
assert_includes it, '▖ 10'
end
tmux.send_keys 1
tmux.until { assert_equal 28, it.match_count }
tmux.send_keys 'C-p'
tmux.until do
assert_includes it, '> 101'
assert_includes it, '▖ 100'
end
tmux.send_keys 'C-n'
tmux.until do
assert_includes it, '> 11'
assert_includes it, '▖ 10'
end
tmux.send_keys :Tab, :Tab, :Tab
tmux.until { assert_equal 3, it.select_count }
tmux.send_keys 'C-x'
tmux.until do
assert_equal 1, it.select_count
assert_includes it, '▌ 110'
assert_includes it, '>>11'
end
tmux.send_keys 'a'
tmux.until do
assert_equal 1, it.select_count
assert_includes it, '>>11'
assert_includes it, '▖ 10'
end
tmux.send_keys :Down, :Space
tmux.until { assert_includes it, '[[0]] 11' }
tmux.send_keys :Up, :Space
tmux.until { assert_includes it, '[[1]] 11' }
tmux.send_keys 'b'
tmux.until do
assert_equal 1, it.select_count
assert_includes it, '▌ 110'
assert_includes it, '>>11'
end
tmux.send_keys :Space
tmux.until { assert_includes it, '[[]] 11' }
tmux.send_keys 'C-u', '5'
tmux.until { assert_includes it, '> 5' }
tmux.send_keys 'C-x', 'C-p', 'C-p'
tmux.until do
assert_includes it, '> 25'
assert_includes it, '▖ 24'
end
tmux.send_keys 'C-x'
tmux.until do
assert_includes it, '> 25'
assert_includes it, '▌ 15'
end
# 35 is the closest match in raw mode
tmux.send_keys 'C-x', :Up, :Up, :Up, :Up, :Up, :Up, 'C-x'
tmux.until do
assert_includes it, '> 35'
assert_includes it, '▌ 25'
end
end
def test_raw_best
tmux.send_keys %(seq 1000 | #{FZF} --raw --bind space:best), :Enter
tmux.send_keys 999
tmux.until { assert_includes it, '> 1' }
tmux.send_keys :Space
tmux.until { assert_includes it, '> 999' }
end
end

View File

@@ -64,19 +64,19 @@ remove_line() {
line_no=1 line_no=1
continue continue
fi fi
line_no=$(($(sed 's/:.*//' <<< "$line") + line_no - 1)) line_no=$(( $(sed 's/:.*//' <<< "$line") + line_no - 1 ))
content=$(sed 's/^[0-9]*://' <<< "$line") content=$(sed 's/^[0-9]*://' <<< "$line")
match=1 match=1
echo " - Line #$line_no: $content" echo " - Line #$line_no: $content"
[ "$content" = "$1" ] || ask " - Remove?" [ "$content" = "$1" ] || ask " - Remove?"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
temp=$(mktemp) temp=$(mktemp)
awk -v n=$line_no 'NR == n {next} {print}' "$src" > "$temp" && awk -v n=$line_no 'NR == n {next} {print}' "$src" > "$temp" &&
cat "$temp" > "$src" && rm -f "$temp" || break cat "$temp" > "$src" && rm -f "$temp" || break
echo " - Removed" echo " - Removed"
else else
echo " - Skipped" echo " - Skipped"
line_no=$((line_no + 1)) line_no=$(( line_no + 1 ))
fi fi
done done
[ $match -eq 0 ] && echo " - Nothing found" [ $match -eq 0 ] && echo " - Nothing found"
@@ -109,6 +109,6 @@ if [ -d "${fish_dir}/functions" ]; then
fi fi
config_dir=$(dirname "$prefix_expand") config_dir=$(dirname "$prefix_expand")
if [[ $xdg == 1 ]] && [[ $config_dir == */fzf ]] && [[ -d $config_dir ]]; then if [[ "$xdg" = 1 ]] && [[ "$config_dir" = */fzf ]] && [[ -d "$config_dir" ]]; then
rmdir "$config_dir" rmdir "$config_dir"
fi fi