mirror of
https://github.com/junegunn/fzf.git
synced 2025-08-22 07:53:49 -07:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
beb2de2dd9 | ||
|
2a8b65e105 | ||
|
62a916bc24 | ||
|
c47b833e7b | ||
|
09b0958b5f | ||
|
3a4c3d3e58 | ||
|
7484292e63 | ||
|
687c2741b8 | ||
|
2fb285e530 | ||
|
16f6473938 | ||
|
66546208b2 | ||
|
532274045e | ||
|
9347c72fb6 | ||
|
e90bb7169c | ||
|
8a2c41e183 | ||
|
59fb65293a | ||
|
e7718b92b7 | ||
|
cdfaf761df | ||
|
1a9ea6f738 | ||
|
945c1c8597 | ||
|
e4d0f7acd5 | ||
|
250496c953 | ||
|
e47dc758c9 | ||
|
b92a843c5f | ||
|
91bea9c5b3 | ||
|
d75bb5cbe1 | ||
|
2671259fdb |
2
.github/workflows/depsreview.yaml
vendored
2
.github/workflows/depsreview.yaml
vendored
@@ -11,4 +11,4 @@ jobs:
|
|||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@v3
|
uses: actions/dependency-review-action@v4
|
||||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@@ -7,4 +7,4 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: crate-ci/typos@v1.16.4
|
- uses: crate-ci/typos@v1.17.2
|
||||||
|
@@ -1 +1 @@
|
|||||||
golang 1.20.4
|
golang 1.21.6
|
||||||
|
53
ADVANCED.md
53
ADVANCED.md
@@ -1,8 +1,8 @@
|
|||||||
Advanced fzf examples
|
Advanced fzf examples
|
||||||
======================
|
======================
|
||||||
|
|
||||||
* *Last update: 2023/12/29*
|
* *Last update: 2024/01/20*
|
||||||
* *Requires fzf 0.45.0 or above*
|
* *Requires fzf 0.46.0 or above*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -22,12 +22,14 @@ Advanced fzf examples
|
|||||||
* [Using fzf as interactive Ripgrep launcher](#using-fzf-as-interactive-ripgrep-launcher)
|
* [Using fzf as interactive Ripgrep launcher](#using-fzf-as-interactive-ripgrep-launcher)
|
||||||
* [Switching to fzf-only search mode](#switching-to-fzf-only-search-mode)
|
* [Switching to fzf-only search mode](#switching-to-fzf-only-search-mode)
|
||||||
* [Switching between Ripgrep mode and fzf mode](#switching-between-ripgrep-mode-and-fzf-mode)
|
* [Switching between Ripgrep mode and fzf mode](#switching-between-ripgrep-mode-and-fzf-mode)
|
||||||
|
* [Switching between Ripgrep mode and fzf mode using a single key binding](#switching-between-ripgrep-mode-and-fzf-mode-using-a-single-key-binding)
|
||||||
* [Log tailing](#log-tailing)
|
* [Log tailing](#log-tailing)
|
||||||
* [Key bindings for git objects](#key-bindings-for-git-objects)
|
* [Key bindings for git objects](#key-bindings-for-git-objects)
|
||||||
* [Files listed in `git status`](#files-listed-in-git-status)
|
* [Files listed in `git status`](#files-listed-in-git-status)
|
||||||
* [Branches](#branches)
|
* [Branches](#branches)
|
||||||
* [Commit hashes](#commit-hashes)
|
* [Commit hashes](#commit-hashes)
|
||||||
* [Color themes](#color-themes)
|
* [Color themes](#color-themes)
|
||||||
|
* [fzf Theme Playground](#fzf-theme-playground)
|
||||||
* [Generating fzf color theme from Vim color schemes](#generating-fzf-color-theme-from-vim-color-schemes)
|
* [Generating fzf color theme from Vim color schemes](#generating-fzf-color-theme-from-vim-color-schemes)
|
||||||
|
|
||||||
<!-- vim-markdown-toc -->
|
<!-- vim-markdown-toc -->
|
||||||
@@ -220,17 +222,17 @@ To make a key binding behave differently each time it is pressed, we need:
|
|||||||
2. and a way to dynamically perform different actions depending on the state.
|
2. and a way to dynamically perform different actions depending on the state.
|
||||||
|
|
||||||
The following example shows how to 1. store the current mode in the prompt
|
The following example shows how to 1. store the current mode in the prompt
|
||||||
string, 2. and use this information (`{fzf:prompt}`) to determine which
|
string, 2. and use this information (`$FZF_PROMPT`) to determine which
|
||||||
actions to perform using the `transform` action.
|
actions to perform using the `transform` action.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
fd --type file |
|
fd --type file |
|
||||||
fzf --prompt 'Files> ' \
|
fzf --prompt 'Files> ' \
|
||||||
--header 'CTRL-T: Switch between Files/Directories' \
|
--header 'CTRL-T: Switch between Files/Directories' \
|
||||||
--bind 'ctrl-t:transform:[[ ! {fzf:prompt} =~ Files ]] &&
|
--bind 'ctrl-t:transform:[[ ! $FZF_PROMPT =~ Files ]] &&
|
||||||
echo "change-prompt(Files> )+reload(fd --type file)" ||
|
echo "change-prompt(Files> )+reload(fd --type file)" ||
|
||||||
echo "change-prompt(Directories> )+reload(fd --type directory)"' \
|
echo "change-prompt(Directories> )+reload(fd --type directory)"' \
|
||||||
--preview '[[ {fzf:prompt} =~ Files ]] && bat --color=always {} || tree -C {}'
|
--preview '[[ $FZF_PROMPT =~ Files ]] && bat --color=always {} || tree -C {}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Ripgrep integration
|
Ripgrep integration
|
||||||
@@ -467,6 +469,41 @@ INITIAL_QUERY="${*:-}"
|
|||||||
[0.30.0]: https://github.com/junegunn/fzf/blob/master/CHANGELOG.md#0300
|
[0.30.0]: https://github.com/junegunn/fzf/blob/master/CHANGELOG.md#0300
|
||||||
[0.36.0]: https://github.com/junegunn/fzf/blob/master/CHANGELOG.md#0360
|
[0.36.0]: https://github.com/junegunn/fzf/blob/master/CHANGELOG.md#0360
|
||||||
|
|
||||||
|
### Switching between Ripgrep mode and fzf mode using a single key binding
|
||||||
|
|
||||||
|
In contrast to the previous version, we use just one hotkey to toggle between
|
||||||
|
ripgrep and fzf mode. This is achieved by using the `$FZF_PROMPT` as a state
|
||||||
|
within the `transform` action, a feature introduced in [fzf 0.45.0][0.45.0]. A
|
||||||
|
more detailed explanation of this feature can be found in a previous section -
|
||||||
|
[Toggling with a single keybinding](#toggling-with-a-single-key-binding).
|
||||||
|
|
||||||
|
[0.45.0]: https://github.com/junegunn/fzf/blob/master/CHANGELOG.md#0450
|
||||||
|
|
||||||
|
When using the `transform` action, the placeholder (`\{q}`) should be escaped to
|
||||||
|
prevent immediate evaluation.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Switch between Ripgrep mode and fzf filtering mode (CTRL-T)
|
||||||
|
rm -f /tmp/rg-fzf-{r,f}
|
||||||
|
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
|
||||||
|
INITIAL_QUERY="${*:-}"
|
||||||
|
: | fzf --ansi --disabled --query "$INITIAL_QUERY" \
|
||||||
|
--bind "start:reload:$RG_PREFIX {q}" \
|
||||||
|
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
|
||||||
|
--bind 'ctrl-t:transform:[[ ! $FZF_PROMPT =~ ripgrep ]] &&
|
||||||
|
echo "rebind(change)+change-prompt(1. ripgrep> )+disable-search+transform-query:echo \{q} > /tmp/rg-fzf-f; cat /tmp/rg-fzf-r" ||
|
||||||
|
echo "unbind(change)+change-prompt(2. fzf> )+enable-search+transform-query:echo \{q} > /tmp/rg-fzf-r; cat /tmp/rg-fzf-f"' \
|
||||||
|
--color "hl:-1:underline,hl+:-1:underline:reverse" \
|
||||||
|
--prompt '1. ripgrep> ' \
|
||||||
|
--delimiter : \
|
||||||
|
--header 'CTRL-T: Switch between ripgrep/fzf' \
|
||||||
|
--preview 'bat --color=always {1} --highlight-line {2}' \
|
||||||
|
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
|
||||||
|
--bind 'enter:become(vim {1} +{2})'
|
||||||
|
```
|
||||||
|
|
||||||
Log tailing
|
Log tailing
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@@ -596,6 +633,12 @@ export FZF_DEFAULT_OPTS='--color=bg+:#293739,bg:#1B1D1E,border:#808080,spinner:#
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### fzf Theme Playground
|
||||||
|
|
||||||
|
[fzf Theme Playground](https://vitormv.github.io/fzf-themes/) created by
|
||||||
|
[Vitor Mello](https://github.com/vitormv) is a webpage where you can
|
||||||
|
interactively create fzf themes.
|
||||||
|
|
||||||
### Generating fzf color theme from Vim color schemes
|
### Generating fzf color theme from Vim color schemes
|
||||||
|
|
||||||
The Vim plugin of fzf can generate `--color` option from the current color
|
The Vim plugin of fzf can generate `--color` option from the current color
|
||||||
|
4
BUILD.md
4
BUILD.md
@@ -34,8 +34,8 @@ make release
|
|||||||
Third-party libraries used
|
Third-party libraries used
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
- [mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
- [rivo/uniseg](https://github.com/rivo/uniseg)
|
||||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
- Licensed under [MIT](https://raw.githubusercontent.com/rivo/uniseg/master/LICENSE.txt)
|
||||||
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
|
- [mattn/go-shellwords](https://github.com/mattn/go-shellwords)
|
||||||
- Licensed under [MIT](http://mattn.mit-license.org)
|
- Licensed under [MIT](http://mattn.mit-license.org)
|
||||||
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
- [mattn/go-isatty](https://github.com/mattn/go-isatty)
|
||||||
|
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,6 +1,46 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.46.0
|
||||||
|
------
|
||||||
|
- Added two new events
|
||||||
|
- `result` - triggered when the filtering for the current query is complete and the result list is ready
|
||||||
|
- `resize` - triggered when the terminal size is changed
|
||||||
|
- fzf now exports the following environment variables to the child processes
|
||||||
|
| Variable | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `FZF_LINES` | Number of lines fzf takes up excluding padding and margin |
|
||||||
|
| `FZF_COLUMNS` | Number of columns fzf takes up excluding padding and margin |
|
||||||
|
| `FZF_TOTAL_COUNT` | Total number of items |
|
||||||
|
| `FZF_MATCH_COUNT` | Number of matched items |
|
||||||
|
| `FZF_SELECT_COUNT` | Number of selected items |
|
||||||
|
| `FZF_QUERY` | Current query string |
|
||||||
|
| `FZF_PROMPT` | Prompt string |
|
||||||
|
| `FZF_ACTION` | The name of the last action performed |
|
||||||
|
- This allows you to write sophisticated transformations like so
|
||||||
|
```sh
|
||||||
|
# Script to dynamically resize the preview window
|
||||||
|
transformer='
|
||||||
|
# 1 line for info, another for prompt, and 2 more lines for preview window border
|
||||||
|
lines=$(( FZF_LINES - FZF_MATCH_COUNT - 4 ))
|
||||||
|
if [[ $FZF_MATCH_COUNT -eq 0 ]]; then
|
||||||
|
echo "change-preview-window:hidden"
|
||||||
|
elif [[ $lines -gt 3 ]]; then
|
||||||
|
echo "change-preview-window:$lines"
|
||||||
|
elif [[ $FZF_PREVIEW_LINES -ne 3 ]]; then
|
||||||
|
echo "change-preview-window:3"
|
||||||
|
fi
|
||||||
|
'
|
||||||
|
seq 10000 | fzf --preview 'seq {} 10000' --preview-window up \
|
||||||
|
--bind "result:transform:$transformer" \
|
||||||
|
--bind "resize:transform:$transformer"
|
||||||
|
```
|
||||||
|
- And we're phasing out `{fzf:prompt}` and `{fzf:action}`
|
||||||
|
- Changed [mattn/go-runewidth](https://github.com/mattn/go-runewidth) dependency to [rivo/uniseg](https://github.com/rivo/uniseg) for accurate results
|
||||||
|
- Set `--ambidouble` if your terminal displays ambiguous width characters (e.g. box-drawing characters for borders) as 2 columns
|
||||||
|
- `RUNEWIDTH_EASTASIAN=1` is still respected for backward compatibility, but it's recommended that you use this new option instead
|
||||||
|
- Bug fixes
|
||||||
|
|
||||||
0.45.0
|
0.45.0
|
||||||
------
|
------
|
||||||
- Added `transform` action to conditionally perform a series of actions
|
- Added `transform` action to conditionally perform a series of actions
|
||||||
@@ -92,7 +132,7 @@ CHANGELOG
|
|||||||
# --transfer-mode=memory is the fastest option but if you want fzf to be able
|
# --transfer-mode=memory is the fastest option but if you want fzf to be able
|
||||||
# to redraw the image on terminal resize or on 'change-preview-window',
|
# to redraw the image on terminal resize or on 'change-preview-window',
|
||||||
# you need to use --transfer-mode=stream.
|
# you need to use --transfer-mode=stream.
|
||||||
kitty icat --clear --transfer-mode=memory --stdin=no --place=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0 {} | sed \$d
|
kitty icat --clear --transfer-mode=memory --unicode-placeholder --stdin=no --place=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}@0x0 {} | sed \$d
|
||||||
else
|
else
|
||||||
bat --color=always {}
|
bat --color=always {}
|
||||||
fi
|
fi
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
FROM --platform=linux/amd64 archlinux
|
FROM --platform=linux/amd64 ubuntu:22.04
|
||||||
RUN pacman -Sy && pacman --noconfirm -S awk git tmux zsh fish ruby procps go make gcc
|
RUN apt-get update -y && apt install -y git make golang zsh fish ruby tmux
|
||||||
RUN gem install --no-document -v 5.14.2 minitest
|
RUN gem install --no-document -v 5.14.2 minitest
|
||||||
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
RUN echo '. /usr/share/bash-completion/completions/git' >> ~/.bashrc
|
||||||
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
||||||
@@ -8,4 +8,5 @@ RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
|||||||
RUN rm -f /etc/bash.bashrc
|
RUN rm -f /etc/bash.bashrc
|
||||||
COPY . /fzf
|
COPY . /fzf
|
||||||
RUN cd /fzf && make install && ./install --all
|
RUN cd /fzf && make install && ./install --all
|
||||||
|
ENV LANG C.UTF-8
|
||||||
CMD tmux new 'set -o pipefail; ruby /fzf/test/test_go.rb | tee out && touch ok' && cat out && [ -e ok ]
|
CMD tmux new 'set -o pipefail; ruby /fzf/test/test_go.rb | tee out && touch ok' && cat out && [ -e ok ]
|
||||||
|
4
Makefile
4
Makefile
@@ -93,7 +93,7 @@ generate:
|
|||||||
PATH=$(PATH):$(GOPATH)/bin $(GO) generate ./...
|
PATH=$(PATH):$(GOPATH)/bin $(GO) generate ./...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
goreleaser build --rm-dist --snapshot --skip-post-hooks
|
goreleaser build --clean --snapshot --skip=post-hooks
|
||||||
|
|
||||||
release:
|
release:
|
||||||
# Make sure that the tests pass and the build works
|
# Make sure that the tests pass and the build works
|
||||||
@@ -126,7 +126,7 @@ endif
|
|||||||
git push origin temp --follow-tags --force
|
git push origin temp --follow-tags --force
|
||||||
|
|
||||||
# Make a GitHub release
|
# Make a GitHub release
|
||||||
goreleaser --rm-dist --release-notes tmp/release-note
|
goreleaser --clean --release-notes tmp/release-note
|
||||||
|
|
||||||
# Push to master
|
# Push to master
|
||||||
git checkout master
|
git checkout master
|
||||||
|
@@ -53,7 +53,7 @@ if [[ $KITTY_WINDOW_ID ]]; then
|
|||||||
# 2. The last line of the output is the ANSI reset code without newline.
|
# 2. The last line of the output is the ANSI reset code without newline.
|
||||||
# This confuses fzf and makes it render scroll offset indicator.
|
# This confuses fzf and makes it render scroll offset indicator.
|
||||||
# So we remove the last line and append the reset code to its previous line.
|
# So we remove the last line and append the reset code to its previous line.
|
||||||
kitty icat --clear --transfer-mode=memory --stdin=no --place="$dim@0x0" "$file" | sed '$d' | sed $'$s/$/\e[m/'
|
kitty icat --clear --transfer-mode=memory --unicode-placeholder --stdin=no --place="$dim@0x0" "$file" | sed '$d' | sed $'$s/$/\e[m/'
|
||||||
|
|
||||||
# 2. Use chafa with Sixel output
|
# 2. Use chafa with Sixel output
|
||||||
elif command -v chafa > /dev/null; then
|
elif command -v chafa > /dev/null; then
|
||||||
|
11
go.mod
11
go.mod
@@ -2,22 +2,21 @@ module github.com/junegunn/fzf
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/tcell/v2 v2.5.4
|
github.com/gdamore/tcell/v2 v2.5.4
|
||||||
|
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2
|
||||||
github.com/mattn/go-isatty v0.0.17
|
github.com/mattn/go-isatty v0.0.17
|
||||||
github.com/mattn/go-runewidth v0.0.14
|
|
||||||
github.com/mattn/go-shellwords v1.0.12
|
github.com/mattn/go-shellwords v1.0.12
|
||||||
github.com/rivo/uniseg v0.4.4
|
|
||||||
github.com/saracen/walker v0.1.3
|
github.com/saracen/walker v0.1.3
|
||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.16.0
|
||||||
golang.org/x/term v0.15.0
|
golang.org/x/term v0.16.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gdamore/encoding v1.0.0 // indirect
|
github.com/gdamore/encoding v1.0.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.4 // indirect
|
||||||
golang.org/x/sync v0.5.0 // indirect
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.16.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.17
|
go 1.17
|
||||||
|
15
go.sum
15
go.sum
@@ -2,6 +2,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk
|
|||||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||||
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
|
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
|
||||||
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
|
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
|
||||||
|
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2 h1:oEwPBh29BPu1MaTsz2dM9bDrkOgKBoYFC0u6uY2izWo=
|
||||||
|
github.com/junegunn/uniseg v0.0.0-20240120174029-b504da4f6ed2/go.mod h1:ywqF55XaSE3/uS2tkJqVFKiE0oIYAXRvU2N7DU4y3XQ=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
@@ -19,14 +21,11 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
@@ -36,12 +35,12 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
@@ -50,6 +49,4 @@ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
|
||||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
2
install
2
install
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
version=0.45.0
|
version=0.46.0
|
||||||
auto_completion=
|
auto_completion=
|
||||||
key_bindings=
|
key_bindings=
|
||||||
update_config=2
|
update_config=2
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
$version="0.45.0"
|
$version="0.46.0"
|
||||||
|
|
||||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||||
|
|
||||||
|
2
main.go
2
main.go
@@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/protector"
|
"github.com/junegunn/fzf/src/protector"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.45"
|
var version string = "0.46"
|
||||||
var revision string = "devel"
|
var revision string = "devel"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
..
|
..
|
||||||
.TH fzf-tmux 1 "Jan 2024" "fzf 0.45.0" "fzf-tmux - open fzf in tmux split pane"
|
.TH fzf-tmux 1 "Jan 2024" "fzf 0.46.0" "fzf-tmux - open fzf in tmux split pane"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf-tmux - open fzf in tmux split pane
|
fzf-tmux - open fzf in tmux split pane
|
||||||
|
@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
..
|
..
|
||||||
.TH fzf 1 "Jan 2024" "fzf 0.45.0" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Jan 2024" "fzf 0.46.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@@ -260,9 +260,8 @@ Draw border around the finder
|
|||||||
.br
|
.br
|
||||||
|
|
||||||
If you use a terminal emulator where each box-drawing character takes
|
If you use a terminal emulator where each box-drawing character takes
|
||||||
2 columns, try setting \fBRUNEWIDTH_EASTASIAN\fR environment variable to
|
2 columns, try setting \fB--ambidouble\fR. If the border is still not properly
|
||||||
\fB0\fR or \fB1\fR. If the border is still not properly rendered, set
|
rendered, set \fB--no-unicode\fR.
|
||||||
\fB--no-unicode\fR.
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "--border-label" [=LABEL]
|
.BI "--border-label" [=LABEL]
|
||||||
@@ -313,6 +312,11 @@ the label. Label is printed on the top border line by default, add
|
|||||||
Use ASCII characters instead of Unicode drawing characters to draw borders,
|
Use ASCII characters instead of Unicode drawing characters to draw borders,
|
||||||
the spinner and the horizontal separator.
|
the spinner and the horizontal separator.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B "--ambidouble"
|
||||||
|
Set this option if your terminal displays ambiguous width characters (e.g.
|
||||||
|
box-drawing characters for borders) as 2 columns.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "--margin=" MARGIN
|
.BI "--margin=" MARGIN
|
||||||
Comma-separated expression for margins around the finder.
|
Comma-separated expression for margins around the finder.
|
||||||
@@ -592,8 +596,6 @@ Also,
|
|||||||
* \fB{n}\fR is replaced to the zero-based ordinal index of the current item.
|
* \fB{n}\fR is replaced to the zero-based ordinal index of the current item.
|
||||||
Use \fB{+n}\fR if you want all index numbers when multiple lines are selected.
|
Use \fB{+n}\fR if you want all index numbers when multiple lines are selected.
|
||||||
.br
|
.br
|
||||||
* \fB{fzf:action}\fR is replaced to to the name of the last action performed
|
|
||||||
* \fB{fzf:prompt}\fR is replaced to to the prompt string
|
|
||||||
|
|
||||||
Note that you can escape a placeholder pattern by prepending a backslash.
|
Note that you can escape a placeholder pattern by prepending a backslash.
|
||||||
|
|
||||||
@@ -829,12 +831,14 @@ e.g.
|
|||||||
\fB# Start HTTP server on port 6266
|
\fB# Start HTTP server on port 6266
|
||||||
fzf --listen 6266
|
fzf --listen 6266
|
||||||
|
|
||||||
# Get program state in JSON format (experimental)
|
|
||||||
curl localhost:6266
|
|
||||||
|
|
||||||
# Send action to the server
|
# Send action to the server
|
||||||
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
||||||
|
|
||||||
|
# Get program state in JSON format (experimental)
|
||||||
|
# * Make sure NOT to access this endpoint from execute/transform actions
|
||||||
|
# as it will result in a timeout
|
||||||
|
curl localhost:6266
|
||||||
|
|
||||||
# Start HTTP server on port 6266 with remote connections allowed
|
# Start HTTP server on port 6266 with remote connections allowed
|
||||||
# * Listening on non-localhost address requires using an API key
|
# * Listening on non-localhost address requires using an API key
|
||||||
export FZF_API_KEY="$(head -c 32 /dev/urandom | base64)"
|
export FZF_API_KEY="$(head -c 32 /dev/urandom | base64)"
|
||||||
@@ -901,6 +905,39 @@ of field index expressions.
|
|||||||
.BR .. " All the fields"
|
.BR .. " All the fields"
|
||||||
.br
|
.br
|
||||||
|
|
||||||
|
.SH ENVIRONMENT VARIABLES EXPORTED TO CHILD PROCESSES
|
||||||
|
|
||||||
|
fzf exports the following environment variables to its child processes.
|
||||||
|
|
||||||
|
.BR FZF_LINES " Number of lines fzf takes up excluding padding and margin"
|
||||||
|
.br
|
||||||
|
.BR FZF_COLUMNS " Number of columns fzf takes up excluding padding and margin"
|
||||||
|
.br
|
||||||
|
.BR FZF_TOTAL_COUNT " Total number of items"
|
||||||
|
.br
|
||||||
|
.BR FZF_MATCH_COUNT " Number of matched items"
|
||||||
|
.br
|
||||||
|
.BR FZF_SELECT_COUNT " Number of selected items"
|
||||||
|
.br
|
||||||
|
.BR FZF_QUERY " Current query string"
|
||||||
|
.br
|
||||||
|
.BR FZF_PROMPT " Prompt string"
|
||||||
|
.br
|
||||||
|
.BR FZF_ACTION " The name of the last action performed"
|
||||||
|
.br
|
||||||
|
.BR FZF_PORT " Port number when --listen option is used"
|
||||||
|
.br
|
||||||
|
|
||||||
|
The following variables are additionally exported to the preview commands.
|
||||||
|
|
||||||
|
.BR FZF_PREVIEW_TOP " Top position of the preview window"
|
||||||
|
.br
|
||||||
|
.BR FZF_PREVIEW_LEFT " Left position of the preview window"
|
||||||
|
.br
|
||||||
|
.BR FZF_PREVIEW_LINES " Number of lines in the preview window"
|
||||||
|
.br
|
||||||
|
.BR FZF_PREVIEW_COLUMNS " Number of columns in the preview window"
|
||||||
|
|
||||||
.SH EXTENDED SEARCH MODE
|
.SH EXTENDED SEARCH MODE
|
||||||
|
|
||||||
Unless specified otherwise, fzf will start in "extended-search mode". In this
|
Unless specified otherwise, fzf will start in "extended-search mode". In this
|
||||||
@@ -1075,6 +1112,22 @@ e.g.
|
|||||||
\fB# Change the prompt to "loaded" when the input stream is complete
|
\fB# Change the prompt to "loaded" when the input stream is complete
|
||||||
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '\fR
|
(seq 10; sleep 1; seq 11 20) | fzf --prompt 'Loading> ' --bind 'load:change-prompt:Loaded> '\fR
|
||||||
.RE
|
.RE
|
||||||
|
\fIresize\fR
|
||||||
|
.RS
|
||||||
|
Triggered when the terminal size is changed.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fBfzf --bind 'resize:transform-header:echo Resized: ${FZF_COLUMNS}x${FZF_LINES}'\fR
|
||||||
|
.RE
|
||||||
|
\fIresult\fR
|
||||||
|
.RS
|
||||||
|
Triggered when the filtering for the current query is complete and the result list is ready.
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
\fB# Put the cursor on the second item when the query string is empty
|
||||||
|
# * Note that you can't use 'change' event in this case because the second position may not be available
|
||||||
|
fzf --sync --bind 'result:transform:[[ -z {fzf:query} ]] && echo "pos(2)"'\fR
|
||||||
|
.RE
|
||||||
\fIchange\fR
|
\fIchange\fR
|
||||||
.RS
|
.RS
|
||||||
Triggered whenever the query string is changed
|
Triggered whenever the query string is changed
|
||||||
|
@@ -98,13 +98,15 @@ bindkey -M viins '\ec' fzf-cd-widget
|
|||||||
fzf-history-widget() {
|
fzf-history-widget() {
|
||||||
local selected num
|
local selected num
|
||||||
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
||||||
selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
selected="$(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
||||||
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd))"
|
||||||
local ret=$?
|
local ret=$?
|
||||||
if [ -n "$selected" ]; then
|
if [ -n "$selected" ]; then
|
||||||
num=$selected[1]
|
num=$(awk '{print $1}' <<< "$selected")
|
||||||
if [ -n "$num" ]; then
|
if [[ "$num" =~ '^[1-9][0-9]*\*?$' ]]; then
|
||||||
zle vi-fetch-history -n $num
|
zle vi-fetch-history -n ${num%\*}
|
||||||
|
else # selected is a custom query, not from history
|
||||||
|
LBUFFER="$selected"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
zle reset-prompt
|
zle reset-prompt
|
||||||
|
@@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/junegunn/fzf/src/algo"
|
"github.com/junegunn/fzf/src/algo"
|
||||||
"github.com/junegunn/fzf/src/tui"
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
"github.com/junegunn/uniseg"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -337,6 +337,7 @@ type Options struct {
|
|||||||
BorderLabel labelOpts
|
BorderLabel labelOpts
|
||||||
PreviewLabel labelOpts
|
PreviewLabel labelOpts
|
||||||
Unicode bool
|
Unicode bool
|
||||||
|
Ambidouble bool
|
||||||
Tabstop int
|
Tabstop int
|
||||||
ListenAddr *listenAddress
|
ListenAddr *listenAddress
|
||||||
Unsafe bool
|
Unsafe bool
|
||||||
@@ -406,6 +407,7 @@ func defaultOptions() *Options {
|
|||||||
Margin: defaultMargin(),
|
Margin: defaultMargin(),
|
||||||
Padding: defaultMargin(),
|
Padding: defaultMargin(),
|
||||||
Unicode: true,
|
Unicode: true,
|
||||||
|
Ambidouble: os.Getenv("RUNEWIDTH_EASTASIAN") == "1",
|
||||||
Tabstop: 8,
|
Tabstop: 8,
|
||||||
BorderLabel: labelOpts{},
|
BorderLabel: labelOpts{},
|
||||||
PreviewLabel: labelOpts{},
|
PreviewLabel: labelOpts{},
|
||||||
@@ -650,6 +652,10 @@ func parseKeyChordsImpl(str string, message string, exit func(string)) map[tui.E
|
|||||||
add(tui.Load)
|
add(tui.Load)
|
||||||
case "focus":
|
case "focus":
|
||||||
add(tui.Focus)
|
add(tui.Focus)
|
||||||
|
case "result":
|
||||||
|
add(tui.Result)
|
||||||
|
case "resize":
|
||||||
|
add(tui.Resize)
|
||||||
case "one":
|
case "one":
|
||||||
add(tui.One)
|
add(tui.One)
|
||||||
case "zero":
|
case "zero":
|
||||||
@@ -1591,8 +1597,6 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateJumpLabels := false
|
validateJumpLabels := false
|
||||||
validatePointer := false
|
|
||||||
validateMarker := false
|
|
||||||
for i := 0; i < len(allArgs); i++ {
|
for i := 0; i < len(allArgs); i++ {
|
||||||
arg := allArgs[i]
|
arg := allArgs[i]
|
||||||
switch arg {
|
switch arg {
|
||||||
@@ -1772,10 +1776,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
opts.Prompt = nextString(allArgs, &i, "prompt string required")
|
||||||
case "--pointer":
|
case "--pointer":
|
||||||
opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
|
opts.Pointer = firstLine(nextString(allArgs, &i, "pointer sign string required"))
|
||||||
validatePointer = true
|
|
||||||
case "--marker":
|
case "--marker":
|
||||||
opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
|
opts.Marker = firstLine(nextString(allArgs, &i, "selected sign string required"))
|
||||||
validateMarker = true
|
|
||||||
case "--sync":
|
case "--sync":
|
||||||
opts.Sync = true
|
opts.Sync = true
|
||||||
case "--no-sync":
|
case "--no-sync":
|
||||||
@@ -1843,6 +1845,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Unicode = false
|
opts.Unicode = false
|
||||||
case "--unicode":
|
case "--unicode":
|
||||||
opts.Unicode = true
|
opts.Unicode = true
|
||||||
|
case "--ambidouble":
|
||||||
|
opts.Ambidouble = true
|
||||||
|
case "--no-ambidouble":
|
||||||
|
opts.Ambidouble = false
|
||||||
case "--margin":
|
case "--margin":
|
||||||
opts.Margin = parseMargin(
|
opts.Margin = parseMargin(
|
||||||
"margin",
|
"margin",
|
||||||
@@ -1901,10 +1907,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
opts.Prompt = value
|
opts.Prompt = value
|
||||||
} else if match, value := optString(arg, "--pointer="); match {
|
} else if match, value := optString(arg, "--pointer="); match {
|
||||||
opts.Pointer = firstLine(value)
|
opts.Pointer = firstLine(value)
|
||||||
validatePointer = true
|
|
||||||
} else if match, value := optString(arg, "--marker="); match {
|
} else if match, value := optString(arg, "--marker="); match {
|
||||||
opts.Marker = firstLine(value)
|
opts.Marker = firstLine(value)
|
||||||
validateMarker = true
|
|
||||||
} else if match, value := optString(arg, "-n", "--nth="); match {
|
} else if match, value := optString(arg, "-n", "--nth="); match {
|
||||||
opts.Nth = splitNth(value)
|
opts.Nth = splitNth(value)
|
||||||
} else if match, value := optString(arg, "--with-nth="); match {
|
} else if match, value := optString(arg, "--with-nth="); match {
|
||||||
@@ -2011,31 +2015,31 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if validatePointer {
|
|
||||||
if err := validateSign(opts.Pointer, "pointer"); err != nil {
|
|
||||||
errorExit(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if validateMarker {
|
|
||||||
if err := validateSign(opts.Marker, "marker"); err != nil {
|
|
||||||
errorExit(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSign(sign string, signOptName string) error {
|
func validateSign(sign string, signOptName string) error {
|
||||||
if sign == "" {
|
if sign == "" {
|
||||||
return fmt.Errorf("%v cannot be empty", signOptName)
|
return fmt.Errorf("%v cannot be empty", signOptName)
|
||||||
}
|
}
|
||||||
if runewidth.StringWidth(sign) > 2 {
|
if uniseg.StringWidth(sign) > 2 {
|
||||||
return fmt.Errorf("%v display width should be up to 2", signOptName)
|
return fmt.Errorf("%v display width should be up to 2", signOptName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func postProcessOptions(opts *Options) {
|
func postProcessOptions(opts *Options) {
|
||||||
|
if opts.Ambidouble {
|
||||||
|
uniseg.EastAsianAmbiguousWidth = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateSign(opts.Pointer, "pointer"); err != nil {
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validateSign(opts.Marker, "marker"); err != nil {
|
||||||
|
errorExit(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
||||||
errorExit("--height option is currently not supported on this platform")
|
errorExit("--height option is currently not supported on this platform")
|
||||||
}
|
}
|
||||||
@@ -2046,7 +2050,7 @@ func postProcessOptions(opts *Options) {
|
|||||||
errorExit("--scrollbar should be given one or two characters")
|
errorExit("--scrollbar should be given one or two characters")
|
||||||
}
|
}
|
||||||
for _, r := range runes {
|
for _, r := range runes {
|
||||||
if runewidth.RuneWidth(r) != 1 {
|
if uniseg.StringWidth(string(r)) != 1 {
|
||||||
errorExit("scrollbar display width should be 1")
|
errorExit("scrollbar display width should be 1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -138,9 +138,11 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
|
|||||||
for i := off[0]; i < off[1]; i++ {
|
for i := off[0]; i < off[1]; i++ {
|
||||||
// Negative of 1-based index of itemColors
|
// Negative of 1-based index of itemColors
|
||||||
// - The extra -1 means highlighted
|
// - The extra -1 means highlighted
|
||||||
|
if cols[i] >= 0 {
|
||||||
cols[i] = cols[i]*-1 - 1
|
cols[i] = cols[i]*-1 - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sort.Sort(ByOrder(offsets))
|
// sort.Sort(ByOrder(offsets))
|
||||||
|
|
||||||
|
@@ -120,7 +120,7 @@ func TestColorOffset(t *testing.T) {
|
|||||||
// ++++++++ ++++++++++
|
// ++++++++ ++++++++++
|
||||||
// --++++++++-- --++++++++++---
|
// --++++++++-- --++++++++++---
|
||||||
|
|
||||||
offsets := []Offset{{5, 15}, {25, 35}}
|
offsets := []Offset{{5, 15}, {10, 12}, {25, 35}}
|
||||||
item := Result{
|
item := Result{
|
||||||
item: &Item{
|
item: &Item{
|
||||||
colors: &[]ansiOffset{
|
colors: &[]ansiOffset{
|
||||||
|
@@ -30,7 +30,9 @@ const (
|
|||||||
httpOk = "HTTP/1.1 200 OK" + crlf
|
httpOk = "HTTP/1.1 200 OK" + crlf
|
||||||
httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf
|
httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf
|
||||||
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf
|
||||||
|
httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf
|
||||||
httpReadTimeout = 10 * time.Second
|
httpReadTimeout = 10 * time.Second
|
||||||
|
jsonContentType = "Content-Type: application/json" + crlf
|
||||||
maxContentLength = 1024 * 1024
|
maxContentLength = 1024 * 1024
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -141,7 +143,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
return answer(httpBadRequest, message)
|
return answer(httpBadRequest, message)
|
||||||
}
|
}
|
||||||
good := func(message string) string {
|
good := func(message string) string {
|
||||||
return answer(httpOk+"Content-Type: application/json"+crlf, message)
|
return answer(httpOk+jsonContentType, message)
|
||||||
}
|
}
|
||||||
conn.SetReadDeadline(time.Now().Add(httpReadTimeout))
|
conn.SetReadDeadline(time.Now().Add(httpReadTimeout))
|
||||||
scanner := bufio.NewScanner(conn)
|
scanner := bufio.NewScanner(conn)
|
||||||
@@ -165,8 +167,16 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string {
|
|||||||
getMatch := getRegex.FindStringSubmatch(text)
|
getMatch := getRegex.FindStringSubmatch(text)
|
||||||
if len(getMatch) > 0 {
|
if len(getMatch) > 0 {
|
||||||
server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}}
|
server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}}
|
||||||
response := <-server.responseChannel
|
select {
|
||||||
|
case response := <-server.responseChannel:
|
||||||
return good(response)
|
return good(response)
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
go func() {
|
||||||
|
// Drain the channel
|
||||||
|
<-server.responseChannel
|
||||||
|
}()
|
||||||
|
return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`)
|
||||||
|
}
|
||||||
} else if !strings.HasPrefix(text, "POST / HTTP") {
|
} else if !strings.HasPrefix(text, "POST / HTTP") {
|
||||||
return bad("invalid request method")
|
return bad("invalid request method")
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/junegunn/uniseg"
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
|
|
||||||
"github.com/junegunn/fzf/src/tui"
|
"github.com/junegunn/fzf/src/tui"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
@@ -251,7 +250,10 @@ type Terminal struct {
|
|||||||
borderWidth int
|
borderWidth int
|
||||||
count int
|
count int
|
||||||
progress int
|
progress int
|
||||||
|
hasResultActions bool
|
||||||
|
hasFocusActions bool
|
||||||
hasLoadActions bool
|
hasLoadActions bool
|
||||||
|
hasResizeActions bool
|
||||||
triggerLoad bool
|
triggerLoad bool
|
||||||
reading bool
|
reading bool
|
||||||
running bool
|
running bool
|
||||||
@@ -289,6 +291,8 @@ type Terminal struct {
|
|||||||
termSize tui.TermSize
|
termSize tui.TermSize
|
||||||
lastAction actionType
|
lastAction actionType
|
||||||
lastFocus int32
|
lastFocus int32
|
||||||
|
areaLines int
|
||||||
|
areaColumns int
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectedItem struct {
|
type selectedItem struct {
|
||||||
@@ -449,6 +453,17 @@ const (
|
|||||||
actHideHeader
|
actHideHeader
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (a actionType) Name() string {
|
||||||
|
name := ""
|
||||||
|
for i, r := range a.String()[3:] {
|
||||||
|
if i > 0 && r >= 'A' && r <= 'Z' {
|
||||||
|
name += "-"
|
||||||
|
}
|
||||||
|
name += string(r)
|
||||||
|
}
|
||||||
|
return strings.ToLower(name)
|
||||||
|
}
|
||||||
|
|
||||||
func processExecution(action actionType) bool {
|
func processExecution(action actionType) bool {
|
||||||
switch action {
|
switch action {
|
||||||
case actTransform,
|
case actTransform,
|
||||||
@@ -519,7 +534,6 @@ func defaultKeymap() map[tui.Event][]*action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(tui.Invalid, actInvalid)
|
add(tui.Invalid, actInvalid)
|
||||||
add(tui.Resize, actClearScreen)
|
|
||||||
add(tui.CtrlA, actBeginningOfLine)
|
add(tui.CtrlA, actBeginningOfLine)
|
||||||
add(tui.CtrlB, actBackwardChar)
|
add(tui.CtrlB, actBackwardChar)
|
||||||
add(tui.CtrlC, actAbort)
|
add(tui.CtrlC, actAbort)
|
||||||
@@ -730,6 +744,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
ellipsis: opts.Ellipsis,
|
ellipsis: opts.Ellipsis,
|
||||||
ansi: opts.Ansi,
|
ansi: opts.Ansi,
|
||||||
tabstop: opts.Tabstop,
|
tabstop: opts.Tabstop,
|
||||||
|
hasResultActions: false,
|
||||||
|
hasFocusActions: false,
|
||||||
hasLoadActions: false,
|
hasLoadActions: false,
|
||||||
triggerLoad: false,
|
triggerLoad: false,
|
||||||
reading: true,
|
reading: true,
|
||||||
@@ -757,7 +773,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
killChan: make(chan int),
|
killChan: make(chan int),
|
||||||
serverInputChan: make(chan []*action, 10),
|
serverInputChan: make(chan []*action, 10),
|
||||||
serverOutputChan: make(chan string),
|
serverOutputChan: make(chan string),
|
||||||
eventChan: make(chan tui.Event, 3), // load / zero|one | GetChar
|
eventChan: make(chan tui.Event, 6), // (load + result + zero|one) | (focus) | (resize) | (GetChar)
|
||||||
tui: renderer,
|
tui: renderer,
|
||||||
initFunc: func() { renderer.Init() },
|
initFunc: func() { renderer.Init() },
|
||||||
executing: util.NewAtomicBool(false),
|
executing: util.NewAtomicBool(false),
|
||||||
@@ -781,7 +797,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
||||||
}
|
}
|
||||||
if t.unicode {
|
if t.unicode {
|
||||||
t.borderWidth = runewidth.RuneWidth('│')
|
t.borderWidth = uniseg.StringWidth("│")
|
||||||
}
|
}
|
||||||
if opts.Scrollbar == nil {
|
if opts.Scrollbar == nil {
|
||||||
if t.unicode && t.borderWidth == 1 {
|
if t.unicode && t.borderWidth == 1 {
|
||||||
@@ -801,6 +817,9 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, t.hasResizeActions = t.keymap[tui.Resize.AsEvent()]
|
||||||
|
_, t.hasResultActions = t.keymap[tui.Result.AsEvent()]
|
||||||
|
_, t.hasFocusActions = t.keymap[tui.Focus.AsEvent()]
|
||||||
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
||||||
|
|
||||||
if t.listenAddr != nil {
|
if t.listenAddr != nil {
|
||||||
@@ -819,6 +838,14 @@ func (t *Terminal) environ() []string {
|
|||||||
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))
|
||||||
}
|
}
|
||||||
|
env = append(env, "FZF_QUERY="+string(t.input))
|
||||||
|
env = append(env, "FZF_ACTION="+t.lastAction.Name())
|
||||||
|
env = append(env, "FZF_PROMPT="+string(t.promptString))
|
||||||
|
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
|
||||||
|
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_LINES=%d", t.areaLines))
|
||||||
|
env = append(env, fmt.Sprintf("FZF_COLUMNS=%d", t.areaColumns))
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,6 +1100,9 @@ func (t *Terminal) UpdateList(merger *Merger) {
|
|||||||
t.eventChan <- one
|
t.eventChan <- one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if t.hasResultActions {
|
||||||
|
t.eventChan <- tui.Result.AsEvent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t.mutex.Unlock()
|
t.mutex.Unlock()
|
||||||
t.reqBox.Set(reqInfo, nil)
|
t.reqBox.Set(reqInfo, nil)
|
||||||
@@ -1282,6 +1312,9 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
|
|||||||
width -= paddingInt[1] + paddingInt[3]
|
width -= paddingInt[1] + paddingInt[3]
|
||||||
height -= paddingInt[0] + paddingInt[2]
|
height -= paddingInt[0] + paddingInt[2]
|
||||||
|
|
||||||
|
t.areaLines = height
|
||||||
|
t.areaColumns = width
|
||||||
|
|
||||||
// Set up preview window
|
// Set up preview window
|
||||||
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
|
||||||
if forcePreview || t.needPreviewWindow() {
|
if forcePreview || t.needPreviewWindow() {
|
||||||
@@ -2534,14 +2567,7 @@ func replacePlaceholder(params replacePlaceholderParams) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case match == "{fzf:action}":
|
case match == "{fzf:action}":
|
||||||
name := ""
|
return params.lastAction.Name()
|
||||||
for i, r := range params.lastAction.String()[3:] {
|
|
||||||
if i > 0 && r >= 'A' && r <= 'Z' {
|
|
||||||
name += "-"
|
|
||||||
}
|
|
||||||
name += string(r)
|
|
||||||
}
|
|
||||||
return strings.ToLower(name)
|
|
||||||
case match == "{fzf:prompt}":
|
case match == "{fzf:prompt}":
|
||||||
return quoteEntry(params.prompt)
|
return quoteEntry(params.prompt)
|
||||||
default:
|
default:
|
||||||
@@ -3070,9 +3096,9 @@ func (t *Terminal) Loop() {
|
|||||||
t.track = trackDisabled
|
t.track = trackDisabled
|
||||||
t.printInfo()
|
t.printInfo()
|
||||||
}
|
}
|
||||||
if onFocus, prs := t.keymap[tui.Focus.AsEvent()]; prs && focusChanged && currentIndex != t.lastFocus {
|
if t.hasFocusActions && focusChanged && currentIndex != t.lastFocus {
|
||||||
t.lastFocus = focusedIndex
|
t.lastFocus = currentIndex
|
||||||
t.serverInputChan <- onFocus
|
t.eventChan <- tui.Focus.AsEvent()
|
||||||
}
|
}
|
||||||
if focusChanged || version != t.version {
|
if focusChanged || version != t.version {
|
||||||
version = t.version
|
version = t.version
|
||||||
@@ -3104,6 +3130,9 @@ func (t *Terminal) Loop() {
|
|||||||
if wasHidden && t.hasPreviewWindow() {
|
if wasHidden && t.hasPreviewWindow() {
|
||||||
refreshPreview(t.previewOpts.command)
|
refreshPreview(t.previewOpts.command)
|
||||||
}
|
}
|
||||||
|
if req == reqResize && t.hasResizeActions {
|
||||||
|
t.eventChan <- tui.Resize.AsEvent()
|
||||||
|
}
|
||||||
case reqClose:
|
case reqClose:
|
||||||
exit(func() int {
|
exit(func() int {
|
||||||
if t.output() {
|
if t.output() {
|
||||||
@@ -3186,7 +3215,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case event = <-t.eventChan:
|
case event = <-t.eventChan:
|
||||||
needBarrier = !event.Is(tui.Load, tui.One, tui.Zero)
|
needBarrier = !event.Is(tui.Load, tui.Result, tui.Focus, tui.One, tui.Zero, tui.Resize)
|
||||||
case serverActions := <-t.serverInputChan:
|
case serverActions := <-t.serverInputChan:
|
||||||
event = tui.Invalid.AsEvent()
|
event = tui.Invalid.AsEvent()
|
||||||
if t.listenAddr == nil || t.listenAddr.IsLocal() || t.listenUnsafe {
|
if t.listenAddr == nil || t.listenAddr.IsLocal() || t.listenUnsafe {
|
||||||
|
@@ -10,8 +10,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/junegunn/uniseg"
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
|
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
@@ -804,7 +803,7 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
hw := runewidth.RuneWidth(w.border.top)
|
hw := runeWidth(w.border.top)
|
||||||
if top {
|
if top {
|
||||||
w.Move(0, 0)
|
w.Move(0, 0)
|
||||||
w.CPrint(color, repeat(w.border.top, w.width/hw))
|
w.CPrint(color, repeat(w.border.top, w.width/hw))
|
||||||
@@ -842,13 +841,13 @@ func (w *LightWindow) drawBorderAround(onlyHorizontal bool) {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
hw := runewidth.RuneWidth(w.border.top)
|
hw := runeWidth(w.border.top)
|
||||||
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
|
tcw := runeWidth(w.border.topLeft) + runeWidth(w.border.topRight)
|
||||||
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
|
bcw := runeWidth(w.border.bottomLeft) + runeWidth(w.border.bottomRight)
|
||||||
rem := (w.width - tcw) % hw
|
rem := (w.width - tcw) % hw
|
||||||
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.top, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
|
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.top, (w.width-tcw)/hw)+repeat(' ', rem)+string(w.border.topRight))
|
||||||
if !onlyHorizontal {
|
if !onlyHorizontal {
|
||||||
vw := runewidth.RuneWidth(w.border.left)
|
vw := runeWidth(w.border.left)
|
||||||
for y := 1; y < w.height-1; y++ {
|
for y := 1; y < w.height-1; y++ {
|
||||||
w.Move(y, 0)
|
w.Move(y, 0)
|
||||||
w.CPrint(color, string(w.border.left))
|
w.CPrint(color, string(w.border.left))
|
||||||
@@ -1020,7 +1019,7 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin
|
|||||||
} else if rs[0] == '\r' {
|
} else if rs[0] == '\r' {
|
||||||
w++
|
w++
|
||||||
} else {
|
} else {
|
||||||
w = runewidth.StringWidth(str)
|
w = uniseg.StringWidth(str)
|
||||||
}
|
}
|
||||||
width += w
|
width += w
|
||||||
|
|
||||||
|
@@ -10,8 +10,7 @@ import (
|
|||||||
"github.com/gdamore/tcell/v2/encoding"
|
"github.com/gdamore/tcell/v2/encoding"
|
||||||
"github.com/junegunn/fzf/src/util"
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/junegunn/uniseg"
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func HasFullscreenRenderer() bool {
|
func HasFullscreenRenderer() bool {
|
||||||
@@ -738,7 +737,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
|||||||
style = w.normal.style()
|
style = w.normal.style()
|
||||||
}
|
}
|
||||||
|
|
||||||
hw := runewidth.RuneWidth(w.borderStyle.top)
|
hw := runeWidth(w.borderStyle.top)
|
||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderHorizontal, BorderTop:
|
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderHorizontal, BorderTop:
|
||||||
max := right - 2*hw
|
max := right - 2*hw
|
||||||
@@ -773,7 +772,7 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
|||||||
}
|
}
|
||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderVertical, BorderRight:
|
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble, BorderVertical, BorderRight:
|
||||||
vw := runewidth.RuneWidth(w.borderStyle.right)
|
vw := runeWidth(w.borderStyle.right)
|
||||||
for y := top; y < bot; y++ {
|
for y := top; y < bot; y++ {
|
||||||
_screen.SetContent(right-vw, y, w.borderStyle.right, nil, style)
|
_screen.SetContent(right-vw, y, w.borderStyle.right, nil, style)
|
||||||
}
|
}
|
||||||
@@ -782,8 +781,8 @@ func (w *TcellWindow) drawBorder(onlyHorizontal bool) {
|
|||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble:
|
case BorderRounded, BorderSharp, BorderBold, BorderBlock, BorderThinBlock, BorderDouble:
|
||||||
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
||||||
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
|
_screen.SetContent(right-runeWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
|
||||||
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
||||||
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
|
_screen.SetContent(right-runeWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/junegunn/uniseg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Types of user action
|
// Types of user action
|
||||||
@@ -105,6 +107,7 @@ const (
|
|||||||
Focus
|
Focus
|
||||||
One
|
One
|
||||||
Zero
|
Zero
|
||||||
|
Result
|
||||||
|
|
||||||
AltBS
|
AltBS
|
||||||
|
|
||||||
@@ -811,3 +814,7 @@ func initPalette(theme *ColorTheme) {
|
|||||||
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
|
ColPreviewScrollbar = pair(theme.PreviewScrollbar, theme.PreviewBg)
|
||||||
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
|
ColPreviewSpinner = pair(theme.Spinner, theme.PreviewBg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runeWidth(r rune) int {
|
||||||
|
return uniseg.StringWidth(string(r))
|
||||||
|
}
|
||||||
|
@@ -6,14 +6,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/junegunn/uniseg"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"github.com/rivo/uniseg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// StringWidth returns string width where each CR/LF character takes 1 column
|
// StringWidth returns string width where each CR/LF character takes 1 column
|
||||||
func StringWidth(s string) int {
|
func StringWidth(s string) int {
|
||||||
return runewidth.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
|
return uniseg.StringWidth(s) + strings.Count(s, "\n") + strings.Count(s, "\r")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunesWidth returns runes width
|
// RunesWidth returns runes width
|
||||||
@@ -165,7 +164,7 @@ func RepeatToFill(str string, length int, limit int) string {
|
|||||||
output := strings.Repeat(str, times)
|
output := strings.Repeat(str, times)
|
||||||
if rest > 0 {
|
if rest > 0 {
|
||||||
for _, r := range str {
|
for _, r := range str {
|
||||||
rest -= runewidth.RuneWidth(r)
|
rest -= uniseg.StringWidth(string(r))
|
||||||
if rest < 0 {
|
if rest < 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@@ -164,6 +164,18 @@ func TestRunesWidth(t *testing.T) {
|
|||||||
t.Errorf("Expected overflow index: %d, actual: %d", args[2], overflowIdx)
|
t.Errorf("Expected overflow index: %d, actual: %d", args[2], overflowIdx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, input := range []struct {
|
||||||
|
s string
|
||||||
|
w int
|
||||||
|
}{
|
||||||
|
{"▶", 1},
|
||||||
|
{"▶️", 2},
|
||||||
|
} {
|
||||||
|
width, _ := RunesWidth([]rune(input.s), 0, 0, 100)
|
||||||
|
if width != input.w {
|
||||||
|
t.Errorf("Expected width of %s: %d, actual: %d", input.s, input.w, width)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTruncate(t *testing.T) {
|
func TestTruncate(t *testing.T) {
|
||||||
|
@@ -2711,12 +2711,26 @@ class TestGoFZF < TestBase
|
|||||||
tmux.until { |lines| assert_includes(lines[-1], '[[2]]') }
|
tmux.until { |lines| assert_includes(lines[-1], '[[2]]') }
|
||||||
tmux.send_keys :X
|
tmux.send_keys :X
|
||||||
tmux.until { |lines| assert_includes(lines[-1], '[[]]') }
|
tmux.until { |lines| assert_includes(lines[-1], '[[]]') }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_includes(lines[-1], '[[1]]') }
|
||||||
|
tmux.send_keys :X
|
||||||
|
tmux.until { |lines| assert_includes(lines[-1], '[[]]') }
|
||||||
tmux.send_keys '?'
|
tmux.send_keys '?'
|
||||||
tmux.send_keys :BSpace
|
tmux.send_keys :BSpace
|
||||||
tmux.until { |lines| assert_equal 100, lines.match_count }
|
tmux.until { |lines| assert_equal 100, lines.match_count }
|
||||||
tmux.until { |lines| refute_includes(lines[-1], '[[1]]') }
|
tmux.until { |lines| refute_includes(lines[-1], '[[1]]') }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_result_event
|
||||||
|
tmux.send_keys '(echo 0; seq 10) | fzf --bind "result:pos(2)"', :Enter
|
||||||
|
tmux.until { |lines| assert_equal 11, lines.item_count }
|
||||||
|
tmux.until { |lines| assert_includes lines, '> 1' }
|
||||||
|
tmux.send_keys '9'
|
||||||
|
tmux.until { |lines| assert_includes lines, '> 9' }
|
||||||
|
tmux.send_keys :BSpace
|
||||||
|
tmux.until { |lines| assert_includes lines, '> 1' }
|
||||||
|
end
|
||||||
|
|
||||||
def test_labels_center
|
def test_labels_center
|
||||||
tmux.send_keys 'echo x | fzf --border --border-label foobar --preview : --preview-label barfoo --bind "space:change-border-label(foobarfoo)+change-preview-label(barfoobar),enter:transform-border-label(echo foo{}foo)+transform-preview-label(echo bar{}bar)"', :Enter
|
tmux.send_keys 'echo x | fzf --border --border-label foobar --preview : --preview-label barfoo --bind "space:change-border-label(foobarfoo)+change-preview-label(barfoobar),enter:transform-border-label(echo foo{}foo)+transform-preview-label(echo bar{}bar)"', :Enter
|
||||||
tmux.until do
|
tmux.until do
|
||||||
|
Reference in New Issue
Block a user