mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-09 03:43:49 -05:00
Compare commits
86 Commits
leverage-b
...
zellij
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81366c548b | ||
|
|
b2c3e567da | ||
|
|
ca5e633399 | ||
|
|
e60a9a628b | ||
|
|
0167691941 | ||
|
|
3b0f976380 | ||
|
|
7bd298b536 | ||
|
|
0476a65fca | ||
|
|
2cb2af115a | ||
|
|
789226ff6d | ||
|
|
805efc5bf1 | ||
|
|
cdcab26766 | ||
|
|
ec3acb1932 | ||
|
|
d30e37434e | ||
|
|
20d5b2e20e | ||
|
|
6c6be4ab1a | ||
|
|
d004eb1f7c | ||
|
|
3148b0f3e8 | ||
|
|
3fc0bd26a5 | ||
|
|
6c9025ff17 | ||
|
|
289997e373 | ||
|
|
db44cbdff0 | ||
|
|
da9179335c | ||
|
|
cdf641fa3e | ||
|
|
66dbee10f5 | ||
|
|
19e9b620ba | ||
|
|
e4e4700aff | ||
|
|
bb55045596 | ||
|
|
d7e51cdeb5 | ||
|
|
7f4964b366 | ||
|
|
a6957aba11 | ||
|
|
b5f94f961d | ||
|
|
e182d3db7a | ||
|
|
3e6e0528a6 | ||
|
|
ac508a1ce4 | ||
|
|
d7fc1e09b1 | ||
|
|
3b0c86e401 | ||
|
|
61d10d8ffa | ||
|
|
7d9548919e | ||
|
|
bee80a730f | ||
|
|
ac3e24c99c | ||
|
|
e7e852bdb3 | ||
|
|
2b7f168571 | ||
|
|
5b3da1d878 | ||
|
|
99f1bc0177 | ||
|
|
ed76f076dd | ||
|
|
4d357d1063 | ||
|
|
961ae1541c | ||
|
|
add1aec685 | ||
|
|
03d6ba7496 | ||
|
|
71e4d5cc51 | ||
|
|
215ab48222 | ||
|
|
0c64c68781 | ||
|
|
3ec035c68b | ||
|
|
20c7dcfbca | ||
|
|
c1b8780b9c | ||
|
|
64c61603e9 | ||
|
|
57c08d925f | ||
|
|
51623a5f6a | ||
|
|
ca3f6181d7 | ||
|
|
9c94f9c3d0 | ||
|
|
4a85843bcf | ||
|
|
d4d9b99879 | ||
|
|
6816b7d95b | ||
|
|
acdf265d7a | ||
|
|
19495eb9bb | ||
|
|
bacc8609ee | ||
|
|
99163f5afa | ||
|
|
0607227730 | ||
|
|
d938fdc496 | ||
|
|
dcb4c3d84a | ||
|
|
82ebcd9209 | ||
|
|
ff1687744d | ||
|
|
782c870fb2 | ||
|
|
71fad63829 | ||
|
|
d65c6101a8 | ||
|
|
3c40b1bd51 | ||
|
|
90a8800bb5 | ||
|
|
97f1dae2d1 | ||
|
|
e54ec05709 | ||
|
|
a24eb99679 | ||
|
|
ad113d00b7 | ||
|
|
7bd5884d12 | ||
|
|
c3505858a6 | ||
|
|
e76aa37fd4 | ||
|
|
1a32220ca9 |
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@@ -7,4 +7,4 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: crate-ci/typos@v1.24.1
|
||||
- uses: crate-ci/typos@v1.28.2
|
||||
|
||||
51
CHANGELOG.md
51
CHANGELOG.md
@@ -1,6 +1,57 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.57.0
|
||||
------
|
||||
- You can now resize the preview window by dragging the border
|
||||
- Built-in walker improvements
|
||||
- `--walker-root` can take multiple directory arguments. e.g. `--walker-root include src lib`
|
||||
- `--walker-skip` can handle multi-component patterns. e.g. `--walker-skip target/build`
|
||||
- Removed long processing delay when displaying images in the preview window
|
||||
- `FZF_PREVIEW_*` environment variables are exported to all child processes (#4098)
|
||||
- Bug fixes in fish scripts
|
||||
|
||||
0.56.3
|
||||
------
|
||||
- Bug fixes in zsh scripts
|
||||
- fix(zsh): handle backtick trigger edge case (#4090)
|
||||
- revert(zsh): remove 'fc -RI' call in the history widget (#4093)
|
||||
- Thanks to @LangLangBart for the contributions
|
||||
|
||||
0.56.2
|
||||
------
|
||||
- Bug fixes
|
||||
- Fixed abnormal scrolling behavior when `--wrap` is set (#4083)
|
||||
- [zsh] Fixed warning message when `ksh_arrays` is set (#4084)
|
||||
|
||||
0.56.1
|
||||
------
|
||||
- Bug fixes and improvements
|
||||
- Fixed a race condition which would cause fzf to present stale results after `reload` (#4070)
|
||||
- `page-up` and `page-down` actions now work correctly with multi-line items (#4069)
|
||||
- `{n}` is allowed in `SCROLL` expression in `--preview-window` (#4079)
|
||||
- [zsh] Fixed regression in history loading with shared option (#4071)
|
||||
- [zsh] Better command extraction in zsh completion (#4082)
|
||||
- Thanks to @LangLangBart, @jaydee-coder, @alex-huff, and @vejkse for the contributions
|
||||
|
||||
0.56.0
|
||||
------
|
||||
- Added `--gap[=N]` option to display empty lines between items.
|
||||
- This can be useful to visually separate adjacent multi-line items.
|
||||
```sh
|
||||
# All bash functions, highlighted
|
||||
declare -f | perl -0777 -pe 's/^}\n/}\0/gm' |
|
||||
bat --plain --language bash --color always |
|
||||
fzf --read0 --ansi --reverse --multi --highlight-line --gap
|
||||
```
|
||||
- Or just to make the list easier to read. For single-line items, you probably want to set `--color gutter:-1` as well to hide the gutter.
|
||||
```sh
|
||||
fzf --info inline-right --gap --color gutter:-1
|
||||
```
|
||||
- Added `noinfo` option to `--preview-window` to hide the scroll indicator in the preview window
|
||||
- Bug fixes
|
||||
- Thanks to @LangLangBart, @akinomyoga, and @charlievieth for fixing the bugs
|
||||
|
||||
0.55.0
|
||||
------
|
||||
_Release highlights: https://junegunn.github.io/fzf/releases/0.55.0/_
|
||||
|
||||
@@ -8,5 +8,5 @@ RUN echo '. ~/.bashrc' >> ~/.bash_profile
|
||||
RUN rm -f /etc/bash.bashrc
|
||||
COPY . /fzf
|
||||
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 ]
|
||||
ENV LANG=C.UTF-8
|
||||
CMD ["bash", "-ic", "tmux new 'set -o pipefail; ruby /fzf/test/test_go.rb | tee out && touch ok' && cat out && [ -e ok ]"]
|
||||
|
||||
6
go.mod
6
go.mod
@@ -1,13 +1,13 @@
|
||||
module github.com/junegunn/fzf
|
||||
|
||||
require (
|
||||
github.com/charlievieth/fastwalk v1.0.8
|
||||
github.com/charlievieth/fastwalk v1.0.9
|
||||
github.com/gdamore/tcell/v2 v2.7.4
|
||||
github.com/junegunn/go-shellwords v0.0.0-20240813092932-a62c48c52e97
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/rivo/uniseg v0.4.7
|
||||
golang.org/x/sys v0.25.0
|
||||
golang.org/x/term v0.24.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/term v0.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
12
go.sum
12
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/charlievieth/fastwalk v1.0.8 h1:uaoH6cAKSk73aK7aKXqs0+bL+J3Txzd3NGH8tRXgHko=
|
||||
github.com/charlievieth/fastwalk v1.0.8/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
|
||||
github.com/charlievieth/fastwalk v1.0.9 h1:Odb92AfoReO3oFBfDGT5J+nwgzQPF/gWAw6E6/lkor0=
|
||||
github.com/charlievieth/fastwalk v1.0.9/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
|
||||
@@ -36,14 +36,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
||||
41
install
41
install
@@ -2,7 +2,7 @@
|
||||
|
||||
set -u
|
||||
|
||||
version=0.55.0
|
||||
version=0.57.0
|
||||
auto_completion=
|
||||
key_bindings=
|
||||
update_config=2
|
||||
@@ -295,35 +295,44 @@ EOF
|
||||
fi
|
||||
|
||||
append_line() {
|
||||
set -e
|
||||
|
||||
local update line file pat lno
|
||||
local update line file pat lines
|
||||
update="$1"
|
||||
line="$2"
|
||||
file="$3"
|
||||
pat="${4:-}"
|
||||
lno=""
|
||||
lines=""
|
||||
|
||||
echo "Update $file:"
|
||||
echo " - $line"
|
||||
if [ -f "$file" ]; then
|
||||
if [ $# -lt 4 ]; then
|
||||
lno=$(\grep -nF "$line" "$file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
lines=$(\grep -nF "$line" "$file")
|
||||
else
|
||||
lno=$(\grep -nF "$pat" "$file" | sed 's/:.*//' | tr '\n' ' ')
|
||||
lines=$(\grep -nF "$pat" "$file")
|
||||
fi
|
||||
fi
|
||||
if [ -n "$lno" ]; then
|
||||
echo " - Already exists: line #$lno"
|
||||
|
||||
if [ -n "$lines" ]; then
|
||||
echo " - Already exists:"
|
||||
sed 's/^/ Line /' <<< "$lines"
|
||||
|
||||
update=0
|
||||
if ! \grep -qv "^[0-9]*:[[:space:]]*#" <<< "$lines" ; then
|
||||
echo " - But they all seem to be commented"
|
||||
ask " - Continue modifying $file?"
|
||||
update=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
set -e
|
||||
if [ "$update" -eq 1 ]; then
|
||||
[ -f "$file" ] && echo >> "$file"
|
||||
echo "$line" >> "$file"
|
||||
echo " + Added"
|
||||
else
|
||||
if [ $update -eq 1 ]; then
|
||||
[ -f "$file" ] && echo >> "$file"
|
||||
echo "$line" >> "$file"
|
||||
echo " + Added"
|
||||
else
|
||||
echo " ~ Skipped"
|
||||
fi
|
||||
echo " ~ Skipped"
|
||||
fi
|
||||
|
||||
echo
|
||||
set +e
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
$version="0.55.0"
|
||||
$version="0.57.0"
|
||||
|
||||
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
||||
|
||||
2
main.go
2
main.go
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/junegunn/fzf/src/protector"
|
||||
)
|
||||
|
||||
var version = "0.55"
|
||||
var version = "0.57"
|
||||
var revision = "devel"
|
||||
|
||||
//go:embed shell/key-bindings.bash
|
||||
|
||||
@@ -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
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf\-tmux 1 "Aug 2024" "fzf 0.55.0" "fzf\-tmux - open fzf in tmux split pane"
|
||||
.TH fzf\-tmux 1 "Dec 2024" "fzf 0.57.0" "fzf\-tmux - open fzf in tmux split pane"
|
||||
|
||||
.SH NAME
|
||||
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
|
||||
THE SOFTWARE.
|
||||
..
|
||||
.TH fzf 1 "Sep 2024" "fzf 0.56.0" "fzf - a command-line fuzzy finder"
|
||||
.TH fzf 1 "Dec 2024" "fzf 0.57.0" "fzf - a command-line fuzzy finder"
|
||||
|
||||
.SH NAME
|
||||
fzf - a command-line fuzzy finder
|
||||
@@ -208,6 +208,9 @@ Indicator for wrapped lines. The default is '↳ ' or '> ' depending on
|
||||
.B "\-\-no\-multi\-line"
|
||||
Disable multi-line display of items when using \fB\-\-read0\fR
|
||||
.TP
|
||||
.BI "\-\-gap" "[=N]"
|
||||
Render empty lines between each item
|
||||
.TP
|
||||
.B "\-\-keep\-right"
|
||||
Keep the right end of the line visible when it's too long. Effective only when
|
||||
the query string is empty.
|
||||
@@ -268,22 +271,23 @@ Adaptive height has the following limitations:
|
||||
Minimum height when \fB\-\-height\fR is given in percent (default: 10).
|
||||
Ignored when \fB\-\-height\fR is not specified.
|
||||
.TP
|
||||
.BI "\-\-tmux" "[=[center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]]"
|
||||
Start fzf in a tmux popup (default \fBcenter,50%\fR). Requires tmux 3.3 or
|
||||
later. This option is ignored if you are not running fzf inside tmux.
|
||||
.BI "\-\-popup" "[=[center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]]"
|
||||
Start fzf in a tmux popup or in a zellij floating pane (default
|
||||
\fBcenter,50%\fR). Requires tmux 3.3+ or zellij. This option is ignored if you
|
||||
are not running fzf inside tmux or zellij.
|
||||
|
||||
e.g.
|
||||
\fB# Popup in the center with 70% width and height
|
||||
fzf \-\-tmux 70%
|
||||
fzf \-\-popup 70%
|
||||
|
||||
# Popup on the left with 40% width and 100% height
|
||||
fzf \-\-tmux right,40%
|
||||
fzf \-\-popup right,40%
|
||||
|
||||
# Popup on the bottom with 100% width and 30% height
|
||||
fzf \-\-tmux bottom,30%
|
||||
fzf \-\-popup bottom,30%
|
||||
|
||||
# Popup on the top with 80% width and 40% height
|
||||
fzf \-\-tmux top,80%,40%\fR
|
||||
fzf \-\-popup top,80%,40%\fR
|
||||
|
||||
.TP
|
||||
.BI "\-\-layout=" "LAYOUT"
|
||||
@@ -802,7 +806,7 @@ e.g. \fBborder\-rounded\fR (border with rounded edges, default),
|
||||
* \fB[:+SCROLL[OFFSETS][/DENOM]]\fR determines the initial scroll offset of the
|
||||
preview window.
|
||||
|
||||
- \fBSCROLL\fR can be either a numeric integer or a single-field index expression that refers to a numeric integer.
|
||||
- \fBSCROLL\fR can be either a numeric integer or a single-field index expression that refers to a numeric integer or {n} to refer to the zero-based ordinal index of the current item.
|
||||
|
||||
- The optional \fBOFFSETS\fR part is for adjusting the base offset. It should be given as a series of signed integers (\fB\-INTEGER\fR or \fB+INTEGER\fR).
|
||||
|
||||
@@ -1002,8 +1006,8 @@ Determines the behavior of the built-in directory walker that is used when
|
||||
.br
|
||||
|
||||
.TP
|
||||
.B "\-\-walker\-root=DIR"
|
||||
The root directory from which to start the built-in directory walker.
|
||||
.B "\-\-walker\-root=DIR [...]"
|
||||
List of directory names to start the built-in directory walker.
|
||||
The default value is the current working directory.
|
||||
|
||||
.TP
|
||||
@@ -1119,9 +1123,6 @@ fzf exports the following environment variables to its child processes.
|
||||
.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"
|
||||
@@ -1506,10 +1507,11 @@ A key or an event can be bound to one or more of the following actions.
|
||||
\fBshow\-preview\fR
|
||||
\fBtoggle\fR (\fIright\-click\fR)
|
||||
\fBtoggle\-all\fR (toggle all matches)
|
||||
\fBtoggle+down\fR \fIctrl\-i (tab)\fR
|
||||
\fBtoggle\-header\fR
|
||||
\fBtoggle\-in\fR (\fB\-\-layout=reverse*\fR ? \fBtoggle+up\fR : \fBtoggle+down\fR)
|
||||
\fBtoggle\-out\fR (\fB\-\-layout=reverse*\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR)
|
||||
\fBtoggle\-header\fR
|
||||
\fBtoggle\-hscroll\fR
|
||||
\fBtoggle\-multi\-line\fR
|
||||
\fBtoggle\-preview\fR
|
||||
\fBtoggle\-preview\-wrap\fR
|
||||
\fBtoggle\-search\fR (toggle search functionality)
|
||||
@@ -1517,6 +1519,7 @@ A key or an event can be bound to one or more of the following actions.
|
||||
\fBtoggle\-track\fR (toggle global tracking option (\fB\-\-track\fR))
|
||||
\fBtoggle\-track\-current\fR (toggle tracking of the current item)
|
||||
\fBtoggle\-wrap\fR \fIctrl\-/\fR \fIalt\-/\fR
|
||||
\fBtoggle+down\fR \fIctrl\-i (tab)\fR
|
||||
\fBtoggle+up\fR \fIbtab (shift\-tab)\fR
|
||||
\fBtrack\-current\fR (track the current item; automatically disabled if focus changes)
|
||||
\fBtransform(...)\fR (transform states using the output of an external command)
|
||||
|
||||
@@ -377,7 +377,7 @@ _fzf_complete() {
|
||||
selected=$(
|
||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
|
||||
FZF_DEFAULT_OPTS_FILE='' \
|
||||
__fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $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"
|
||||
if [[ -n "$selected" ]]; then
|
||||
COMPREPLY=("$selected")
|
||||
|
||||
@@ -120,25 +120,18 @@ __fzf_comprun() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Extract the name of the command. e.g. foo=1 bar baz**<tab>
|
||||
# Extract the name of the command. e.g. ls; foo=1 ssh **<tab>
|
||||
__fzf_extract_command() {
|
||||
local token tokens
|
||||
tokens=(${(z)1})
|
||||
for token in $tokens; do
|
||||
token=${(Q)token}
|
||||
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
|
||||
echo "$token"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo "${tokens[1]}"
|
||||
# Control completion with the "compstate" parameter, insert and list nothing
|
||||
compstate[insert]=
|
||||
compstate[list]=
|
||||
cmd_word="${(Q)words[1]}"
|
||||
}
|
||||
|
||||
__fzf_generic_path_completion() {
|
||||
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
|
||||
local base lbuf compgen fzf_opts suffix tail dir leftover matches
|
||||
base=$1
|
||||
lbuf=$2
|
||||
cmd=$(__fzf_extract_command "$lbuf")
|
||||
compgen=$3
|
||||
fzf_opts=$4
|
||||
suffix=$5
|
||||
@@ -161,7 +154,7 @@ __fzf_generic_path_completion() {
|
||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse --scheme=path" "${FZF_COMPLETION_OPTS-}")
|
||||
unset FZF_DEFAULT_COMMAND FZF_DEFAULT_OPTS_FILE
|
||||
if declare -f "$compgen" > /dev/null; then
|
||||
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
|
||||
eval "$compgen $(printf %q "$dir")" | __fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover"
|
||||
else
|
||||
if [[ $compgen =~ dir ]]; then
|
||||
walker=dir,follow
|
||||
@@ -170,7 +163,7 @@ __fzf_generic_path_completion() {
|
||||
walker=file,dir,follow,hidden
|
||||
rest=${FZF_COMPLETION_PATH_OPTS-}
|
||||
fi
|
||||
__fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
|
||||
__fzf_comprun "$cmd_word" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" --walker "$walker" --walker-root="$dir" ${(Q)${(Z+n+)rest}} < /dev/tty
|
||||
fi | while read -r item; do
|
||||
item="${item%$suffix}$suffix"
|
||||
echo -n -E "${(q)item} "
|
||||
@@ -227,10 +220,9 @@ _fzf_complete() {
|
||||
rest=("$@")
|
||||
fi
|
||||
|
||||
local fifo lbuf cmd matches post
|
||||
local fifo lbuf matches post
|
||||
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
||||
lbuf=${rest[0]}
|
||||
cmd=$(__fzf_extract_command "$lbuf")
|
||||
post="${funcstack[1]}_post"
|
||||
type $post > /dev/null 2>&1 || post=cat
|
||||
|
||||
@@ -238,7 +230,7 @@ _fzf_complete() {
|
||||
matches=$(
|
||||
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
|
||||
FZF_DEFAULT_OPTS_FILE='' \
|
||||
__fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||
__fzf_comprun "$cmd_word" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||
if [ -n "$matches" ]; then
|
||||
LBUFFER="$lbuf$matches"
|
||||
fi
|
||||
@@ -310,7 +302,7 @@ _fzf_complete_kill_post() {
|
||||
}
|
||||
|
||||
fzf-completion() {
|
||||
local tokens cmd prefix trigger tail matches lbuf d_cmds
|
||||
local tokens prefix trigger tail matches lbuf d_cmds cursor_pos cmd_word
|
||||
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
||||
|
||||
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
|
||||
@@ -321,11 +313,9 @@ fzf-completion() {
|
||||
return
|
||||
fi
|
||||
|
||||
cmd=$(__fzf_extract_command "$LBUFFER")
|
||||
|
||||
# Explicitly allow for empty trigger.
|
||||
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
|
||||
[[ -z $trigger && ${LBUFFER[-1]} == ' ' ]] && tokens+=("")
|
||||
|
||||
# When the trigger starts with ';', it becomes a separate token
|
||||
if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
|
||||
@@ -340,16 +330,37 @@ fzf-completion() {
|
||||
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
||||
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS-cd pushd rmdir})
|
||||
|
||||
{
|
||||
cursor_pos=$CURSOR
|
||||
# Move the cursor before the trigger to preserve word array elements when
|
||||
# trigger chars like ';' or '`' would otherwise reset the 'words' array.
|
||||
CURSOR=$((cursor_pos - ${#trigger} - 1))
|
||||
# Check if at least one completion system (old or new) is active.
|
||||
# If at least one user-defined completion widget is detected, nothing will
|
||||
# be completed if neither the old nor the new completion system is enabled.
|
||||
# In such cases, the 'zsh/compctl' module is loaded as a fallback.
|
||||
if ! zmodload -F zsh/parameter p:functions 2>/dev/null || ! (( ${+functions[compdef]} )); then
|
||||
zmodload -F zsh/compctl 2>/dev/null
|
||||
fi
|
||||
# Create a completion widget to access the 'words' array (man zshcompwid)
|
||||
zle -C __fzf_extract_command .complete-word __fzf_extract_command
|
||||
zle __fzf_extract_command
|
||||
} always {
|
||||
CURSOR=$cursor_pos
|
||||
# Delete the completion widget
|
||||
zle -D __fzf_extract_command 2>/dev/null
|
||||
}
|
||||
|
||||
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
|
||||
if [[ $prefix = *'$('* ]] || [[ $prefix = *'<('* ]] || [[ $prefix = *'>('* ]] || [[ $prefix = *':='* ]] || [[ $prefix = *'`'* ]]; then
|
||||
return
|
||||
fi
|
||||
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
|
||||
|
||||
if eval "type _fzf_complete_${cmd} > /dev/null"; then
|
||||
prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
|
||||
if eval "noglob type _fzf_complete_${cmd_word} >/dev/null"; then
|
||||
prefix="$prefix" eval _fzf_complete_${cmd_word} ${(q)lbuf}
|
||||
zle reset-prompt
|
||||
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
|
||||
elif [ ${d_cmds[(i)$cmd_word]} -le ${#d_cmds} ]; then
|
||||
_fzf_dir_completion "$prefix" "$lbuf"
|
||||
else
|
||||
_fzf_path_completion "$prefix" "$lbuf"
|
||||
@@ -366,6 +377,7 @@ fzf-completion() {
|
||||
unset binding
|
||||
}
|
||||
|
||||
# Normal widget
|
||||
zle -N fzf-completion
|
||||
bindkey '^I' fzf-completion
|
||||
fi
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
# - $FZF_ALT_C_COMMAND
|
||||
# - $FZF_ALT_C_OPTS
|
||||
|
||||
status is-interactive; or exit 0
|
||||
|
||||
|
||||
# Key bindings
|
||||
# ------------
|
||||
@@ -23,7 +21,7 @@ function fzf_key_bindings
|
||||
# $2: Append to FZF_DEFAULT_OPTS_FILE and FZF_DEFAULT_OPTS
|
||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||
echo "--height $FZF_TMUX_HEIGHT --bind=ctrl-z:ignore" $argv[1]
|
||||
command cat "$FZF_DEFAULT_OPTS_FILE" 2> /dev/null
|
||||
test -r "$FZF_DEFAULT_OPTS_FILE"; and string collect -N -- <$FZF_DEFAULT_OPTS_FILE
|
||||
echo $FZF_DEFAULT_OPTS $argv[2]
|
||||
end
|
||||
|
||||
@@ -36,12 +34,12 @@ function fzf_key_bindings
|
||||
|
||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||
begin
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path --walker-root='$dir'" "$FZF_CTRL_T_OPTS")
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --walker=file,dir,follow,hidden --scheme=path --walker-root=$dir" "$FZF_CTRL_T_OPTS")
|
||||
set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND"
|
||||
set -lx FZF_DEFAULT_OPTS_FILE ''
|
||||
eval (__fzfcmd)' -m --query "'$fzf_query'"' | while read -l r; set result $result $r; end
|
||||
eval (__fzfcmd) -m --query=$fzf_query | while read -l r; set -a result $r; end
|
||||
end
|
||||
if [ -z "$result" ]
|
||||
if test -z "$result"
|
||||
commandline -f repaint
|
||||
return
|
||||
else
|
||||
@@ -50,7 +48,7 @@ function fzf_key_bindings
|
||||
end
|
||||
for i in $result
|
||||
commandline -it -- $prefix
|
||||
commandline -it -- (string escape $i)
|
||||
commandline -it -- (string escape -- $i)
|
||||
commandline -it -- ' '
|
||||
end
|
||||
commandline -f repaint
|
||||
@@ -59,27 +57,27 @@ function fzf_key_bindings
|
||||
function fzf-history-widget -d "Show command history"
|
||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||
begin
|
||||
set -l FISH_MAJOR (echo $version | cut -f1 -d.)
|
||||
set -l FISH_MINOR (echo $version | cut -f2 -d.)
|
||||
set -l FISH_MAJOR (string split -- '.' $version)[1]
|
||||
set -l FISH_MINOR (string split -- '.' $version)[2]
|
||||
|
||||
# merge history from other sessions before searching
|
||||
if test -z "$fish_private_mode"
|
||||
builtin history merge
|
||||
end
|
||||
test -z "$fish_private_mode"; and builtin history merge
|
||||
|
||||
# history's -z flag is needed for multi-line support.
|
||||
# history's -z flag was added in fish 2.4.0, so don't use it for versions
|
||||
# before 2.4.0.
|
||||
if [ "$FISH_MAJOR" -gt 2 -o \( "$FISH_MAJOR" -eq 2 -a "$FISH_MINOR" -ge 4 \) ];
|
||||
if type -P perl > /dev/null 2>&1
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"\t"↳ ' --highlight-line $FZF_CTRL_R_OPTS +m")
|
||||
set -lx FZF_DEFAULT_OPTS_FILE ''
|
||||
builtin history -z --reverse | command perl -0 -pe 's/^/$.\t/g; s/\n/\n\t/gm' | eval (__fzfcmd) --tac --read0 --print0 -q '(commandline)' | command perl -pe 's/^\d*\t//' | read -lz result
|
||||
if test "$FISH_MAJOR" -gt 2 -o \( "$FISH_MAJOR" -eq 2 -a "$FISH_MINOR" -ge 4 \)
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"\t"↳ ' --highlight-line $FZF_CTRL_R_OPTS +m")
|
||||
set -lx FZF_DEFAULT_OPTS_FILE ''
|
||||
if type -q perl
|
||||
builtin history -z --reverse | command perl -0 -pe 's/^/$.\t/g; s/\n/\n\t/gm' | eval (__fzfcmd) --tac --read0 --print0 -q '(commandline)' | string replace -r '^\d*\t' '' | read -lz result
|
||||
and commandline -- $result
|
||||
else
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "" "--scheme=history --bind=ctrl-r:toggle-sort --wrap-sign '"\t"↳ ' --highlight-line $FZF_CTRL_R_OPTS +m")
|
||||
set -lx FZF_DEFAULT_OPTS_FILE ''
|
||||
builtin history -z | eval (__fzfcmd) --read0 --print0 -q '(commandline)' | read -lz result
|
||||
set -l line 0
|
||||
for i in (builtin history -z --reverse | string split0)
|
||||
set line (math $line + 1)
|
||||
string escape -n -- $line\t$i
|
||||
end | string join0 | string replace -a '\n' '\n\t' | string unescape -n | eval (__fzfcmd) --tac --read0 --print0 -q '(commandline)' | string replace -r '^\d*\t' '' | read -lz result
|
||||
and commandline -- $result
|
||||
end
|
||||
else
|
||||
@@ -98,12 +96,12 @@ function fzf_key_bindings
|
||||
|
||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||
begin
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path --walker-root='$dir'" "$FZF_ALT_C_OPTS")
|
||||
set -lx FZF_DEFAULT_OPTS (__fzf_defaults "--reverse --walker=dir,follow,hidden --scheme=path --walker-root=$dir" "$FZF_ALT_C_OPTS")
|
||||
set -lx FZF_DEFAULT_OPTS_FILE ''
|
||||
set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND"
|
||||
eval (__fzfcmd)' +m --query "'$fzf_query'"' | read -l result
|
||||
eval (__fzfcmd) +m --query=$fzf_query | read -l result
|
||||
|
||||
if [ -n "$result" ]
|
||||
if test -n "$result"
|
||||
cd -- $result
|
||||
|
||||
# Remove last token from commandline.
|
||||
@@ -118,9 +116,9 @@ function fzf_key_bindings
|
||||
function __fzfcmd
|
||||
test -n "$FZF_TMUX"; or set FZF_TMUX 0
|
||||
test -n "$FZF_TMUX_HEIGHT"; or set FZF_TMUX_HEIGHT 40%
|
||||
if [ -n "$FZF_TMUX_OPTS" ]
|
||||
if test -n "$FZF_TMUX_OPTS"
|
||||
echo "fzf-tmux $FZF_TMUX_OPTS -- "
|
||||
else if [ $FZF_TMUX -eq 1 ]
|
||||
else if test "$FZF_TMUX" = "1"
|
||||
echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- "
|
||||
else
|
||||
echo "fzf"
|
||||
@@ -135,7 +133,7 @@ function fzf_key_bindings
|
||||
bind \ec fzf-cd-widget
|
||||
end
|
||||
|
||||
if bind -M insert > /dev/null 2>&1
|
||||
if bind -M insert &> /dev/null
|
||||
bind -M insert \cr fzf-history-widget
|
||||
if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND"
|
||||
bind -M insert \ct fzf-file-widget
|
||||
@@ -152,40 +150,53 @@ function fzf_key_bindings
|
||||
set -l prefix (string match -r -- '^-[^\s=]+=' $commandline)
|
||||
set commandline (string replace -- "$prefix" '' $commandline)
|
||||
|
||||
# Enable home directory expansion of leading ~/
|
||||
set commandline (string replace -r -- '^~/' '\$HOME/' $commandline)
|
||||
|
||||
# escape special characters, except for the $ sign of valid variable names,
|
||||
# so that after eval, the original string is returned, but with the
|
||||
# variable names replaced by their values.
|
||||
set commandline (string escape -n -- $commandline)
|
||||
set commandline (string replace -r -a -- '\x5c\$(?=[\w])' '\$' $commandline)
|
||||
|
||||
# eval is used to do shell expansion on paths
|
||||
eval set commandline $commandline
|
||||
|
||||
if [ -z $commandline ]
|
||||
# Combine multiple consecutive slashes into one
|
||||
set commandline (string replace -r -a -- '/+' '/' $commandline)
|
||||
|
||||
if test -z "$commandline"
|
||||
# Default to current directory with no --query
|
||||
set dir '.'
|
||||
set fzf_query ''
|
||||
else
|
||||
set dir (__fzf_get_dir $commandline)
|
||||
|
||||
if [ "$dir" = "." -a (string sub -l 1 -- $commandline) != '.' ]
|
||||
# BUG: on combined expressions, if a left argument is a single `!`, the
|
||||
# builtin test command of fish will treat it as the ! operator. To
|
||||
# overcome this, have the variable parts on the right.
|
||||
if test "." = "$dir" -a "./" != (string sub -l 2 -- $commandline)
|
||||
# if $dir is "." but commandline is not a relative path, this means no file path found
|
||||
set fzf_query $commandline
|
||||
else
|
||||
# Also remove trailing slash after dir, to "split" input properly
|
||||
set fzf_query (string replace -r "^$dir/?" -- '' "$commandline")
|
||||
set fzf_query (string replace -r -- "^$dir/?" '' $commandline)
|
||||
end
|
||||
end
|
||||
|
||||
echo $dir
|
||||
echo $fzf_query
|
||||
echo (string escape -- $dir)
|
||||
echo (string escape -- $fzf_query)
|
||||
echo $prefix
|
||||
end
|
||||
|
||||
function __fzf_get_dir -d 'Find the longest existing filepath from input string'
|
||||
set dir $argv
|
||||
|
||||
# Strip all trailing slashes. Ignore if $dir is root dir (/)
|
||||
if [ (string length -- $dir) -gt 1 ]
|
||||
set dir (string replace -r '/*$' -- '' $dir)
|
||||
end
|
||||
# Strip trailing slash, unless $dir is root dir (/)
|
||||
set dir (string replace -r -- '(?<!^)/$' '' $dir)
|
||||
|
||||
# Iteratively check if dir exists and strip tail end of path
|
||||
while [ ! -d "$dir" ]
|
||||
while test ! -d "$dir"
|
||||
# If path is absolute, this can keep going until ends up at /
|
||||
# If path is relative, this can keep going until entire input is consumed, dirname returns "."
|
||||
set dir (dirname -- "$dir")
|
||||
|
||||
@@ -108,9 +108,10 @@ fi
|
||||
fzf-history-widget() {
|
||||
local selected
|
||||
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases noglob nobash_rematch 2> /dev/null
|
||||
# Ensure the associative history array, which maps event numbers to the full
|
||||
# history lines, is loaded, and that Perl is installed for multi-line output.
|
||||
if zmodload -F zsh/parameter p:history 2>/dev/null && (( ${#commands[perl]} )); then
|
||||
# Ensure the module is loaded if not already, and the required features, such
|
||||
# as the associative 'history' array, which maps event numbers to full history
|
||||
# lines, are set. Also, make sure Perl is installed for multi-line output.
|
||||
if zmodload -F zsh/parameter p:{commands,history} 2>/dev/null && (( ${+commands[perl]} )); then
|
||||
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; }' |
|
||||
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") \
|
||||
|
||||
@@ -59,73 +59,75 @@ func _() {
|
||||
_ = x[actToggleTrackCurrent-48]
|
||||
_ = x[actToggleHeader-49]
|
||||
_ = x[actToggleWrap-50]
|
||||
_ = x[actTrackCurrent-51]
|
||||
_ = x[actUntrackCurrent-52]
|
||||
_ = x[actDown-53]
|
||||
_ = x[actUp-54]
|
||||
_ = x[actPageUp-55]
|
||||
_ = x[actPageDown-56]
|
||||
_ = x[actPosition-57]
|
||||
_ = x[actHalfPageUp-58]
|
||||
_ = x[actHalfPageDown-59]
|
||||
_ = x[actOffsetUp-60]
|
||||
_ = x[actOffsetDown-61]
|
||||
_ = x[actOffsetMiddle-62]
|
||||
_ = x[actJump-63]
|
||||
_ = x[actJumpAccept-64]
|
||||
_ = x[actPrintQuery-65]
|
||||
_ = x[actRefreshPreview-66]
|
||||
_ = x[actReplaceQuery-67]
|
||||
_ = x[actToggleSort-68]
|
||||
_ = x[actShowPreview-69]
|
||||
_ = x[actHidePreview-70]
|
||||
_ = x[actTogglePreview-71]
|
||||
_ = x[actTogglePreviewWrap-72]
|
||||
_ = x[actTransform-73]
|
||||
_ = x[actTransformBorderLabel-74]
|
||||
_ = x[actTransformHeader-75]
|
||||
_ = x[actTransformPreviewLabel-76]
|
||||
_ = x[actTransformPrompt-77]
|
||||
_ = x[actTransformQuery-78]
|
||||
_ = x[actPreview-79]
|
||||
_ = x[actChangePreview-80]
|
||||
_ = x[actChangePreviewWindow-81]
|
||||
_ = x[actPreviewTop-82]
|
||||
_ = x[actPreviewBottom-83]
|
||||
_ = x[actPreviewUp-84]
|
||||
_ = x[actPreviewDown-85]
|
||||
_ = x[actPreviewPageUp-86]
|
||||
_ = x[actPreviewPageDown-87]
|
||||
_ = x[actPreviewHalfPageUp-88]
|
||||
_ = x[actPreviewHalfPageDown-89]
|
||||
_ = x[actPrevHistory-90]
|
||||
_ = x[actPrevSelected-91]
|
||||
_ = x[actPrint-92]
|
||||
_ = x[actPut-93]
|
||||
_ = x[actNextHistory-94]
|
||||
_ = x[actNextSelected-95]
|
||||
_ = x[actExecute-96]
|
||||
_ = x[actExecuteSilent-97]
|
||||
_ = x[actExecuteMulti-98]
|
||||
_ = x[actSigStop-99]
|
||||
_ = x[actFirst-100]
|
||||
_ = x[actLast-101]
|
||||
_ = x[actReload-102]
|
||||
_ = x[actReloadSync-103]
|
||||
_ = x[actDisableSearch-104]
|
||||
_ = x[actEnableSearch-105]
|
||||
_ = x[actSelect-106]
|
||||
_ = x[actDeselect-107]
|
||||
_ = x[actUnbind-108]
|
||||
_ = x[actRebind-109]
|
||||
_ = x[actBecome-110]
|
||||
_ = x[actShowHeader-111]
|
||||
_ = x[actHideHeader-112]
|
||||
_ = x[actToggleMultiLine-51]
|
||||
_ = x[actToggleHscroll-52]
|
||||
_ = x[actTrackCurrent-53]
|
||||
_ = x[actUntrackCurrent-54]
|
||||
_ = x[actDown-55]
|
||||
_ = x[actUp-56]
|
||||
_ = x[actPageUp-57]
|
||||
_ = x[actPageDown-58]
|
||||
_ = x[actPosition-59]
|
||||
_ = x[actHalfPageUp-60]
|
||||
_ = x[actHalfPageDown-61]
|
||||
_ = x[actOffsetUp-62]
|
||||
_ = x[actOffsetDown-63]
|
||||
_ = x[actOffsetMiddle-64]
|
||||
_ = x[actJump-65]
|
||||
_ = x[actJumpAccept-66]
|
||||
_ = x[actPrintQuery-67]
|
||||
_ = x[actRefreshPreview-68]
|
||||
_ = x[actReplaceQuery-69]
|
||||
_ = x[actToggleSort-70]
|
||||
_ = x[actShowPreview-71]
|
||||
_ = x[actHidePreview-72]
|
||||
_ = x[actTogglePreview-73]
|
||||
_ = x[actTogglePreviewWrap-74]
|
||||
_ = x[actTransform-75]
|
||||
_ = x[actTransformBorderLabel-76]
|
||||
_ = x[actTransformHeader-77]
|
||||
_ = x[actTransformPreviewLabel-78]
|
||||
_ = x[actTransformPrompt-79]
|
||||
_ = x[actTransformQuery-80]
|
||||
_ = x[actPreview-81]
|
||||
_ = x[actChangePreview-82]
|
||||
_ = x[actChangePreviewWindow-83]
|
||||
_ = x[actPreviewTop-84]
|
||||
_ = x[actPreviewBottom-85]
|
||||
_ = x[actPreviewUp-86]
|
||||
_ = x[actPreviewDown-87]
|
||||
_ = x[actPreviewPageUp-88]
|
||||
_ = x[actPreviewPageDown-89]
|
||||
_ = x[actPreviewHalfPageUp-90]
|
||||
_ = x[actPreviewHalfPageDown-91]
|
||||
_ = x[actPrevHistory-92]
|
||||
_ = x[actPrevSelected-93]
|
||||
_ = x[actPrint-94]
|
||||
_ = x[actPut-95]
|
||||
_ = x[actNextHistory-96]
|
||||
_ = x[actNextSelected-97]
|
||||
_ = x[actExecute-98]
|
||||
_ = x[actExecuteSilent-99]
|
||||
_ = x[actExecuteMulti-100]
|
||||
_ = x[actSigStop-101]
|
||||
_ = x[actFirst-102]
|
||||
_ = x[actLast-103]
|
||||
_ = x[actReload-104]
|
||||
_ = x[actReloadSync-105]
|
||||
_ = x[actDisableSearch-106]
|
||||
_ = x[actEnableSearch-107]
|
||||
_ = x[actSelect-108]
|
||||
_ = x[actDeselect-109]
|
||||
_ = x[actUnbind-110]
|
||||
_ = x[actRebind-111]
|
||||
_ = x[actBecome-112]
|
||||
_ = x[actShowHeader-113]
|
||||
_ = x[actHideHeader-114]
|
||||
}
|
||||
|
||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
|
||||
const _actionType_name = "actIgnoreactStartactClickactInvalidactCharactMouseactBeginningOfLineactAbortactAcceptactAcceptNonEmptyactAcceptOrPrintQueryactBackwardCharactBackwardDeleteCharactBackwardDeleteCharEofactBackwardWordactCancelactChangeBorderLabelactChangeHeaderactChangeMultiactChangePreviewLabelactChangePromptactChangeQueryactClearScreenactClearQueryactClearSelectionactCloseactDeleteCharactDeleteCharEofactEndOfLineactFatalactForwardCharactForwardWordactKillLineactKillWordactUnixLineDiscardactUnixWordRuboutactYankactBackwardKillWordactSelectAllactDeselectAllactToggleactToggleSearchactToggleAllactToggleDownactToggleUpactToggleInactToggleOutactToggleTrackactToggleTrackCurrentactToggleHeaderactToggleWrapactToggleMultiLineactToggleHscrollactTrackCurrentactUntrackCurrentactDownactUpactPageUpactPageDownactPositionactHalfPageUpactHalfPageDownactOffsetUpactOffsetDownactOffsetMiddleactJumpactJumpAcceptactPrintQueryactRefreshPreviewactReplaceQueryactToggleSortactShowPreviewactHidePreviewactTogglePreviewactTogglePreviewWrapactTransformactTransformBorderLabelactTransformHeaderactTransformPreviewLabelactTransformPromptactTransformQueryactPreviewactChangePreviewactChangePreviewWindowactPreviewTopactPreviewBottomactPreviewUpactPreviewDownactPreviewPageUpactPreviewPageDownactPreviewHalfPageUpactPreviewHalfPageDownactPrevHistoryactPrevSelectedactPrintactPutactNextHistoryactNextSelectedactExecuteactExecuteSilentactExecuteMultiactSigStopactFirstactLastactReloadactReloadSyncactDisableSearchactEnableSearchactSelectactDeselectactUnbindactRebindactBecomeactShowHeaderactHideHeader"
|
||||
|
||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 407, 421, 435, 446, 457, 475, 492, 499, 518, 530, 544, 553, 568, 580, 593, 604, 615, 627, 641, 662, 677, 690, 705, 722, 729, 734, 743, 754, 765, 778, 793, 804, 817, 832, 839, 852, 865, 882, 897, 910, 924, 938, 954, 974, 986, 1009, 1027, 1051, 1069, 1086, 1096, 1112, 1134, 1147, 1163, 1175, 1189, 1205, 1223, 1243, 1265, 1279, 1294, 1302, 1308, 1322, 1337, 1347, 1363, 1378, 1388, 1396, 1403, 1412, 1425, 1441, 1456, 1465, 1476, 1485, 1494, 1503, 1516, 1529}
|
||||
var _actionType_index = [...]uint16{0, 9, 17, 25, 35, 42, 50, 68, 76, 85, 102, 123, 138, 159, 183, 198, 207, 227, 242, 256, 277, 292, 306, 320, 333, 350, 358, 371, 387, 399, 407, 421, 435, 446, 457, 475, 492, 499, 518, 530, 544, 553, 568, 580, 593, 604, 615, 627, 641, 662, 677, 690, 708, 724, 739, 756, 763, 768, 777, 788, 799, 812, 827, 838, 851, 866, 873, 886, 899, 916, 931, 944, 958, 972, 988, 1008, 1020, 1043, 1061, 1085, 1103, 1120, 1130, 1146, 1168, 1181, 1197, 1209, 1223, 1239, 1257, 1277, 1299, 1313, 1328, 1336, 1342, 1356, 1371, 1381, 1397, 1412, 1422, 1430, 1437, 1446, 1459, 1475, 1490, 1499, 1510, 1519, 1528, 1537, 1550, 1563}
|
||||
|
||||
func (i actionType) String() string {
|
||||
if i < 0 || i >= actionType(len(_actionType_index)-1) {
|
||||
|
||||
19
src/core.go
19
src/core.go
@@ -39,8 +39,13 @@ func (r revision) compatible(other revision) bool {
|
||||
// Run starts fzf
|
||||
func Run(opts *Options) (int, error) {
|
||||
if opts.Filter == nil {
|
||||
if opts.Tmux != nil && len(os.Getenv("TMUX")) > 0 && opts.Tmux.index >= opts.Height.index {
|
||||
return runTmux(os.Args, opts)
|
||||
if opts.Tmux != nil && opts.Tmux.index >= opts.Height.index {
|
||||
if len(os.Getenv("TMUX")) > 0 {
|
||||
return runTmux(os.Args, opts)
|
||||
}
|
||||
if len(os.Getenv("ZELLIJ")) > 0 {
|
||||
return runZellij(os.Args, opts)
|
||||
}
|
||||
}
|
||||
|
||||
if needWinpty(opts) {
|
||||
@@ -172,7 +177,9 @@ func Run(opts *Options) (int, error) {
|
||||
return chunkList.Push(data)
|
||||
}, eventBox, executor, opts.ReadZero, opts.Filter == nil)
|
||||
|
||||
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv)
|
||||
readyChan := make(chan bool)
|
||||
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv, readyChan)
|
||||
<-readyChan
|
||||
}
|
||||
|
||||
// Matcher
|
||||
@@ -224,7 +231,7 @@ func Run(opts *Options) (int, error) {
|
||||
}
|
||||
return false
|
||||
}, eventBox, executor, opts.ReadZero, false)
|
||||
reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv)
|
||||
reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv, nil)
|
||||
} else {
|
||||
eventBox.Unwatch(EvtReadNew)
|
||||
eventBox.WaitFor(EvtReadFin)
|
||||
@@ -299,7 +306,9 @@ func Run(opts *Options) (int, error) {
|
||||
itemIndex = 0
|
||||
inputRevision.bumpMajor()
|
||||
header = make([]string, 0, opts.HeaderLines)
|
||||
go reader.restart(command, environ)
|
||||
readyChan := make(chan bool)
|
||||
go reader.restart(command, environ, readyChan)
|
||||
<-readyChan
|
||||
}
|
||||
|
||||
exitCode := ExitOk
|
||||
|
||||
@@ -102,7 +102,7 @@ func (m *Matcher) Loop() {
|
||||
if !cacheCleared {
|
||||
if count == prevCount {
|
||||
// Look up mergerCache
|
||||
if cached, found := m.mergerCache[patternString]; found {
|
||||
if cached, found := m.mergerCache[patternString]; found && cached.final == request.final {
|
||||
merger = cached
|
||||
}
|
||||
} else {
|
||||
|
||||
113
src/options.go
113
src/options.go
@@ -56,6 +56,7 @@ Usage: fzf [options]
|
||||
--wrap Enable line wrap
|
||||
--wrap-sign=STR Indicator for wrapped lines
|
||||
--no-multi-line Disable multi-line display of items when using --read0
|
||||
--gap[=N] Render empty lines between each item
|
||||
--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
|
||||
scrolling to the top or to the bottom (default: 0)
|
||||
@@ -74,7 +75,7 @@ Usage: fzf [options]
|
||||
according to the input size.
|
||||
--min-height=HEIGHT Minimum height when --height is given in percent
|
||||
(default: 10)
|
||||
--tmux[=OPTS] Start fzf in a tmux popup (requires tmux 3.3+)
|
||||
--popup[=OPTS] Start fzf in a popup (requires tmux 3.3+ or zellij)
|
||||
[center|top|bottom|left|right][,SIZE[%]][,SIZE[%]]
|
||||
(default: center,50%)
|
||||
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
|
||||
@@ -144,7 +145,7 @@ Usage: fzf [options]
|
||||
|
||||
Directory traversal (Only used when $FZF_DEFAULT_COMMAND is not set)
|
||||
--walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden)
|
||||
--walker-root=DIR Root directory from which to start walker (default: .)
|
||||
--walker-root=DIR [...] List of directories to walk (default: .)
|
||||
--walker-skip=DIRS Comma-separated list of directory names to skip
|
||||
(default: .git,node_modules)
|
||||
|
||||
@@ -298,7 +299,7 @@ func parseTmuxOptions(arg string, index int) (*tmuxOptions, error) {
|
||||
var err error
|
||||
opts := defaultTmuxOptions(index)
|
||||
tokens := splitRegexp.Split(arg, -1)
|
||||
errorToReturn := errors.New("invalid tmux option: " + arg + " (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]])")
|
||||
errorToReturn := errors.New("invalid popup option: " + arg + " (expected: [center|top|bottom|left|right][,SIZE[%]][,SIZE[%]])")
|
||||
if len(tokens) == 0 || len(tokens) > 3 {
|
||||
return nil, errorToReturn
|
||||
}
|
||||
@@ -380,14 +381,46 @@ func (a previewOpts) aboveOrBelow() bool {
|
||||
return a.size.size > 0 && (a.position == posUp || a.position == posDown)
|
||||
}
|
||||
|
||||
func (a previewOpts) sameLayout(b previewOpts) bool {
|
||||
return a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden && a.threshold == b.threshold &&
|
||||
(a.alternative != nil && b.alternative != nil && a.alternative.sameLayout(*b.alternative) ||
|
||||
a.alternative == nil && b.alternative == nil)
|
||||
}
|
||||
type previewOptsCompare int
|
||||
|
||||
func (a previewOpts) sameContentLayout(b previewOpts) bool {
|
||||
return a.wrap == b.wrap && a.headerLines == b.headerLines && a.info == b.info
|
||||
const (
|
||||
previewOptsSame previewOptsCompare = iota
|
||||
previewOptsDifferentContentLayout
|
||||
previewOptsDifferentLayout
|
||||
)
|
||||
|
||||
func (o *previewOpts) compare(active *previewOpts, b *previewOpts) previewOptsCompare {
|
||||
a := o
|
||||
|
||||
sameThreshold := o.position == b.position && o.threshold == b.threshold
|
||||
// Alternative layout is being used
|
||||
if o.alternative == active {
|
||||
a = active
|
||||
|
||||
// If the other also has an alternative layout,
|
||||
if b.alternative != nil {
|
||||
// and if the same condition is the same, compare alt vs. alt.
|
||||
if sameThreshold {
|
||||
b = b.alternative
|
||||
} else {
|
||||
// If not, we pessimistically decide that the layouts may not be the same
|
||||
return previewOptsDifferentLayout
|
||||
}
|
||||
}
|
||||
} else if b.alternative != nil && !sameThreshold {
|
||||
// We may choose the other's alternative layout, so let's be conservative.
|
||||
return previewOptsDifferentLayout
|
||||
}
|
||||
|
||||
if !(a.size == b.size && a.position == b.position && a.border == b.border && a.hidden == b.hidden) {
|
||||
return previewOptsDifferentLayout
|
||||
}
|
||||
|
||||
if a.wrap == b.wrap && a.headerLines == b.headerLines && a.info == b.info && a.scroll == b.scroll {
|
||||
return previewOptsSame
|
||||
}
|
||||
|
||||
return previewOptsDifferentContentLayout
|
||||
}
|
||||
|
||||
func firstLine(s string) string {
|
||||
@@ -473,6 +506,7 @@ type Options struct {
|
||||
Header []string
|
||||
HeaderLines int
|
||||
HeaderFirst bool
|
||||
Gap int
|
||||
Ellipsis *string
|
||||
Scrollbar *string
|
||||
Margin [4]sizeSpec
|
||||
@@ -488,7 +522,7 @@ type Options struct {
|
||||
Unsafe bool
|
||||
ClearOnExit bool
|
||||
WalkerOpts walkerOpts
|
||||
WalkerRoot string
|
||||
WalkerRoot []string
|
||||
WalkerSkip []string
|
||||
Version bool
|
||||
Help bool
|
||||
@@ -579,6 +613,7 @@ func defaultOptions() *Options {
|
||||
Header: make([]string, 0),
|
||||
HeaderLines: 0,
|
||||
HeaderFirst: false,
|
||||
Gap: 0,
|
||||
Ellipsis: nil,
|
||||
Scrollbar: nil,
|
||||
Margin: defaultMargin(),
|
||||
@@ -591,7 +626,7 @@ func defaultOptions() *Options {
|
||||
Unsafe: false,
|
||||
ClearOnExit: true,
|
||||
WalkerOpts: walkerOpts{file: true, hidden: true, follow: true},
|
||||
WalkerRoot: ".",
|
||||
WalkerRoot: []string{"."},
|
||||
WalkerSkip: []string{".git", "node_modules"},
|
||||
Help: false,
|
||||
Version: false}
|
||||
@@ -623,6 +658,28 @@ func optionalNextString(args []string, i *int) (bool, string) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func isDir(path string) bool {
|
||||
stat, err := os.Stat(path)
|
||||
return err == nil && stat.IsDir()
|
||||
}
|
||||
|
||||
func nextDirs(args []string, i *int) ([]string, error) {
|
||||
dirs := []string{}
|
||||
for *i < len(args)-1 {
|
||||
arg := args[*i+1]
|
||||
if isDir(arg) {
|
||||
dirs = append(dirs, arg)
|
||||
*i++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(dirs) == 0 {
|
||||
return nil, errors.New("no directory specified")
|
||||
}
|
||||
return dirs, nil
|
||||
}
|
||||
|
||||
func atoi(str string) (int, error) {
|
||||
num, err := strconv.Atoi(str)
|
||||
if err != nil {
|
||||
@@ -1374,6 +1431,10 @@ func parseActionList(masked string, original string, prevActions []*action, putA
|
||||
appendAction(actToggleHeader)
|
||||
case "toggle-wrap":
|
||||
appendAction(actToggleWrap)
|
||||
case "toggle-multi-line":
|
||||
appendAction(actToggleMultiLine)
|
||||
case "toggle-hscroll":
|
||||
appendAction(actToggleHscroll)
|
||||
case "show-header":
|
||||
appendAction(actShowHeader)
|
||||
case "hide-header":
|
||||
@@ -1723,7 +1784,7 @@ func parsePreviewWindowImpl(opts *previewOpts, input string) error {
|
||||
var err error
|
||||
tokenRegex := regexp.MustCompile(`[:,]*(<([1-9][0-9]*)\(([^)<]+)\)|[^,:]+)`)
|
||||
sizeRegex := regexp.MustCompile("^[0-9]+%?$")
|
||||
offsetRegex := regexp.MustCompile(`^(\+{-?[0-9]+})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
|
||||
offsetRegex := regexp.MustCompile(`^(\+{(-?[0-9]+|n)})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`)
|
||||
headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$")
|
||||
tokens := tokenRegex.FindAllStringSubmatch(input, -1)
|
||||
var alternative string
|
||||
@@ -1970,7 +2031,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
opts.Version = true
|
||||
case "--no-winpty":
|
||||
opts.NoWinpty = true
|
||||
case "--tmux":
|
||||
case "--tmux", "--popup":
|
||||
given, str := optionalNextString(allArgs, &i)
|
||||
if given {
|
||||
if opts.Tmux, err = parseTmuxOptions(str, index); err != nil {
|
||||
@@ -1979,7 +2040,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
} else {
|
||||
opts.Tmux = defaultTmuxOptions(index)
|
||||
}
|
||||
case "--no-tmux":
|
||||
case "--no-tmux", "--no-popup":
|
||||
opts.Tmux = nil
|
||||
case "--force-tty-in":
|
||||
// NOTE: We need this because `system('fzf --tmux < /dev/tty')` doesn't
|
||||
@@ -2343,6 +2404,12 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
opts.HeaderFirst = true
|
||||
case "--no-header-first":
|
||||
opts.HeaderFirst = false
|
||||
case "--gap":
|
||||
if opts.Gap, err = optionalNumeric(allArgs, &i, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
case "--no-gap":
|
||||
opts.Gap = 0
|
||||
case "--ellipsis":
|
||||
str, err := nextString(allArgs, &i, "ellipsis string required")
|
||||
if err != nil {
|
||||
@@ -2478,7 +2545,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
return err
|
||||
}
|
||||
case "--walker-root":
|
||||
if opts.WalkerRoot, err = nextString(allArgs, &i, "directory required"); err != nil {
|
||||
if opts.WalkerRoot, err = nextDirs(allArgs, &i); err != nil {
|
||||
return err
|
||||
}
|
||||
case "--walker-skip":
|
||||
@@ -2510,6 +2577,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
if opts.FuzzyAlgo, err = parseAlgo(value); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if match, value := optString(arg, "--popup="); match {
|
||||
if opts.Tmux, err = parseTmuxOptions(value, index); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if match, value := optString(arg, "--tmux="); match {
|
||||
if opts.Tmux, err = parseTmuxOptions(value, index); err != nil {
|
||||
return err
|
||||
@@ -2630,6 +2701,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
if opts.HeaderLines, err = atoi(value); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if match, value := optString(arg, "--gap="); match {
|
||||
if opts.Gap, err = atoi(value); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if match, value := optString(arg, "--ellipsis="); match {
|
||||
str := firstLine(value)
|
||||
opts.Ellipsis = &str
|
||||
@@ -2672,7 +2747,11 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
|
||||
return err
|
||||
}
|
||||
} else if match, value := optString(arg, "--walker-root="); match {
|
||||
opts.WalkerRoot = value
|
||||
if !isDir(value) {
|
||||
return errors.New("not a directory: " + value)
|
||||
}
|
||||
dirs, _ := nextDirs(allArgs, &i)
|
||||
opts.WalkerRoot = append([]string{value}, dirs...)
|
||||
} else if match, value := optString(arg, "--walker-skip="); match {
|
||||
opts.WalkerSkip = filterNonEmpty(strings.Split(value, ","))
|
||||
} else if match, value := optString(arg, "--hscroll-off="); match {
|
||||
|
||||
124
src/reader.go
124
src/reader.go
@@ -6,8 +6,8 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -25,16 +25,26 @@ type Reader struct {
|
||||
event int32
|
||||
finChan chan bool
|
||||
mutex sync.Mutex
|
||||
exec *exec.Cmd
|
||||
execOut io.ReadCloser
|
||||
command *string
|
||||
killed bool
|
||||
termFunc func()
|
||||
command *string
|
||||
wait bool
|
||||
}
|
||||
|
||||
// NewReader returns new Reader object
|
||||
func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, executor *util.Executor, delimNil bool, wait bool) *Reader {
|
||||
return &Reader{pusher, executor, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, nil, false, wait}
|
||||
return &Reader{
|
||||
pusher,
|
||||
executor,
|
||||
eventBox,
|
||||
delimNil,
|
||||
int32(EvtReady),
|
||||
make(chan bool, 1),
|
||||
sync.Mutex{},
|
||||
false,
|
||||
func() { os.Stdin.Close() },
|
||||
nil,
|
||||
wait}
|
||||
}
|
||||
|
||||
func (r *Reader) startEventPoller() {
|
||||
@@ -80,19 +90,19 @@ func (r *Reader) fin(success bool) {
|
||||
func (r *Reader) terminate() {
|
||||
r.mutex.Lock()
|
||||
r.killed = true
|
||||
if r.exec != nil && r.exec.Process != nil {
|
||||
r.execOut.Close()
|
||||
util.KillCommand(r.exec)
|
||||
} else {
|
||||
os.Stdin.Close()
|
||||
if r.termFunc != nil {
|
||||
r.termFunc()
|
||||
r.termFunc = nil
|
||||
}
|
||||
r.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (r *Reader) restart(command commandSpec, environ []string) {
|
||||
func (r *Reader) restart(command commandSpec, environ []string, readyChan chan bool) {
|
||||
r.event = int32(EvtReady)
|
||||
r.startEventPoller()
|
||||
success := r.readFromCommand(command.command, environ)
|
||||
success := r.readFromCommand(command.command, environ, func() {
|
||||
readyChan <- true
|
||||
})
|
||||
r.fin(success)
|
||||
removeFiles(command.tempFiles)
|
||||
}
|
||||
@@ -111,21 +121,29 @@ func (r *Reader) readChannel(inputChan chan string) bool {
|
||||
}
|
||||
|
||||
// ReadSource reads data from the default command or from standard input
|
||||
func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, initCmd string, initEnv []string) {
|
||||
func (r *Reader) ReadSource(inputChan chan string, roots []string, opts walkerOpts, ignores []string, initCmd string, initEnv []string, readyChan chan bool) {
|
||||
r.startEventPoller()
|
||||
var success bool
|
||||
signalReady := func() {
|
||||
if readyChan != nil {
|
||||
readyChan <- true
|
||||
}
|
||||
}
|
||||
if inputChan != nil {
|
||||
signalReady()
|
||||
success = r.readChannel(inputChan)
|
||||
} else if len(initCmd) > 0 {
|
||||
success = r.readFromCommand(initCmd, initEnv)
|
||||
success = r.readFromCommand(initCmd, initEnv, signalReady)
|
||||
} else if util.IsTty(os.Stdin) {
|
||||
cmd := os.Getenv("FZF_DEFAULT_COMMAND")
|
||||
if len(cmd) == 0 {
|
||||
success = r.readFiles(root, opts, ignores)
|
||||
signalReady()
|
||||
success = r.readFiles(roots, opts, ignores)
|
||||
} else {
|
||||
success = r.readFromCommand(cmd, initEnv)
|
||||
success = r.readFromCommand(cmd, initEnv, signalReady)
|
||||
}
|
||||
} else {
|
||||
signalReady()
|
||||
success = r.readFromStdin()
|
||||
}
|
||||
r.fin(success)
|
||||
@@ -248,14 +266,33 @@ func trimPath(path string) string {
|
||||
return byteString(bytes)
|
||||
}
|
||||
|
||||
func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool {
|
||||
r.killed = false
|
||||
func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bool {
|
||||
conf := fastwalk.Config{
|
||||
Follow: opts.follow,
|
||||
// Use forward slashes when running a Windows binary under WSL or MSYS
|
||||
ToSlash: fastwalk.DefaultToSlash(),
|
||||
Sort: fastwalk.SortFilesFirst,
|
||||
}
|
||||
ignoresBase := []string{}
|
||||
ignoresFull := []string{}
|
||||
ignoresSuffix := []string{}
|
||||
sep := string(os.PathSeparator)
|
||||
for _, ignore := range ignores {
|
||||
if strings.ContainsRune(ignore, os.PathSeparator) {
|
||||
if strings.HasPrefix(ignore, sep) {
|
||||
ignoresSuffix = append(ignoresSuffix, ignore)
|
||||
} else {
|
||||
// 'foo/bar' should match match
|
||||
// * 'foo/bar'
|
||||
// * 'baz/foo/bar'
|
||||
// * but NOT 'bazfoo/bar'
|
||||
ignoresFull = append(ignoresFull, ignore)
|
||||
ignoresSuffix = append(ignoresSuffix, sep+ignore)
|
||||
}
|
||||
} else {
|
||||
ignoresBase = append(ignoresBase, ignore)
|
||||
}
|
||||
}
|
||||
fn := func(path string, de os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -265,14 +302,24 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
|
||||
isDir := de.IsDir()
|
||||
if isDir || opts.follow && isSymlinkToDir(path, de) {
|
||||
base := filepath.Base(path)
|
||||
if !opts.hidden && base[0] == '.' {
|
||||
if !opts.hidden && base[0] == '.' && base != ".." {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
for _, ignore := range ignores {
|
||||
for _, ignore := range ignoresBase {
|
||||
if ignore == base {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
for _, ignore := range ignoresFull {
|
||||
if ignore == path {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
for _, ignore := range ignoresSuffix {
|
||||
if strings.HasSuffix(path, ignore) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((opts.file && !isDir) || (opts.dir && isDir)) && r.pusher(stringBytes(path)) {
|
||||
atomic.StoreInt32(&r.event, int32(EvtReadNew))
|
||||
@@ -285,34 +332,39 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fastwalk.Walk(&conf, root, fn) == nil
|
||||
noerr := true
|
||||
for _, root := range roots {
|
||||
noerr = noerr && (fastwalk.Walk(&conf, root, fn) == nil)
|
||||
}
|
||||
return noerr
|
||||
}
|
||||
|
||||
func (r *Reader) readFromCommand(command string, environ []string) bool {
|
||||
func (r *Reader) readFromCommand(command string, environ []string, signalReady func()) bool {
|
||||
r.mutex.Lock()
|
||||
|
||||
r.killed = false
|
||||
r.termFunc = nil
|
||||
r.command = &command
|
||||
r.exec = r.executor.ExecCommand(command, true)
|
||||
exec := r.executor.ExecCommand(command, true)
|
||||
if environ != nil {
|
||||
r.exec.Env = environ
|
||||
exec.Env = environ
|
||||
}
|
||||
|
||||
var err error
|
||||
r.execOut, err = r.exec.StdoutPipe()
|
||||
if err != nil {
|
||||
r.exec = nil
|
||||
execOut, err := exec.StdoutPipe()
|
||||
if err != nil || exec.Start() != nil {
|
||||
signalReady()
|
||||
r.mutex.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
err = r.exec.Start()
|
||||
if err != nil {
|
||||
r.exec = nil
|
||||
r.mutex.Unlock()
|
||||
return false
|
||||
// Function to call to terminate the running command
|
||||
r.termFunc = func() {
|
||||
execOut.Close()
|
||||
util.KillCommand(exec)
|
||||
}
|
||||
|
||||
signalReady()
|
||||
r.mutex.Unlock()
|
||||
r.feed(r.execOut)
|
||||
return r.exec.Wait() == nil
|
||||
|
||||
r.feed(execOut)
|
||||
return exec.Wait() == nil
|
||||
}
|
||||
|
||||
@@ -23,8 +23,12 @@ func TestReadFromCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
// Normal command
|
||||
reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil))
|
||||
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" {
|
||||
counter := 0
|
||||
ready := func() {
|
||||
counter++
|
||||
}
|
||||
reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil, ready))
|
||||
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" || counter != 1 {
|
||||
t.Errorf("%s", strs)
|
||||
}
|
||||
|
||||
@@ -48,9 +52,9 @@ func TestReadFromCommand(t *testing.T) {
|
||||
reader.startEventPoller()
|
||||
|
||||
// Failing command
|
||||
reader.fin(reader.readFromCommand(`no-such-command`, nil))
|
||||
reader.fin(reader.readFromCommand(`no-such-command`, nil, ready))
|
||||
strs = []string{}
|
||||
if len(strs) > 0 {
|
||||
if len(strs) > 0 || counter != 2 {
|
||||
t.Errorf("%s", strs)
|
||||
}
|
||||
|
||||
|
||||
582
src/terminal.go
582
src/terminal.go
File diff suppressed because it is too large
Load Diff
@@ -507,6 +507,34 @@ func TestParsePlaceholder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractPassthroughs(t *testing.T) {
|
||||
for _, middle := range []string{
|
||||
"\x1bPtmux;\x1b\x1bbar\x1b\\",
|
||||
"\x1bPtmux;\x1b\x1bbar\x1bbar\x1b\\",
|
||||
"\x1b]1337;bar\x1b\\",
|
||||
"\x1b]1337;bar\x1bbar\x1b\\",
|
||||
"\x1b]1337;bar\a",
|
||||
"\x1b_Ga=T,f=32,s=1258,v=1295,c=74,r=35,m=1\x1b\\",
|
||||
"\x1b_Ga=T,f=32,s=1258,v=1295,c=74,r=35,m=1\x1b\\\r",
|
||||
"\x1b_Ga=T,f=32,s=1258,v=1295,c=74,r=35,m=1\x1bbar\x1b\\\r",
|
||||
"\x1b_Gm=1;AAAAAAAAA=\x1b\\",
|
||||
"\x1b_Gm=1;AAAAAAAAA=\x1b\\\r",
|
||||
"\x1b_Gm=1;\x1bAAAAAAAAA=\x1b\\\r",
|
||||
} {
|
||||
line := "foo" + middle + "baz"
|
||||
loc := findPassThrough(line)
|
||||
if loc == nil || line[0:loc[0]] != "foo" || line[loc[1]:] != "baz" {
|
||||
t.Error("failed to find passthrough")
|
||||
}
|
||||
garbage := "\x1bPtmux;\x1b]1337;\x1b_Ga=\x1b]1337;bar\x1b."
|
||||
line = strings.Repeat("foo"+middle+middle+"baz", 3) + garbage
|
||||
passthroughs, result := extractPassThroughs(line)
|
||||
if result != "foobazfoobazfoobaz"+garbage || len(passthroughs) != 6 {
|
||||
t.Error("failed to extract passthroughs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* utilities section */
|
||||
|
||||
// Item represents one line in fzf UI. Usually it is relative path to files and folders.
|
||||
|
||||
@@ -18,7 +18,7 @@ func runTmux(args []string, opts *Options) (int, error) {
|
||||
for _, arg := range args {
|
||||
argStr += " " + escapeSingleQuote(arg)
|
||||
}
|
||||
argStr += ` --no-tmux --no-height`
|
||||
argStr += ` --no-popup --no-height`
|
||||
|
||||
// Get current directory
|
||||
dir, err := os.Getwd()
|
||||
|
||||
@@ -73,11 +73,15 @@ func (r *LightRenderer) csi(code string) string {
|
||||
|
||||
func (r *LightRenderer) flush() {
|
||||
if r.queued.Len() > 0 {
|
||||
fmt.Fprint(r.ttyout, "\x1b[?7l\x1b[?25l"+r.queued.String()+"\x1b[?25h\x1b[?7h")
|
||||
r.flushRaw("\x1b[?7l\x1b[?25l" + r.queued.String() + "\x1b[?25h\x1b[?7h")
|
||||
r.queued.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LightRenderer) flushRaw(sequence string) {
|
||||
fmt.Fprint(r.ttyout, sequence)
|
||||
}
|
||||
|
||||
// Light renderer
|
||||
type LightRenderer struct {
|
||||
closed *util.AtomicBool
|
||||
@@ -655,11 +659,13 @@ func (r *LightRenderer) mouseSequence(sz *int) Event {
|
||||
}
|
||||
|
||||
func (r *LightRenderer) smcup() {
|
||||
r.csi("?1049h")
|
||||
r.flush()
|
||||
r.flushRaw("\x1b[?1049h")
|
||||
}
|
||||
|
||||
func (r *LightRenderer) rmcup() {
|
||||
r.csi("?1049l")
|
||||
r.flush()
|
||||
r.flushRaw("\x1b[?1049l")
|
||||
}
|
||||
|
||||
func (r *LightRenderer) Pause(clear bool) {
|
||||
@@ -929,9 +935,6 @@ func (w *LightWindow) Height() int {
|
||||
func (w *LightWindow) Refresh() {
|
||||
}
|
||||
|
||||
func (w *LightWindow) Close() {
|
||||
}
|
||||
|
||||
func (w *LightWindow) X() int {
|
||||
return w.posx
|
||||
}
|
||||
@@ -1097,7 +1100,7 @@ func (w *LightWindow) fill(str string, resetCode string) FillReturn {
|
||||
}
|
||||
}
|
||||
}
|
||||
if w.posx+1 >= w.Width() {
|
||||
if w.posx >= w.Width() {
|
||||
if w.posy+1 >= w.height {
|
||||
return FillSuspend
|
||||
}
|
||||
|
||||
@@ -555,10 +555,6 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *TcellWindow) Close() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func fill(x, y, w, h int, n ColorPair, r rune) {
|
||||
for ly := 0; ly <= h; ly++ {
|
||||
for lx := 0; lx <= w; lx++ {
|
||||
|
||||
@@ -387,6 +387,14 @@ func (s BorderShape) HasTop() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s BorderShape) HasBottom() bool {
|
||||
switch s {
|
||||
case BorderNone, BorderLeft, BorderRight, BorderTop, BorderVertical: // No bottom
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type BorderStyle struct {
|
||||
shape BorderShape
|
||||
top rune
|
||||
@@ -402,6 +410,18 @@ type BorderStyle struct {
|
||||
type BorderCharacter int
|
||||
|
||||
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
||||
if shape == BorderNone {
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
top: ' ',
|
||||
bottom: ' ',
|
||||
left: ' ',
|
||||
right: ' ',
|
||||
topLeft: ' ',
|
||||
topRight: ' ',
|
||||
bottomLeft: ' ',
|
||||
bottomRight: ' '}
|
||||
}
|
||||
if !unicode {
|
||||
return BorderStyle{
|
||||
shape: shape,
|
||||
@@ -498,19 +518,6 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
|
||||
}
|
||||
}
|
||||
|
||||
func MakeTransparentBorder() BorderStyle {
|
||||
return BorderStyle{
|
||||
shape: BorderRounded,
|
||||
top: ' ',
|
||||
bottom: ' ',
|
||||
left: ' ',
|
||||
right: ' ',
|
||||
topLeft: ' ',
|
||||
topRight: ' ',
|
||||
bottomLeft: ' ',
|
||||
bottomRight: ' '}
|
||||
}
|
||||
|
||||
type TermSize struct {
|
||||
Lines int
|
||||
Columns int
|
||||
@@ -552,7 +559,6 @@ type Window interface {
|
||||
DrawHBorder()
|
||||
Refresh()
|
||||
FinishFill()
|
||||
Close()
|
||||
|
||||
X() int
|
||||
Y() int
|
||||
|
||||
@@ -306,5 +306,5 @@ func (chars *Chars) Lines(multiLine bool, maxLines int, wrapCols int, wrapSignWi
|
||||
}
|
||||
}
|
||||
|
||||
return wrapped, false
|
||||
return wrapped, overflow
|
||||
}
|
||||
|
||||
103
src/zellij.go
Normal file
103
src/zellij.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/junegunn/fzf/src/tui"
|
||||
)
|
||||
|
||||
func runZellij(args []string, opts *Options) (int, error) {
|
||||
// Prepare arguments
|
||||
fzf := args[0]
|
||||
args = append([]string{"--bind=ctrl-z:ignore"}, args[1:]...)
|
||||
if opts.BorderShape == tui.BorderUndefined {
|
||||
args = append(args, "--no-border")
|
||||
}
|
||||
argStr := escapeSingleQuote(fzf)
|
||||
for _, arg := range args {
|
||||
argStr += " " + escapeSingleQuote(arg)
|
||||
}
|
||||
argStr += ` --no-popup --no-height`
|
||||
|
||||
// Get current directory
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
dir = "."
|
||||
}
|
||||
|
||||
sh, err := sh(false)
|
||||
if err != nil {
|
||||
return ExitError, err
|
||||
}
|
||||
|
||||
fifo, err := fifo("zellij-fifo")
|
||||
if err != nil {
|
||||
return ExitError, err
|
||||
}
|
||||
|
||||
zopts := " --width " + opts.Tmux.width.String() + " --height " + opts.Tmux.height.String()
|
||||
centerX := func() {
|
||||
// TODO: Handle non-percent values
|
||||
if opts.Tmux.width.percent {
|
||||
x := (100.0 - opts.Tmux.width.size) / 2
|
||||
if x <= 0 {
|
||||
zopts += " -x0"
|
||||
} else {
|
||||
zopts += fmt.Sprintf(" -x%d%%", int(x))
|
||||
}
|
||||
} else if cols := os.Getenv("COLUMNS"); len(cols) > 0 {
|
||||
if w, e := strconv.Atoi(cols); e == nil {
|
||||
x := (float64(w) - opts.Tmux.width.size) / 2
|
||||
zopts += fmt.Sprintf(" -x%d", int(x))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
centerY := func() {
|
||||
if opts.Tmux.height.percent {
|
||||
y := (100.0 - opts.Tmux.height.size) / 2
|
||||
if y <= 0 {
|
||||
zopts += " -y0"
|
||||
} else {
|
||||
zopts += fmt.Sprintf(" -y%d%%", int(y))
|
||||
}
|
||||
} else if lines := os.Getenv("LINES"); len(lines) > 0 {
|
||||
if h, e := strconv.Atoi(lines); e == nil {
|
||||
y := (float64(h) - opts.Tmux.height.size) / 2
|
||||
zopts += fmt.Sprintf(" -y%d", int(y))
|
||||
}
|
||||
}
|
||||
}
|
||||
switch opts.Tmux.position {
|
||||
case posUp:
|
||||
zopts += " -y0"
|
||||
centerX()
|
||||
case posDown:
|
||||
zopts += " -y9999"
|
||||
centerX()
|
||||
case posLeft:
|
||||
zopts += " -x0"
|
||||
centerY()
|
||||
case posRight:
|
||||
zopts += " -x9999"
|
||||
centerY()
|
||||
case posCenter:
|
||||
centerX()
|
||||
centerY()
|
||||
}
|
||||
|
||||
lines := []string{
|
||||
"#!/bin/sh",
|
||||
fmt.Sprintf(`zellij run --name '' --floating --close-on-exit --cwd %s %s -- %s -c "%s $1; echo \$? > %s" || exit $?`, dir, zopts, sh, sh, fifo),
|
||||
fmt.Sprintf(`exit $(cat %s)`, fifo),
|
||||
}
|
||||
temptemp := WriteTemporaryFile(lines, "\n")
|
||||
defer os.Remove(temptemp)
|
||||
|
||||
return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
|
||||
return exec.Command(sh, temptemp, temp), nil
|
||||
}, opts, true)
|
||||
}
|
||||
@@ -1442,10 +1442,14 @@ class TestGoFZF < TestBase
|
||||
writelines(['=' * 10_000 + '0123456789'])
|
||||
[0, 3, 6].each do |off|
|
||||
tmux.prepare
|
||||
tmux.send_keys "#{FZF} --hscroll-off=#{off} -q 0 < #{tempname}", :Enter
|
||||
tmux.send_keys "#{FZF} --hscroll-off=#{off} -q 0 --bind space:toggle-hscroll < #{tempname}", :Enter
|
||||
tmux.until { |lines| assert lines[-3]&.end_with?((0..off).to_a.join + '··') }
|
||||
tmux.send_keys '9'
|
||||
tmux.until { |lines| assert lines[-3]&.end_with?('789') }
|
||||
tmux.send_keys :Space
|
||||
tmux.until { |lines| assert lines[-3]&.end_with?('=··') }
|
||||
tmux.send_keys :Space
|
||||
tmux.until { |lines| assert lines[-3]&.end_with?('789') }
|
||||
tmux.send_keys :Enter
|
||||
end
|
||||
end
|
||||
@@ -2133,7 +2137,11 @@ class TestGoFZF < TestBase
|
||||
end
|
||||
|
||||
def test_keep_right
|
||||
tmux.send_keys "seq 10000 | #{FZF} --read0 --keep-right --no-multi-line", :Enter
|
||||
tmux.send_keys "seq 10000 | #{FZF} --read0 --keep-right --no-multi-line --bind space:toggle-multi-line", :Enter
|
||||
tmux.until { |lines| assert lines.any_include?('9999␊10000') }
|
||||
tmux.send_keys :Space
|
||||
tmux.until { |lines| assert lines.any_include?('> 1') }
|
||||
tmux.send_keys :Space
|
||||
tmux.until { |lines| assert lines.any_include?('9999␊10000') }
|
||||
end
|
||||
|
||||
@@ -2814,13 +2822,13 @@ class TestGoFZF < TestBase
|
||||
tmux.send_keys "seq 3 | fzf --height ~100% --border=vertical --preview 'seq {}' --preview-window left,5,border-right --padding 1 --exit-0 --header $'hello\\nworld' --header-lines=2", :Enter
|
||||
expected = <<~OUTPUT
|
||||
│
|
||||
│ 1 │ > 3
|
||||
│ 2 │ 2
|
||||
│ 3 │ 1
|
||||
│ │ hello
|
||||
│ │ world
|
||||
│ │ 1/1 ─
|
||||
│ │ >
|
||||
│ 1 │ > 3
|
||||
│ 2 │ 2
|
||||
│ 3 │ 1
|
||||
│ │ hello
|
||||
│ │ world
|
||||
│ │ 1/1 ─
|
||||
│ │ >
|
||||
│
|
||||
OUTPUT
|
||||
tmux.until { assert_block(expected, _1) }
|
||||
@@ -3073,6 +3081,21 @@ class TestGoFZF < TestBase
|
||||
tmux.until { |lines| assert_includes lines, '/1/1/' }
|
||||
end
|
||||
|
||||
def test_alternative_preview_window_opts
|
||||
tmux.send_keys "seq 10 | #{FZF} --preview-window '~5,2,+0,<100000(~0,+100,wrap,noinfo)' --preview 'seq 1000'", :Enter
|
||||
tmux.until { |lines| assert_equal 10, lines.item_count }
|
||||
tmux.until do |lines|
|
||||
assert_equal ['╭────╮', '│ 10 │', '│ 0 │', '│ 10 │', '│ 1 │'], lines.take(5).map(&:strip)
|
||||
end
|
||||
end
|
||||
|
||||
def test_preview_window_width_exception
|
||||
tmux.send_keys "seq 10 | #{FZF} --scrollbar --preview-window border-left --border --preview 'seq 1000'", :Enter
|
||||
tmux.until do |lines|
|
||||
assert lines[1]&.end_with?(' 1/1000││')
|
||||
end
|
||||
end
|
||||
|
||||
def test_become
|
||||
tmux.send_keys "seq 100 | #{FZF} --bind 'enter:become:seq {} | #{FZF}'", :Enter
|
||||
tmux.until { |lines| assert_equal 100, lines.item_count }
|
||||
@@ -3392,6 +3415,40 @@ class TestGoFZF < TestBase
|
||||
assert lines[1]&.end_with?('1000││')
|
||||
end
|
||||
end
|
||||
|
||||
def test_gap
|
||||
tmux.send_keys %(seq 100 | #{FZF} --gap --border --reverse), :Enter
|
||||
block = <<~BLOCK
|
||||
╭─────────────────
|
||||
│ >
|
||||
│ 100/100 ──────
|
||||
│ > 1
|
||||
│
|
||||
│ 2
|
||||
│
|
||||
│ 3
|
||||
│
|
||||
│ 4
|
||||
BLOCK
|
||||
tmux.until { assert_block(block, _1) }
|
||||
end
|
||||
|
||||
def test_gap_2
|
||||
tmux.send_keys %(seq 100 | #{FZF} --gap=2 --border --reverse), :Enter
|
||||
block = <<~BLOCK
|
||||
╭─────────────────
|
||||
│ >
|
||||
│ 100/100 ──────
|
||||
│ > 1
|
||||
│
|
||||
│
|
||||
│ 2
|
||||
│
|
||||
│
|
||||
│ 3
|
||||
BLOCK
|
||||
tmux.until { assert_block(block, _1) }
|
||||
end
|
||||
end
|
||||
|
||||
module TestShell
|
||||
@@ -3703,6 +3760,23 @@ module CompletionTest
|
||||
tmux.until { |lines| assert_equal 'unset FZFFOOBAR', lines[-1] }
|
||||
end
|
||||
|
||||
def test_completion_in_command_sequence
|
||||
tmux.send_keys 'export FZFFOOBAR=BAZ', :Enter
|
||||
tmux.prepare
|
||||
|
||||
triggers = ['**', '~~', '++', 'ff', '/']
|
||||
triggers.concat(['&', '[', ';', '`']) if instance_of?(TestZsh)
|
||||
|
||||
triggers.each do |trigger|
|
||||
set_var('FZF_COMPLETION_TRIGGER', trigger)
|
||||
command = "echo foo; QUX=THUD unset FZFFOOBR#{trigger}"
|
||||
tmux.send_keys command.sub(/(;|`)$/, '\\\\\1'), :Tab
|
||||
tmux.until { |lines| assert_equal 1, lines.match_count }
|
||||
tmux.send_keys :Enter
|
||||
tmux.until { |lines| assert_equal 'echo foo; QUX=THUD unset FZFFOOBAR', lines[-1] }
|
||||
end
|
||||
end
|
||||
|
||||
def test_file_completion_unicode
|
||||
FileUtils.mkdir_p('/tmp/fzf-test')
|
||||
tmux.paste "cd /tmp/fzf-test; echo test3 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2701'; echo test4 > $'fzf-unicode \\355\\205\\214\\354\\212\\244\\355\\212\\2702'"
|
||||
|
||||
Reference in New Issue
Block a user